summaryrefslogtreecommitdiff
path: root/media
diff options
context:
space:
mode:
authorTorne (Richard Coles) <torne@google.com>2014-08-28 12:05:23 +0100
committerTorne (Richard Coles) <torne@google.com>2014-08-28 12:05:23 +0100
commit03b57e008b61dfcb1fbad3aea950ae0e001748b0 (patch)
tree9a740c1a5fbe659ec83484b67cbc679332f5a408 /media
parentca7d0c81aa30d24514c34c963f43cd24da34a2bf (diff)
downloadchromium_org-03b57e008b61dfcb1fbad3aea950ae0e001748b0.tar.gz
Merge from Chromium at DEPS revision 291560
This commit was generated by merge_to_master.py. Change-Id: Ic58269055810d51286b4109e59b90b6856887a30
Diffstat (limited to 'media')
-rw-r--r--media/audio/audio_input_controller.cc68
-rw-r--r--media/audio/audio_input_controller.h27
-rw-r--r--media/audio/pulse/pulse_input.cc5
-rw-r--r--media/base/android/BUILD.gn6
-rw-r--r--media/base/demuxer_perftest.cc2
-rw-r--r--media/base/video_frame.cc29
-rw-r--r--media/base/video_frame.h12
-rw-r--r--media/cast/BUILD.gn6
-rw-r--r--media/cast/cast.gyp2
-rw-r--r--media/cast/cast_defines.h5
-rw-r--r--media/cast/cast_sender.h5
-rw-r--r--media/cast/cast_sender_impl.cc21
-rw-r--r--media/cast/cast_sender_impl.h3
-rw-r--r--media/cast/cast_testing.gypi4
-rw-r--r--media/cast/net/cast_transport_config.cc3
-rw-r--r--media/cast/net/cast_transport_config.h8
-rw-r--r--media/cast/net/cast_transport_defines.h9
-rw-r--r--media/cast/net/cast_transport_sender.h22
-rw-r--r--media/cast/net/cast_transport_sender_impl.cc88
-rw-r--r--media/cast/net/cast_transport_sender_impl.h36
-rw-r--r--media/cast/net/cast_transport_sender_impl_unittest.cc255
-rw-r--r--media/cast/net/pacing/mock_paced_packet_sender.h2
-rw-r--r--media/cast/net/pacing/paced_sender.cc89
-rw-r--r--media/cast/net/pacing/paced_sender.h57
-rw-r--r--media/cast/net/pacing/paced_sender_unittest.cc74
-rw-r--r--media/cast/net/rtcp/mock_rtcp_receiver_feedback.cc15
-rw-r--r--media/cast/net/rtcp/mock_rtcp_receiver_feedback.h45
-rw-r--r--media/cast/net/rtcp/mock_rtcp_sender_feedback.cc15
-rw-r--r--media/cast/net/rtcp/mock_rtcp_sender_feedback.h28
-rw-r--r--media/cast/net/rtcp/rtcp.cc233
-rw-r--r--media/cast/net/rtcp/rtcp.h22
-rw-r--r--media/cast/net/rtcp/rtcp_builder.cc123
-rw-r--r--media/cast/net/rtcp/rtcp_builder.h43
-rw-r--r--media/cast/net/rtcp/rtcp_builder_unittest.cc159
-rw-r--r--media/cast/net/rtcp/rtcp_receiver.cc414
-rw-r--r--media/cast/net/rtcp/rtcp_receiver.h101
-rw-r--r--media/cast/net/rtcp/rtcp_receiver_unittest.cc493
-rw-r--r--media/cast/net/rtcp/rtcp_sender.cc37
-rw-r--r--media/cast/net/rtcp/rtcp_sender.h5
-rw-r--r--media/cast/net/rtcp/rtcp_sender_unittest.cc62
-rw-r--r--media/cast/net/rtcp/rtcp_unittest.cc37
-rw-r--r--media/cast/net/rtcp/rtcp_utility.cc760
-rw-r--r--media/cast/net/rtcp/rtcp_utility.h243
-rw-r--r--media/cast/net/rtcp/rtcp_utility_unittest.cc402
-rw-r--r--media/cast/net/rtcp/test_rtcp_packet_builder.cc16
-rw-r--r--media/cast/net/rtcp/test_rtcp_packet_builder.h17
-rw-r--r--media/cast/net/rtp/frame_buffer.cc3
-rw-r--r--media/cast/net/rtp/frame_buffer.h1
-rw-r--r--media/cast/net/rtp/rtp_defines.h20
-rw-r--r--media/cast/net/rtp/rtp_packetizer.cc29
-rw-r--r--media/cast/net/rtp/rtp_packetizer_unittest.cc4
-rw-r--r--media/cast/net/rtp/rtp_parser.cc24
-rw-r--r--media/cast/net/rtp/rtp_receiver_defines.cc3
-rw-r--r--media/cast/net/rtp/rtp_receiver_defines.h2
-rw-r--r--media/cast/net/rtp/rtp_sender.cc43
-rw-r--r--media/cast/net/rtp/rtp_sender.h12
-rw-r--r--media/cast/net/udp_transport.cc8
-rw-r--r--media/cast/net/udp_transport.h2
-rw-r--r--media/cast/receiver/cast_receiver_impl.cc5
-rw-r--r--media/cast/receiver/frame_receiver.cc21
-rw-r--r--media/cast/receiver/frame_receiver.h4
-rw-r--r--media/cast/sender/audio_sender.cc46
-rw-r--r--media/cast/sender/audio_sender.h12
-rw-r--r--media/cast/sender/audio_sender_unittest.cc7
-rw-r--r--media/cast/sender/congestion_control.cc11
-rw-r--r--media/cast/sender/frame_sender.cc20
-rw-r--r--media/cast/sender/frame_sender.h36
-rw-r--r--media/cast/sender/video_sender.cc40
-rw-r--r--media/cast/sender/video_sender.h13
-rw-r--r--media/cast/sender/video_sender_unittest.cc34
-rw-r--r--media/cast/test/cast_benchmarks.cc17
-rw-r--r--media/cast/test/end2end_unittest.cc74
-rw-r--r--media/cast/test/loopback_transport.cc8
-rw-r--r--media/cast/test/loopback_transport.h3
-rw-r--r--media/cast/test/simulator.cc6
-rw-r--r--media/cast/test/utility/udp_proxy.cc23
-rw-r--r--media/cast/test/utility/udp_proxy.h7
-rw-r--r--media/cdm/aes_decryptor.cc7
-rw-r--r--media/cdm/aes_decryptor_unittest.cc13
-rw-r--r--media/cdm/json_web_key.cc103
-rw-r--r--media/cdm/json_web_key.h52
-rw-r--r--media/cdm/json_web_key_unittest.cc137
-rw-r--r--media/cdm/ppapi/cdm_wrapper.h28
-rw-r--r--media/filters/decoder_stream.cc9
-rw-r--r--media/filters/decoder_stream.h4
-rw-r--r--media/filters/decoder_stream_traits.cc29
-rw-r--r--media/filters/decoder_stream_traits.h8
-rw-r--r--media/filters/gpu_video_decoder.cc14
-rw-r--r--media/filters/skcanvas_video_renderer.cc15
-rw-r--r--media/filters/skcanvas_video_renderer.h5
-rw-r--r--media/filters/skcanvas_video_renderer_unittest.cc68
-rw-r--r--media/formats/mp2t/es_parser.h3
-rw-r--r--media/formats/mp2t/es_parser_h264.h2
-rw-r--r--media/test/data/eme_player_js/clearkey_player.js2
-rw-r--r--media/test/data/eme_player_js/prefixed_clearkey_player.js2
-rw-r--r--media/test/data/eme_player_js/utils.js30
-rw-r--r--media/video/capture/mac/platform_video_capturing_mac.h9
-rw-r--r--media/video/capture/mac/video_capture_device_mac.h5
-rw-r--r--media/video/capture/mac/video_capture_device_mac.mm1
-rw-r--r--media/video/capture/mac/video_capture_device_qtkit_mac.mm60
-rw-r--r--media/video/capture/win/video_capture_device_win.cc15
-rw-r--r--media/video/picture.cc7
-rw-r--r--media/video/picture.h11
103 files changed, 2472 insertions, 2813 deletions
diff --git a/media/audio/audio_input_controller.cc b/media/audio/audio_input_controller.cc
index bcd9c7281e..c49cb34f34 100644
--- a/media/audio/audio_input_controller.cc
+++ b/media/audio/audio_input_controller.cc
@@ -8,6 +8,7 @@
#include "base/strings/stringprintf.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
+#include "media/audio/audio_parameters.h"
#include "media/base/limits.h"
#include "media/base/scoped_histogram_timer.h"
#include "media/base/user_input_monitor.h"
@@ -85,6 +86,7 @@ AudioInputController::AudioInputController(EventHandler* handler,
max_volume_(0.0),
user_input_monitor_(user_input_monitor),
#if defined(AUDIO_POWER_MONITORING)
+ log_silence_state_(false),
silence_state_(SILENCE_STATE_NO_MEASUREMENT),
#endif
prev_key_down_count_(0) {
@@ -150,7 +152,7 @@ scoped_refptr<AudioInputController> AudioInputController::CreateLowLatency(
// Create and open a new audio input stream from the existing
// audio-device thread. Use the provided audio-input device.
if (!controller->task_runner_->PostTask(FROM_HERE,
- base::Bind(&AudioInputController::DoCreate, controller,
+ base::Bind(&AudioInputController::DoCreateForLowLatency, controller,
base::Unretained(audio_manager), params, device_id))) {
controller = NULL;
}
@@ -238,6 +240,21 @@ void AudioInputController::DoCreate(AudioManager* audio_manager,
DoCreateForStream(audio_manager->MakeAudioInputStream(params, device_id));
}
+void AudioInputController::DoCreateForLowLatency(AudioManager* audio_manager,
+ const AudioParameters& params,
+ const std::string& device_id) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+
+#if defined(AUDIO_POWER_MONITORING)
+ // We only log silence state UMA stats for low latency mode and if we use a
+ // real device.
+ if (params.format() != AudioParameters::AUDIO_FAKE)
+ log_silence_state_ = true;
+#endif
+
+ DoCreate(audio_manager, params, device_id);
+}
+
void AudioInputController::DoCreateForStream(
AudioInputStream* stream_to_control) {
DCHECK(task_runner_->BelongsToCurrentThread());
@@ -329,10 +346,10 @@ void AudioInputController::DoClose() {
user_input_monitor_->DisableKeyPressMonitoring();
#if defined(AUDIO_POWER_MONITORING)
- // Send UMA stats if we have enabled power monitoring.
- if (audio_level_) {
+ // Send UMA stats if enabled.
+ if (log_silence_state_)
LogSilenceState(silence_state_);
- }
+ log_silence_state_ = false;
#endif
state_ = CLOSED;
@@ -503,26 +520,11 @@ void AudioInputController::DoLogAudioLevel(float level_dbfs) {
std::string log_string = base::StringPrintf(
"AIC::OnData: average audio level=%.2f dBFS", level_dbfs);
static const float kSilenceThresholdDBFS = -72.24719896f;
- if (level_dbfs < kSilenceThresholdDBFS) {
+ if (level_dbfs < kSilenceThresholdDBFS)
log_string += " <=> no audio input!";
- if (silence_state_ == SILENCE_STATE_NO_MEASUREMENT)
- silence_state_ = SILENCE_STATE_ONLY_SILENCE;
- else if (silence_state_ == SILENCE_STATE_ONLY_AUDIO)
- silence_state_ = SILENCE_STATE_AUDIO_AND_SILENCE;
- else
- DCHECK(silence_state_ == SILENCE_STATE_ONLY_SILENCE ||
- silence_state_ == SILENCE_STATE_AUDIO_AND_SILENCE);
- } else {
- if (silence_state_ == SILENCE_STATE_NO_MEASUREMENT)
- silence_state_ = SILENCE_STATE_ONLY_AUDIO;
- else if (silence_state_ == SILENCE_STATE_ONLY_SILENCE)
- silence_state_ = SILENCE_STATE_AUDIO_AND_SILENCE;
- else
- DCHECK(silence_state_ == SILENCE_STATE_ONLY_AUDIO ||
- silence_state_ == SILENCE_STATE_AUDIO_AND_SILENCE);
- }
-
handler_->OnLog(this, log_string);
+
+ UpdateSilenceState(level_dbfs < kSilenceThresholdDBFS);
#endif
}
@@ -555,6 +557,28 @@ bool AudioInputController::GetDataIsActive() {
}
#if defined(AUDIO_POWER_MONITORING)
+void AudioInputController::UpdateSilenceState(bool silence) {
+ if (silence) {
+ if (silence_state_ == SILENCE_STATE_NO_MEASUREMENT) {
+ silence_state_ = SILENCE_STATE_ONLY_SILENCE;
+ } else if (silence_state_ == SILENCE_STATE_ONLY_AUDIO) {
+ silence_state_ = SILENCE_STATE_AUDIO_AND_SILENCE;
+ } else {
+ DCHECK(silence_state_ == SILENCE_STATE_ONLY_SILENCE ||
+ silence_state_ == SILENCE_STATE_AUDIO_AND_SILENCE);
+ }
+ } else {
+ if (silence_state_ == SILENCE_STATE_NO_MEASUREMENT) {
+ silence_state_ = SILENCE_STATE_ONLY_AUDIO;
+ } else if (silence_state_ == SILENCE_STATE_ONLY_SILENCE) {
+ silence_state_ = SILENCE_STATE_AUDIO_AND_SILENCE;
+ } else {
+ DCHECK(silence_state_ == SILENCE_STATE_ONLY_AUDIO ||
+ silence_state_ == SILENCE_STATE_AUDIO_AND_SILENCE);
+ }
+ }
+}
+
void AudioInputController::LogSilenceState(SilenceState value) {
UMA_HISTOGRAM_ENUMERATION("Media.AudioInputControllerSessionSilenceReport",
value,
diff --git a/media/audio/audio_input_controller.h b/media/audio/audio_input_controller.h
index e1e14ee3c3..5d555766a1 100644
--- a/media/audio/audio_input_controller.h
+++ b/media/audio/audio_input_controller.h
@@ -191,11 +191,11 @@ class MEDIA_EXPORT AudioInputController
SyncWriter* sync_writer,
UserInputMonitor* user_input_monitor);
- // Factory method for creating an AudioInputController for low-latency mode,
- // taking ownership of |stream|. The stream will be opened on the audio
- // thread, and when that is done, the event handler will receive an
- // OnCreated() call from that same thread. |user_input_monitor| is used for
- // typing detection and can be NULL.
+ // Factory method for creating an AudioInputController with an existing
+ // |stream| for low-latency mode, taking ownership of |stream|. The stream
+ // will be opened on the audio thread, and when that is done, the event
+ // handler will receive an OnCreated() call from that same thread.
+ // |user_input_monitor| is used for typing detection and can be NULL.
static scoped_refptr<AudioInputController> CreateForStream(
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
EventHandler* event_handler,
@@ -251,6 +251,12 @@ class MEDIA_EXPORT AudioInputController
// Elements in this enum should not be deleted or rearranged; the only
// permitted operation is to add new elements before SILENCE_STATE_MAX and
// update SILENCE_STATE_MAX.
+ // Possible silence state transitions:
+ // SILENCE_STATE_AUDIO_AND_SILENCE
+ // ^ ^
+ // SILENCE_STATE_ONLY_AUDIO SILENCE_STATE_ONLY_SILENCE
+ // ^ ^
+ // SILENCE_STATE_NO_MEASUREMENT
enum SilenceState {
SILENCE_STATE_NO_MEASUREMENT = 0,
SILENCE_STATE_ONLY_AUDIO = 1,
@@ -268,6 +274,9 @@ class MEDIA_EXPORT AudioInputController
// Methods called on the audio thread (owned by the AudioManager).
void DoCreate(AudioManager* audio_manager, const AudioParameters& params,
const std::string& device_id);
+ void DoCreateForLowLatency(AudioManager* audio_manager,
+ const AudioParameters& params,
+ const std::string& device_id);
void DoCreateForStream(AudioInputStream* stream_to_control);
void DoRecord();
void DoClose();
@@ -292,6 +301,11 @@ class MEDIA_EXPORT AudioInputController
bool GetDataIsActive();
#if defined(AUDIO_POWER_MONITORING)
+ // Updates the silence state, see enum SilenceState above for state
+ // transitions.
+ void UpdateSilenceState(bool silence);
+
+ // Logs the silence state as UMA stat.
void LogSilenceState(SilenceState value);
#endif
@@ -345,6 +359,9 @@ class MEDIA_EXPORT AudioInputController
media::AudioParameters audio_params_;
base::TimeTicks last_audio_level_log_time_;
+ // Whether the silence state should sent as UMA stat.
+ bool log_silence_state_;
+
// The silence report sent as UMA stat at the end of a session.
SilenceState silence_state_;
#endif
diff --git a/media/audio/pulse/pulse_input.cc b/media/audio/pulse/pulse_input.cc
index 4976b5610e..6c9855e504 100644
--- a/media/audio/pulse/pulse_input.cc
+++ b/media/audio/pulse/pulse_input.cc
@@ -281,7 +281,10 @@ void PulseAudioInputStream::ReadData() {
hardware_delay += fifo_.GetAvailableFrames() * params_.GetBytesPerFrame();
callback_->OnData(this, audio_bus, hardware_delay, normalized_volume);
- base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(5));
+ // Sleep 5ms to wait until render consumes the data in order to avoid
+ // back to back OnData() method.
+ if (fifo_.available_blocks())
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(5));
}
pa_threaded_mainloop_signal(pa_mainloop_, 0);
diff --git a/media/base/android/BUILD.gn b/media/base/android/BUILD.gn
index a1b999935c..5df4dc35e0 100644
--- a/media/base/android/BUILD.gn
+++ b/media/base/android/BUILD.gn
@@ -45,7 +45,10 @@ source_set("android") {
"webaudio_media_codec_info.h",
]
configs += [ "//media:media_config" ]
- deps = [ ":media_jni_headers" ]
+ deps = [
+ ":media_jni_headers",
+ "//third_party/widevine/cdm:version_h",
+ ]
}
source_set("unittests") {
@@ -59,6 +62,7 @@ source_set("unittests") {
"//media/base:test_support",
"//testing/gmock",
"//testing/gtest",
+ "//third_party/widevine/cdm:version_h",
]
configs += [ "//media:media_config" ]
}
diff --git a/media/base/demuxer_perftest.cc b/media/base/demuxer_perftest.cc
index f5a11526b2..ad6736302f 100644
--- a/media/base/demuxer_perftest.cc
+++ b/media/base/demuxer_perftest.cc
@@ -17,7 +17,7 @@
namespace media {
-static const int kBenchmarkIterations = 500;
+static const int kBenchmarkIterations = 100;
class DemuxerHostImpl : public media::DemuxerHost {
public:
diff --git a/media/base/video_frame.cc b/media/base/video_frame.cc
index 456cf6e9d8..6b3427db1a 100644
--- a/media/base/video_frame.cc
+++ b/media/base/video_frame.cc
@@ -689,19 +689,20 @@ VideoFrame::~VideoFrame() {
base::ResetAndReturn(&no_longer_needed_cb_).Run();
}
-bool VideoFrame::IsValidPlane(size_t plane) const {
- return (plane < NumPlanes(format_));
+// static
+bool VideoFrame::IsValidPlane(size_t plane, VideoFrame::Format format) {
+ return (plane < NumPlanes(format));
}
int VideoFrame::stride(size_t plane) const {
- DCHECK(IsValidPlane(plane));
+ DCHECK(IsValidPlane(plane, format_));
return strides_[plane];
}
-int VideoFrame::row_bytes(size_t plane) const {
- DCHECK(IsValidPlane(plane));
- int width = coded_size_.width();
- switch (format_) {
+// static
+int VideoFrame::RowBytes(size_t plane, VideoFrame::Format format, int width) {
+ DCHECK(IsValidPlane(plane, format));
+ switch (format) {
case VideoFrame::YV24:
switch (plane) {
case kYPlane:
@@ -754,13 +755,17 @@ int VideoFrame::row_bytes(size_t plane) const {
case VideoFrame::NATIVE_TEXTURE:
break;
}
- NOTREACHED() << "Unsupported video frame format/plane: "
- << format_ << "/" << plane;
+ NOTREACHED() << "Unsupported video frame format/plane: " << format << "/"
+ << plane;
return 0;
}
+int VideoFrame::row_bytes(size_t plane) const {
+ return RowBytes(plane, format_, coded_size_.width());
+}
+
int VideoFrame::rows(size_t plane) const {
- DCHECK(IsValidPlane(plane));
+ DCHECK(IsValidPlane(plane, format_));
int height = coded_size_.height();
switch (format_) {
case VideoFrame::YV24:
@@ -822,7 +827,7 @@ int VideoFrame::rows(size_t plane) const {
}
uint8* VideoFrame::data(size_t plane) const {
- DCHECK(IsValidPlane(plane));
+ DCHECK(IsValidPlane(plane, format_));
return data_[plane];
}
@@ -854,7 +859,7 @@ int VideoFrame::dmabuf_fd(size_t plane) const {
void VideoFrame::HashFrameForTesting(base::MD5Context* context) {
for (int plane = 0; plane < kMaxPlanes; ++plane) {
- if (!IsValidPlane(plane))
+ if (!IsValidPlane(plane, format_))
break;
for (int row = 0; row < rows(plane); ++row) {
base::MD5Update(context, base::StringPiece(
diff --git a/media/base/video_frame.h b/media/base/video_frame.h
index 5939c45211..9ad9a3d937 100644
--- a/media/base/video_frame.h
+++ b/media/base/video_frame.h
@@ -219,6 +219,10 @@ class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> {
// Returns horizontal bits per pixel for given |plane| and |format|.
static int PlaneHorizontalBitsPerPixel(Format format, size_t plane);
+ // Returns the number of bytes per row for the given plane, format, and width.
+ // The width may be aligned to format requirements.
+ static int RowBytes(size_t plane, Format format, int width);
+
Format format() const { return format_; }
const gfx::Size& coded_size() const { return coded_size_; }
@@ -284,6 +288,11 @@ class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> {
private:
friend class base::RefCountedThreadSafe<VideoFrame>;
+
+ // Returns true if |plane| is a valid plane number for the given format. This
+ // can be used to DCHECK() plane parameters.
+ static bool IsValidPlane(size_t plane, VideoFrame::Format format);
+
// Clients must use the static CreateFrame() method to create a new frame.
VideoFrame(Format format,
const gfx::Size& coded_size,
@@ -296,9 +305,6 @@ class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> {
void AllocateYUV();
- // Used to DCHECK() plane parameters.
- bool IsValidPlane(size_t plane) const;
-
// Frame format.
const Format format_;
diff --git a/media/cast/BUILD.gn b/media/cast/BUILD.gn
index 8d38089dae..e1c9e66330 100644
--- a/media/cast/BUILD.gn
+++ b/media/cast/BUILD.gn
@@ -72,8 +72,6 @@ source_set("net") {
"net/rtcp/rtcp_defines.h",
"net/rtcp/rtcp.h",
"net/rtcp/rtcp.cc",
- "net/rtcp/rtcp_receiver.cc",
- "net/rtcp/rtcp_receiver.h",
"net/rtcp/rtcp_sender.cc",
"net/rtcp/rtcp_sender.h",
"net/rtcp/rtcp_utility.cc",
@@ -237,11 +235,9 @@ test("cast_unittests") {
"net/pacing/mock_paced_packet_sender.cc",
"net/pacing/mock_paced_packet_sender.h",
"net/pacing/paced_sender_unittest.cc",
- "net/rtcp/mock_rtcp_receiver_feedback.cc",
- "net/rtcp/mock_rtcp_receiver_feedback.h",
- "net/rtcp/rtcp_receiver_unittest.cc",
"net/rtcp/rtcp_sender_unittest.cc",
"net/rtcp/rtcp_unittest.cc",
+ "net/rtcp/rtcp_utility_unittest.cc",
"net/rtcp/receiver_rtcp_event_subscriber_unittest.cc",
# TODO(miu): The following two are test utility modules. Rename/move the files.
"net/rtcp/test_rtcp_packet_builder.cc",
diff --git a/media/cast/cast.gyp b/media/cast/cast.gyp
index 303748a54f..2f83f75cf2 100644
--- a/media/cast/cast.gyp
+++ b/media/cast/cast.gyp
@@ -192,8 +192,6 @@
'net/rtcp/rtcp_defines.h',
'net/rtcp/rtcp.h',
'net/rtcp/rtcp.cc',
- 'net/rtcp/rtcp_receiver.cc',
- 'net/rtcp/rtcp_receiver.h',
'net/rtcp/rtcp_sender.cc',
'net/rtcp/rtcp_sender.h',
'net/rtcp/rtcp_utility.cc',
diff --git a/media/cast/cast_defines.h b/media/cast/cast_defines.h
index c02fa2bfa7..faafddb885 100644
--- a/media/cast/cast_defines.h
+++ b/media/cast/cast_defines.h
@@ -27,7 +27,10 @@ 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 = 60;
+// This value is carefully choosen such that it fits in the 8-bits range for
+// frame IDs. It is also less than half of the full 8-bits range such that we
+// can handle wrap around and compare two frame IDs.
+const int kMaxUnackedFrames = 120;
const int kStartRttMs = 20;
const int64 kCastMessageUpdateIntervalMs = 33;
diff --git a/media/cast/cast_sender.h b/media/cast/cast_sender.h
index c9bad7aa1e..7615c28602 100644
--- a/media/cast/cast_sender.h
+++ b/media/cast/cast_sender.h
@@ -86,6 +86,11 @@ class CastSender {
const CastInitializationCallback& cast_initialization_cb,
const CreateVideoEncodeAcceleratorCallback& create_vea_cb,
const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb) = 0;
+
+ // Change the target delay. This is only valid if the receiver
+ // supports the "adaptive_target_delay" rtp extension.
+ virtual void SetTargetPlayoutDelay(
+ base::TimeDelta new_target_playout_delay) = 0;
};
} // namespace cast
diff --git a/media/cast/cast_sender_impl.cc b/media/cast/cast_sender_impl.cc
index c684858775..19a054f513 100644
--- a/media/cast/cast_sender_impl.cc
+++ b/media/cast/cast_sender_impl.cc
@@ -9,7 +9,6 @@
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "media/base/video_frame.h"
-#include "media/cast/net/rtcp/rtcp_receiver.h"
namespace media {
namespace cast {
@@ -111,6 +110,10 @@ void CastSenderImpl::InitializeAudio(
new LocalAudioFrameInput(cast_environment_, audio_sender_->AsWeakPtr());
}
cast_initialization_cb.Run(status);
+ if (video_sender_) {
+ DCHECK(audio_sender_->GetTargetPlayoutDelay() ==
+ video_sender_->GetTargetPlayoutDelay());
+ }
}
void CastSenderImpl::InitializeVideo(
@@ -137,6 +140,10 @@ void CastSenderImpl::InitializeVideo(
new LocalVideoFrameInput(cast_environment_, video_sender_->AsWeakPtr());
}
cast_initialization_cb.Run(status);
+ if (audio_sender_) {
+ DCHECK(audio_sender_->GetTargetPlayoutDelay() ==
+ video_sender_->GetTargetPlayoutDelay());
+ }
}
CastSenderImpl::~CastSenderImpl() {
@@ -151,5 +158,17 @@ scoped_refptr<VideoFrameInput> CastSenderImpl::video_frame_input() {
return video_frame_input_;
}
+void CastSenderImpl::SetTargetPlayoutDelay(
+ base::TimeDelta new_target_playout_delay) {
+ VLOG(1) << "CastSenderImpl@" << this << "::SetTargetPlayoutDelay("
+ << new_target_playout_delay.InMilliseconds() << " ms)";
+ if (audio_sender_) {
+ audio_sender_->SetTargetPlayoutDelay(new_target_playout_delay);
+ }
+ if (video_sender_) {
+ video_sender_->SetTargetPlayoutDelay(new_target_playout_delay);
+ }
+}
+
} // namespace cast
} // namespace media
diff --git a/media/cast/cast_sender_impl.h b/media/cast/cast_sender_impl.h
index c34fccf0b1..c4680abf16 100644
--- a/media/cast/cast_sender_impl.h
+++ b/media/cast/cast_sender_impl.h
@@ -37,6 +37,9 @@ class CastSenderImpl : public CastSender {
const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb)
OVERRIDE;
+ virtual void SetTargetPlayoutDelay(
+ base::TimeDelta new_target_playout_delay) OVERRIDE;
+
virtual ~CastSenderImpl();
virtual scoped_refptr<AudioFrameInput> audio_frame_input() OVERRIDE;
diff --git a/media/cast/cast_testing.gypi b/media/cast/cast_testing.gypi
index 21585c81ab..ef12af72f2 100644
--- a/media/cast/cast_testing.gypi
+++ b/media/cast/cast_testing.gypi
@@ -86,11 +86,9 @@
'net/pacing/mock_paced_packet_sender.cc',
'net/pacing/mock_paced_packet_sender.h',
'net/pacing/paced_sender_unittest.cc',
- 'net/rtcp/mock_rtcp_receiver_feedback.cc',
- 'net/rtcp/mock_rtcp_receiver_feedback.h',
- 'net/rtcp/rtcp_receiver_unittest.cc',
'net/rtcp/rtcp_sender_unittest.cc',
'net/rtcp/rtcp_unittest.cc',
+ 'net/rtcp/rtcp_utility_unittest.cc',
'net/rtcp/receiver_rtcp_event_subscriber_unittest.cc',
# TODO(miu): The following two are test utility modules. Rename/move the files.
'net/rtcp/test_rtcp_packet_builder.cc',
diff --git a/media/cast/net/cast_transport_config.cc b/media/cast/net/cast_transport_config.cc
index a607f6fcb4..b6c9a07aa6 100644
--- a/media/cast/net/cast_transport_config.cc
+++ b/media/cast/net/cast_transport_config.cc
@@ -16,7 +16,8 @@ EncodedFrame::EncodedFrame()
: dependency(UNKNOWN_DEPENDENCY),
frame_id(0),
referenced_frame_id(0),
- rtp_timestamp(0) {}
+ rtp_timestamp(0),
+ new_playout_delay_ms(0) {}
EncodedFrame::~EncodedFrame() {}
diff --git a/media/cast/net/cast_transport_config.h b/media/cast/net/cast_transport_config.h
index fa0f8f2ac8..dcfd133bd0 100644
--- a/media/cast/net/cast_transport_config.h
+++ b/media/cast/net/cast_transport_config.h
@@ -117,6 +117,10 @@ struct EncodedFrame {
// timestamps; and it may not necessarily increment with precise regularity.
base::TimeTicks reference_time;
+ // Playout delay for this and all future frames. Used by the Adaptive
+ // Playout delay extension. Zero means no change.
+ uint16 new_playout_delay_ms;
+
// The encoded signal data.
std::string data;
};
@@ -135,6 +139,10 @@ class PacketSender {
// 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;
+
+ // Returns the number of bytes ever sent.
+ virtual int64 GetBytesSent() = 0;
+
virtual ~PacketSender() {}
};
diff --git a/media/cast/net/cast_transport_defines.h b/media/cast/net/cast_transport_defines.h
index 63407aa7ba..f7d681c7ad 100644
--- a/media/cast/net/cast_transport_defines.h
+++ b/media/cast/net/cast_transport_defines.h
@@ -47,15 +47,6 @@ enum RtcpPacketFields {
kPacketTypeHigh = 210, // Port Mapping.
};
-enum RtcpPacketField {
- kRtcpSr = 0x0002,
- kRtcpRr = 0x0004,
- kRtcpDlrr = 0x0400,
- kRtcpRrtr = 0x0800,
- kRtcpCast = 0x20000,
- kRtcpReceiverLog = 0x80000,
- };
-
// Each uint16 represents one packet id within a cast frame.
typedef std::set<uint16> PacketIdSet;
// Each uint8 represents one cast frame.
diff --git a/media/cast/net/cast_transport_sender.h b/media/cast/net/cast_transport_sender.h
index 31166f549d..9c75d12275 100644
--- a/media/cast/net/cast_transport_sender.h
+++ b/media/cast/net/cast_transport_sender.h
@@ -85,19 +85,15 @@ class CastTransportSender : public base::NonThreadSafe {
base::TimeTicks current_time,
uint32 current_time_as_rtp_timestamp) = 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;
+ // Cancels sending packets for the frames in the set.
+ // |ssrc| is the SSRC for the stream.
+ // |frame_ids| contains the IDs of the frames that will be cancelled.
+ virtual void CancelSendingFrames(uint32 ssrc,
+ const std::vector<uint32>& frame_ids) = 0;
+
+ // Resends a frame or part of a frame to kickstart. This is used when the
+ // stream appears to be stalled.
+ virtual void ResendFrameForKickstart(uint32 ssrc, uint32 frame_id) = 0;
// Returns a callback for receiving packets for testing purposes.
virtual PacketReceiverCallback PacketReceiverForTesting();
diff --git a/media/cast/net/cast_transport_sender_impl.cc b/media/cast/net/cast_transport_sender_impl.cc
index 248c69d6bb..478f75a39b 100644
--- a/media/cast/net/cast_transport_sender_impl.cc
+++ b/media/cast/net/cast_transport_sender_impl.cc
@@ -60,6 +60,7 @@ CastTransportSenderImpl::CastTransportSenderImpl(
transport_task_runner),
raw_events_callback_(raw_events_callback),
raw_events_callback_interval_(raw_events_callback_interval),
+ last_byte_acked_for_audio_(0),
weak_factory_(this) {
DCHECK(clock_);
if (!raw_events_callback_.is_null()) {
@@ -111,7 +112,9 @@ void CastTransportSenderImpl::InitializeAudio(
}
audio_rtcp_session_.reset(
- new Rtcp(cast_message_cb,
+ new Rtcp(base::Bind(&CastTransportSenderImpl::OnReceivedCastMessage,
+ weak_factory_.GetWeakPtr(), config.ssrc,
+ cast_message_cb),
rtt_cb,
base::Bind(&CastTransportSenderImpl::OnReceivedLogMessage,
weak_factory_.GetWeakPtr(), AUDIO_EVENT),
@@ -142,7 +145,9 @@ void CastTransportSenderImpl::InitializeVideo(
}
video_rtcp_session_.reset(
- new Rtcp(cast_message_cb,
+ new Rtcp(base::Bind(&CastTransportSenderImpl::OnReceivedCastMessage,
+ weak_factory_.GetWeakPtr(), config.ssrc,
+ cast_message_cb),
rtt_cb,
base::Bind(&CastTransportSenderImpl::OnReceivedLogMessage,
weak_factory_.GetWeakPtr(), VIDEO_EVENT),
@@ -202,21 +207,48 @@ void CastTransportSenderImpl::SendSenderReport(
}
}
+void CastTransportSenderImpl::CancelSendingFrames(
+ uint32 ssrc,
+ const std::vector<uint32>& frame_ids) {
+ if (audio_sender_ && ssrc == audio_sender_->ssrc()) {
+ audio_sender_->CancelSendingFrames(frame_ids);
+ } else if (video_sender_ && ssrc == video_sender_->ssrc()) {
+ video_sender_->CancelSendingFrames(frame_ids);
+ } else {
+ NOTREACHED() << "Invalid request for cancel sending.";
+ }
+}
+
+void CastTransportSenderImpl::ResendFrameForKickstart(uint32 ssrc,
+ uint32 frame_id) {
+ if (audio_sender_ && ssrc == audio_sender_->ssrc()) {
+ DCHECK(audio_rtcp_session_);
+ audio_sender_->ResendFrameForKickstart(frame_id,
+ audio_rtcp_session_->rtt());
+ } else if (video_sender_ && ssrc == video_sender_->ssrc()) {
+ DCHECK(video_rtcp_session_);
+ video_sender_->ResendFrameForKickstart(frame_id,
+ video_rtcp_session_->rtt());
+ } else {
+ NOTREACHED() << "Invalid request for kickstart.";
+ }
+}
+
void CastTransportSenderImpl::ResendPackets(
- bool is_audio,
+ uint32 ssrc,
const MissingFramesAndPacketsMap& missing_packets,
bool cancel_rtx_if_not_in_list,
- base::TimeDelta dedupe_window) {
- if (is_audio) {
- DCHECK(audio_sender_) << "Audio sender uninitialized";
+ const DedupInfo& dedup_info) {
+ if (audio_sender_ && ssrc == audio_sender_->ssrc()) {
audio_sender_->ResendPackets(missing_packets,
cancel_rtx_if_not_in_list,
- dedupe_window);
- } else {
- DCHECK(video_sender_) << "Video sender uninitialized";
+ dedup_info);
+ } else if (video_sender_ && ssrc == video_sender_->ssrc()) {
video_sender_->ResendPackets(missing_packets,
cancel_rtx_if_not_in_list,
- dedupe_window);
+ dedup_info);
+ } else {
+ NOTREACHED() << "Invalid request for retransmission.";
}
}
@@ -293,5 +325,41 @@ void CastTransportSenderImpl::OnReceivedLogMessage(
}
}
+void CastTransportSenderImpl::OnReceivedCastMessage(
+ uint32 ssrc,
+ const RtcpCastMessageCallback& cast_message_cb,
+ const RtcpCastMessage& cast_message) {
+ if (!cast_message_cb.is_null())
+ cast_message_cb.Run(cast_message);
+
+ DedupInfo dedup_info;
+ if (audio_sender_ && audio_sender_->ssrc() == ssrc) {
+ const int64 acked_bytes =
+ audio_sender_->GetLastByteSentForFrame(cast_message.ack_frame_id);
+ last_byte_acked_for_audio_ =
+ std::max(acked_bytes, last_byte_acked_for_audio_);
+ } else if (video_sender_ && video_sender_->ssrc() == ssrc) {
+ dedup_info.resend_interval = video_rtcp_session_->rtt();
+
+ // Only use audio stream to dedup if there is one.
+ if (audio_sender_) {
+ dedup_info.last_byte_acked_for_audio = last_byte_acked_for_audio_;
+ }
+ }
+
+ if (cast_message.missing_frames_and_packets.empty())
+ return;
+
+ // This call does two things.
+ // 1. Specifies that retransmissions for packets not listed in the set are
+ // cancelled.
+ // 2. Specifies a deduplication window. For video this would be the most
+ // recent RTT. For audio there is no deduplication.
+ ResendPackets(ssrc,
+ cast_message.missing_frames_and_packets,
+ true,
+ dedup_info);
+}
+
} // namespace cast
} // namespace media
diff --git a/media/cast/net/cast_transport_sender_impl.h b/media/cast/net/cast_transport_sender_impl.h
index cee845223c..a9c92f7f47 100644
--- a/media/cast/net/cast_transport_sender_impl.h
+++ b/media/cast/net/cast_transport_sender_impl.h
@@ -25,6 +25,7 @@
#define MEDIA_CAST_NET_CAST_TRANSPORT_IMPL_H_
#include "base/callback.h"
+#include "base/gtest_prod_util.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
@@ -79,15 +80,30 @@ class CastTransportSenderImpl : public CastTransportSender {
base::TimeTicks current_time,
uint32 current_time_as_rtp_timestamp) OVERRIDE;
- virtual void ResendPackets(bool is_audio,
- const MissingFramesAndPacketsMap& missing_packets,
- bool cancel_rtx_if_not_in_list,
- base::TimeDelta dedupe_window)
- OVERRIDE;
+ virtual void CancelSendingFrames(
+ uint32 ssrc,
+ const std::vector<uint32>& frame_ids) OVERRIDE;
+
+ virtual void ResendFrameForKickstart(uint32 ssrc, uint32 frame_id) OVERRIDE;
virtual PacketReceiverCallback PacketReceiverForTesting() OVERRIDE;
private:
+ FRIEND_TEST_ALL_PREFIXES(CastTransportSenderImplTest, NacksCancelRetransmits);
+ FRIEND_TEST_ALL_PREFIXES(CastTransportSenderImplTest, CancelRetransmits);
+ FRIEND_TEST_ALL_PREFIXES(CastTransportSenderImplTest, Kickstart);
+ FRIEND_TEST_ALL_PREFIXES(CastTransportSenderImplTest,
+ DedupRetransmissionWithAudio);
+
+ // Resend packets for the stream identified by |ssrc|.
+ // If |cancel_rtx_if_not_in_list| is true then transmission of packets for the
+ // frames but not in the list will be dropped.
+ // See PacedSender::ResendPackets() to see how |dedup_info| works.
+ void ResendPackets(uint32 ssrc,
+ const MissingFramesAndPacketsMap& missing_packets,
+ bool cancel_rtx_if_not_in_list,
+ const DedupInfo& dedup_info);
+
// If |raw_events_callback_| is non-null, calls it with events collected
// by |event_subscriber_| since last call.
void SendRawEvents();
@@ -99,6 +115,11 @@ class CastTransportSenderImpl : public CastTransportSender {
void OnReceivedLogMessage(EventMediaType media_type,
const RtcpReceiverLogMessage& log);
+ // Called when a RTCP Cast message is received.
+ void OnReceivedCastMessage(uint32 ssrc,
+ const RtcpCastMessageCallback& cast_message_cb,
+ const RtcpCastMessage& cast_message);
+
base::TickClock* clock_; // Not owned by this class.
CastTransportStatusCallback status_callback_;
scoped_refptr<base::SingleThreadTaskRunner> transport_task_runner_;
@@ -132,6 +153,11 @@ class CastTransportSenderImpl : public CastTransportSender {
BulkRawEventsCallback raw_events_callback_;
base::TimeDelta raw_events_callback_interval_;
+ // Right after a frame is sent we record the number of bytes sent to the
+ // socket. We record the corresponding bytes sent for the most recent ACKed
+ // audio packet.
+ int64 last_byte_acked_for_audio_;
+
base::WeakPtrFactory<CastTransportSenderImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(CastTransportSenderImpl);
diff --git a/media/cast/net/cast_transport_sender_impl_unittest.cc b/media/cast/net/cast_transport_sender_impl_unittest.cc
index b8a49cce1f..1cbdf5293a 100644
--- a/media/cast/net/cast_transport_sender_impl_unittest.cc
+++ b/media/cast/net/cast_transport_sender_impl_unittest.cc
@@ -19,15 +19,50 @@
namespace media {
namespace cast {
-static const int64 kStartMillisecond = INT64_C(12345678900000);
+namespace {
+const int64 kStartMillisecond = INT64_C(12345678900000);
+const uint32 kVideoSsrc = 1;
+const uint32 kAudioSsrc = 2;
+} // namespace
class FakePacketSender : public PacketSender {
public:
- FakePacketSender() {}
+ FakePacketSender()
+ : paused_(false), packets_sent_(0), bytes_sent_(0) {}
virtual bool SendPacket(PacketRef packet, const base::Closure& cb) OVERRIDE {
+ if (paused_) {
+ stored_packet_ = packet;
+ callback_ = cb;
+ return false;
+ }
+ ++packets_sent_;
+ bytes_sent_ += packet->data.size();
return true;
}
+
+ virtual int64 GetBytesSent() OVERRIDE {
+ return bytes_sent_;
+ }
+
+ void SetPaused(bool paused) {
+ paused_ = paused;
+ if (!paused && stored_packet_) {
+ SendPacket(stored_packet_, callback_);
+ callback_.Run();
+ }
+ }
+
+ int packets_sent() const { return packets_sent_; }
+
+ private:
+ bool paused_;
+ base::Closure callback_;
+ PacketRef stored_packet_;
+ int packets_sent_;
+ int64 bytes_sent_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakePacketSender);
};
class CastTransportSenderImplTest : public ::testing::Test {
@@ -68,6 +103,28 @@ class CastTransportSenderImplTest : public ::testing::Test {
task_runner_->RunTasks();
}
+ void InitializeVideo() {
+ CastTransportRtpConfig rtp_config;
+ rtp_config.ssrc = kVideoSsrc;
+ rtp_config.feedback_ssrc = 2;
+ rtp_config.rtp_payload_type = 3;
+ rtp_config.stored_frames = 10;
+ transport_sender_->InitializeVideo(rtp_config,
+ RtcpCastMessageCallback(),
+ RtcpRttCallback());
+ }
+
+ void InitializeAudio() {
+ CastTransportRtpConfig rtp_config;
+ rtp_config.ssrc = kAudioSsrc;
+ rtp_config.feedback_ssrc = 3;
+ rtp_config.rtp_payload_type = 4;
+ rtp_config.stored_frames = 10;
+ transport_sender_->InitializeAudio(rtp_config,
+ RtcpCastMessageCallback(),
+ RtcpRttCallback());
+ }
+
void LogRawEvents(const std::vector<PacketEvent>& packet_events,
const std::vector<FrameEvent>& frame_events) {
num_times_callback_called_++;
@@ -95,5 +152,199 @@ TEST_F(CastTransportSenderImplTest, InitWithLogging) {
EXPECT_EQ(5, num_times_callback_called_);
}
+TEST_F(CastTransportSenderImplTest, NacksCancelRetransmits) {
+ InitWithoutLogging();
+ InitializeVideo();
+ task_runner_->Sleep(base::TimeDelta::FromMilliseconds(50));
+
+ // A fake frame that will be decomposed into 4 packets.
+ EncodedFrame fake_frame;
+ fake_frame.frame_id = 1;
+ fake_frame.rtp_timestamp = 1;
+ fake_frame.dependency = EncodedFrame::KEY;
+ fake_frame.data.resize(5000, ' ');
+
+ transport_sender_->InsertCodedVideoFrame(fake_frame);
+ task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10));
+ EXPECT_EQ(4, transport_.packets_sent());
+
+ // Resend packet 0.
+ MissingFramesAndPacketsMap missing_packets;
+ missing_packets[1].insert(0);
+ missing_packets[1].insert(1);
+ missing_packets[1].insert(2);
+
+ transport_.SetPaused(true);
+ DedupInfo dedup_info;
+ dedup_info.resend_interval = base::TimeDelta::FromMilliseconds(10);
+ transport_sender_->ResendPackets(
+ kVideoSsrc, missing_packets, true, dedup_info);
+
+ task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10));
+
+ RtcpCastMessage cast_message;
+ cast_message.media_ssrc = kVideoSsrc;
+ cast_message.ack_frame_id = 1;
+ cast_message.missing_frames_and_packets[1].insert(3);
+ transport_sender_->OnReceivedCastMessage(kVideoSsrc,
+ RtcpCastMessageCallback(),
+ cast_message);
+ transport_.SetPaused(false);
+ task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10));
+
+ // Resend one packet in the socket when unpaused.
+ // Resend one more packet from NACK.
+ EXPECT_EQ(6, transport_.packets_sent());
+}
+
+TEST_F(CastTransportSenderImplTest, CancelRetransmits) {
+ InitWithoutLogging();
+ InitializeVideo();
+ task_runner_->Sleep(base::TimeDelta::FromMilliseconds(50));
+
+ // A fake frame that will be decomposed into 4 packets.
+ EncodedFrame fake_frame;
+ fake_frame.frame_id = 1;
+ fake_frame.rtp_timestamp = 1;
+ fake_frame.dependency = EncodedFrame::KEY;
+ fake_frame.data.resize(5000, ' ');
+
+ transport_sender_->InsertCodedVideoFrame(fake_frame);
+ task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10));
+ EXPECT_EQ(4, transport_.packets_sent());
+
+ // Resend all packets for frame 1.
+ MissingFramesAndPacketsMap missing_packets;
+ missing_packets[1].insert(kRtcpCastAllPacketsLost);
+
+ transport_.SetPaused(true);
+ DedupInfo dedup_info;
+ dedup_info.resend_interval = base::TimeDelta::FromMilliseconds(10);
+ transport_sender_->ResendPackets(
+ kVideoSsrc, missing_packets, true, dedup_info);
+
+ task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10));
+ std::vector<uint32> cancel_sending_frames;
+ cancel_sending_frames.push_back(1);
+ transport_sender_->CancelSendingFrames(kVideoSsrc,
+ cancel_sending_frames);
+ transport_.SetPaused(false);
+ task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10));
+
+ // Resend one packet in the socket when unpaused.
+ EXPECT_EQ(5, transport_.packets_sent());
+}
+
+TEST_F(CastTransportSenderImplTest, Kickstart) {
+ InitWithoutLogging();
+ InitializeVideo();
+ task_runner_->Sleep(base::TimeDelta::FromMilliseconds(50));
+
+ // A fake frame that will be decomposed into 4 packets.
+ EncodedFrame fake_frame;
+ fake_frame.frame_id = 1;
+ fake_frame.rtp_timestamp = 1;
+ fake_frame.dependency = EncodedFrame::KEY;
+ fake_frame.data.resize(5000, ' ');
+
+ transport_.SetPaused(true);
+ transport_sender_->InsertCodedVideoFrame(fake_frame);
+ transport_sender_->ResendFrameForKickstart(kVideoSsrc, 1);
+ transport_.SetPaused(false);
+ task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10));
+ EXPECT_EQ(4, transport_.packets_sent());
+
+ // Resend 2 packets for frame 1.
+ MissingFramesAndPacketsMap missing_packets;
+ missing_packets[1].insert(0);
+ missing_packets[1].insert(1);
+
+ transport_.SetPaused(true);
+ DedupInfo dedup_info;
+ dedup_info.resend_interval = base::TimeDelta::FromMilliseconds(10);
+ transport_sender_->ResendPackets(
+ kVideoSsrc, missing_packets, true, dedup_info);
+ transport_sender_->ResendFrameForKickstart(kVideoSsrc, 1);
+ transport_.SetPaused(false);
+ task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10));
+
+ // Resend one packet in the socket when unpaused.
+ // Two more retransmission packets sent.
+ EXPECT_EQ(7, transport_.packets_sent());
+}
+
+TEST_F(CastTransportSenderImplTest, DedupRetransmissionWithAudio) {
+ InitWithoutLogging();
+ InitializeAudio();
+ InitializeVideo();
+ task_runner_->Sleep(base::TimeDelta::FromMilliseconds(50));
+
+ // Send two audio frames.
+ EncodedFrame fake_audio;
+ fake_audio.frame_id = 1;
+ fake_audio.reference_time = testing_clock_.NowTicks();
+ fake_audio.dependency = EncodedFrame::KEY;
+ fake_audio.data.resize(100, ' ');
+ transport_sender_->InsertCodedAudioFrame(fake_audio);
+ task_runner_->Sleep(base::TimeDelta::FromMilliseconds(2));
+ fake_audio.frame_id = 2;
+ fake_audio.reference_time = testing_clock_.NowTicks();
+ transport_sender_->InsertCodedAudioFrame(fake_audio);
+ task_runner_->Sleep(base::TimeDelta::FromMilliseconds(2));
+ EXPECT_EQ(2, transport_.packets_sent());
+
+ // Ack the first audio frame.
+ RtcpCastMessage cast_message;
+ cast_message.media_ssrc = kAudioSsrc;
+ cast_message.ack_frame_id = 1;
+ transport_sender_->OnReceivedCastMessage(kAudioSsrc,
+ RtcpCastMessageCallback(),
+ cast_message);
+ task_runner_->RunTasks();
+ EXPECT_EQ(2, transport_.packets_sent());
+
+ // Send a fake video frame that will be decomposed into 4 packets.
+ EncodedFrame fake_video;
+ fake_video.frame_id = 1;
+ fake_video.dependency = EncodedFrame::KEY;
+ fake_video.data.resize(5000, ' ');
+ transport_sender_->InsertCodedVideoFrame(fake_video);
+ task_runner_->RunTasks();
+ EXPECT_EQ(6, transport_.packets_sent());
+
+ // Retransmission is reject because audio is not acked yet.
+ cast_message.media_ssrc = kVideoSsrc;
+ cast_message.ack_frame_id = 0;
+ cast_message.missing_frames_and_packets[1].insert(3);
+ task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10));
+ transport_sender_->OnReceivedCastMessage(kVideoSsrc,
+ RtcpCastMessageCallback(),
+ cast_message);
+ task_runner_->RunTasks();
+ EXPECT_EQ(6, transport_.packets_sent());
+
+ // Ack the second audio frame.
+ cast_message.media_ssrc = kAudioSsrc;
+ cast_message.ack_frame_id = 2;
+ cast_message.missing_frames_and_packets.clear();
+ task_runner_->Sleep(base::TimeDelta::FromMilliseconds(2));
+ transport_sender_->OnReceivedCastMessage(kAudioSsrc,
+ RtcpCastMessageCallback(),
+ cast_message);
+ task_runner_->RunTasks();
+ EXPECT_EQ(6, transport_.packets_sent());
+
+ // Retransmission of video packet now accepted.
+ cast_message.media_ssrc = kVideoSsrc;
+ cast_message.ack_frame_id = 1;
+ cast_message.missing_frames_and_packets[1].insert(3);
+ task_runner_->Sleep(base::TimeDelta::FromMilliseconds(2));
+ transport_sender_->OnReceivedCastMessage(kVideoSsrc,
+ RtcpCastMessageCallback(),
+ cast_message);
+ task_runner_->RunTasks();
+ EXPECT_EQ(7, transport_.packets_sent());
+}
+
} // namespace cast
} // namespace media
diff --git a/media/cast/net/pacing/mock_paced_packet_sender.h b/media/cast/net/pacing/mock_paced_packet_sender.h
index 2f7f1b2650..1920ef23ae 100644
--- a/media/cast/net/pacing/mock_paced_packet_sender.h
+++ b/media/cast/net/pacing/mock_paced_packet_sender.h
@@ -18,7 +18,7 @@ class MockPacedPacketSender : public PacedPacketSender {
MOCK_METHOD1(SendPackets, bool(const SendPacketVector& packets));
MOCK_METHOD2(ResendPackets, bool(const SendPacketVector& packets,
- base::TimeDelta dedupe_window));
+ const DedupInfo& dedup_info));
MOCK_METHOD2(SendRtcpPacket, bool(unsigned int ssrc, PacketRef packet));
MOCK_METHOD1(CancelSendingPacket, void(const PacketKey& packet_key));
};
diff --git a/media/cast/net/pacing/paced_sender.cc b/media/cast/net/pacing/paced_sender.cc
index 3677da736d..b83dc0f1c8 100644
--- a/media/cast/net/pacing/paced_sender.cc
+++ b/media/cast/net/pacing/paced_sender.cc
@@ -22,8 +22,15 @@ static const size_t kTargetBurstSize = 10;
static const size_t kMaxBurstSize = 20;
static const size_t kMaxDedupeWindowMs = 500;
+// Number of packets that we keep the information of sent time and sent bytes.
+// This number allows 0.5 seconds of history if sending at maximum rate.
+static const size_t kPacketHistorySize =
+ kMaxBurstSize * kMaxDedupeWindowMs / kPacingIntervalMs;
+
} // namespace
+DedupInfo::DedupInfo() : last_byte_acked_for_audio(0) {}
+
// static
PacketKey PacedPacketSender::MakePacketKey(const base::TimeTicks& ticks,
uint32 ssrc,
@@ -31,6 +38,9 @@ PacketKey PacedPacketSender::MakePacketKey(const base::TimeTicks& ticks,
return std::make_pair(ticks, std::make_pair(ssrc, packet_id));
}
+PacedSender::PacketSendRecord::PacketSendRecord()
+ : last_byte_sent(0), last_byte_sent_for_audio(0) {}
+
PacedSender::PacedSender(
base::TickClock* clock,
LoggingImpl* logging,
@@ -64,6 +74,20 @@ void PacedSender::RegisterPrioritySsrc(uint32 ssrc) {
priority_ssrcs_.push_back(ssrc);
}
+int64 PacedSender::GetLastByteSentForPacket(const PacketKey& packet_key) {
+ PacketSendHistory::const_iterator it = send_history_.find(packet_key);
+ if (it == send_history_.end())
+ return 0;
+ return it->second.last_byte_sent;
+}
+
+int64 PacedSender::GetLastByteSentForSsrc(uint32 ssrc) {
+ std::map<uint32, int64>::const_iterator it = last_byte_sent_.find(ssrc);
+ if (it == last_byte_sent_.end())
+ return 0;
+ return it->second;
+}
+
bool PacedSender::SendPackets(const SendPacketVector& packets) {
if (packets.empty()) {
return true;
@@ -85,18 +109,42 @@ bool PacedSender::SendPackets(const SendPacketVector& packets) {
return true;
}
+bool PacedSender::ShouldResend(const PacketKey& packet_key,
+ const DedupInfo& dedup_info,
+ const base::TimeTicks& now) {
+ PacketSendHistory::const_iterator it = send_history_.find(packet_key);
+
+ // No history of previous transmission. It might be sent too long ago.
+ if (it == send_history_.end())
+ return true;
+
+ // Suppose there is request to retransmit X and there is an audio
+ // packet Y sent just before X. Reject retransmission of X if ACK for
+ // Y has not been received.
+ // Only do this for video packets.
+ if (packet_key.second.first == video_ssrc_) {
+ if (dedup_info.last_byte_acked_for_audio &&
+ it->second.last_byte_sent_for_audio &&
+ dedup_info.last_byte_acked_for_audio <
+ it->second.last_byte_sent_for_audio) {
+ return false;
+ }
+ }
+ // Retransmission interval has to be greater than |resend_interval|.
+ if (now - it->second.time < dedup_info.resend_interval)
+ return false;
+ return true;
+}
+
bool PacedSender::ResendPackets(const SendPacketVector& packets,
- base::TimeDelta dedupe_window) {
+ const DedupInfo& dedup_info) {
if (packets.empty()) {
return true;
}
const bool high_priority = IsHighPriority(packets.begin()->first);
- base::TimeTicks now = clock_->NowTicks();
+ const 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) {
+ if (!ShouldResend(packets[i].first, dedup_info, now)) {
LogPacketEvent(packets[i].second->data, PACKET_RTX_REJECTED);
continue;
}
@@ -225,8 +273,8 @@ void PacedSender::SendStoredPackets() {
PacketType packet_type;
PacketKey packet_key;
PacketRef packet = PopNextPacket(&packet_type, &packet_key);
- sent_time_[packet_key] = now;
- sent_time_buffer_[packet_key] = now;
+ PacketSendRecord send_record;
+ send_record.time = now;
switch (packet_type) {
case PacketType_Resend:
@@ -238,20 +286,29 @@ void PacedSender::SendStoredPackets() {
case PacketType_RTCP:
break;
}
- if (!transport_->SendPacket(packet, cb)) {
+
+ const bool socket_blocked = !transport_->SendPacket(packet, cb);
+
+ // Save the send record.
+ send_record.last_byte_sent = transport_->GetBytesSent();
+ send_record.last_byte_sent_for_audio = GetLastByteSentForSsrc(audio_ssrc_);
+ send_history_[packet_key] = send_record;
+ send_history_buffer_[packet_key] = send_record;
+ last_byte_sent_[packet_key.second.first] = send_record.last_byte_sent;
+
+ if (socket_blocked) {
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();
+
+ // Keep ~0.5 seconds of data (1000 packets).
+ if (send_history_buffer_.size() >= kPacketHistorySize) {
+ send_history_.swap(send_history_buffer_);
+ send_history_buffer_.clear();
}
- DCHECK_LE(sent_time_buffer_.size(),
- kMaxBurstSize * kMaxDedupeWindowMs / kPacingIntervalMs);
+ DCHECK_LE(send_history_buffer_.size(), kPacketHistorySize);
state_ = State_Unblocked;
}
diff --git a/media/cast/net/pacing/paced_sender.h b/media/cast/net/pacing/paced_sender.h
index 094e5299e1..8e5a60366a 100644
--- a/media/cast/net/pacing/paced_sender.h
+++ b/media/cast/net/pacing/paced_sender.h
@@ -35,12 +35,32 @@ class LoggingImpl;
typedef std::pair<base::TimeTicks, std::pair<uint32, uint16> > PacketKey;
typedef std::vector<std::pair<PacketKey, PacketRef> > SendPacketVector;
+// Information used to deduplicate retransmission packets.
+// There are two criteria for deduplication.
+//
+// 1. Using another muxed stream.
+// Suppose there are multiple streams muxed and sent via the same
+// socket. When there is a retransmission request for packet X, we
+// will reject the retransmission if there is a packet sent from
+// another stream just before X but not acked. Typically audio stream
+// is used for this purpose. |last_byte_acked_for_audio| provides this
+// information.
+//
+// 2. Using a time interval.
+// Time between sending the same packet must be greater than
+// |resend_interval|.
+struct DedupInfo {
+ DedupInfo();
+ base::TimeDelta resend_interval;
+ int64 last_byte_acked_for_audio;
+};
+
// 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;
+ const DedupInfo& dedup_info) = 0;
virtual bool SendRtcpPacket(uint32 ssrc, PacketRef packet) = 0;
virtual void CancelSendingPacket(const PacketKey& packet_key) = 0;
@@ -75,10 +95,19 @@ class PacedSender : public PacedPacketSender,
// Because IsHigherPriority() is determined in linear time.
void RegisterPrioritySsrc(uint32 ssrc);
+ // Returns the total number of bytes sent to the socket when the specified
+ // packet was just sent.
+ // Returns 0 if the packet cannot be found or not yet sent.
+ int64 GetLastByteSentForPacket(const PacketKey& packet_key);
+
+ // Returns the total number of bytes sent to the socket when the last payload
+ // identified by SSRC is just sent.
+ int64 GetLastByteSentForSsrc(uint32 ssrc);
+
// PacedPacketSender implementation.
virtual bool SendPackets(const SendPacketVector& packets) OVERRIDE;
virtual bool ResendPackets(const SendPacketVector& packets,
- base::TimeDelta dedupe_window) OVERRIDE;
+ const DedupInfo& dedup_info) OVERRIDE;
virtual bool SendRtcpPacket(uint32 ssrc, PacketRef packet) OVERRIDE;
virtual void CancelSendingPacket(const PacketKey& packet_key) OVERRIDE;
@@ -87,6 +116,14 @@ class PacedSender : public PacedPacketSender,
void SendStoredPackets();
void LogPacketEvent(const Packet& packet, CastLoggingEvent event);
+ // Returns true if retransmission for packet indexed by |packet_key| is
+ // accepted. |dedup_info| contains information to help deduplicate
+ // retransmission. |now| is the current time to save on fetching it from the
+ // clock multiple times.
+ bool ShouldResend(const PacketKey& packet_key,
+ const DedupInfo& dedup_info,
+ const base::TimeTicks& now);
+
enum PacketType {
PacketType_RTCP,
PacketType_Resend,
@@ -133,8 +170,20 @@ class PacedSender : public PacedPacketSender,
typedef std::map<PacketKey, std::pair<PacketType, PacketRef> > PacketList;
PacketList packet_list_;
PacketList priority_packet_list_;
- std::map<PacketKey, base::TimeTicks> sent_time_;
- std::map<PacketKey, base::TimeTicks> sent_time_buffer_;
+
+ struct PacketSendRecord {
+ PacketSendRecord();
+ base::TimeTicks time; // Time when the packet was sent.
+ int64 last_byte_sent; // Number of bytes sent to network just after this
+ // packet was sent.
+ int64 last_byte_sent_for_audio; // Number of bytes sent to network from
+ // audio stream just before this packet.
+ };
+ typedef std::map<PacketKey, PacketSendRecord> PacketSendHistory;
+ PacketSendHistory send_history_;
+ PacketSendHistory send_history_buffer_;
+ // Records the last byte sent for payload with a specific SSRC.
+ std::map<uint32, int64> last_byte_sent_;
// Maximum burst size for the next three bursts.
size_t max_burst_size_;
diff --git a/media/cast/net/pacing/paced_sender_unittest.cc b/media/cast/net/pacing/paced_sender_unittest.cc
index 43e7603385..68b8c06fcc 100644
--- a/media/cast/net/pacing/paced_sender_unittest.cc
+++ b/media/cast/net/pacing/paced_sender_unittest.cc
@@ -30,16 +30,21 @@ static const uint32 kAudioSsrc = 0x5678;
class TestPacketSender : public PacketSender {
public:
- TestPacketSender() {}
+ TestPacketSender() : bytes_sent_(0) {}
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());
+ bytes_sent_ += packet->data.size();
return true;
}
+ virtual int64 GetBytesSent() OVERRIDE {
+ return bytes_sent_;
+ }
+
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);
@@ -48,6 +53,7 @@ class TestPacketSender : public PacketSender {
public:
std::list<int> expected_packet_size_;
+ int64 bytes_sent_;
DISALLOW_COPY_AND_ASSIGN(TestPacketSender);
};
@@ -132,7 +138,7 @@ TEST_F(PacedSenderTest, PassThroughRtcp) {
SendPacketVector packets = CreateSendPacketVector(kSize1, 1, true);
EXPECT_TRUE(paced_sender_->SendPackets(packets));
- EXPECT_TRUE(paced_sender_->ResendPackets(packets, base::TimeDelta()));
+ EXPECT_TRUE(paced_sender_->ResendPackets(packets, DedupInfo()));
mock_transport_.AddExpectedSize(kSize2, 1);
Packet tmp(kSize2, kValue);
@@ -205,7 +211,7 @@ TEST_F(PacedSenderTest, PaceWithNack) {
EXPECT_TRUE(paced_sender_->SendPackets(first_frame_packets));
// Add first NACK request.
- EXPECT_TRUE(paced_sender_->ResendPackets(nack_packets, base::TimeDelta()));
+ EXPECT_TRUE(paced_sender_->ResendPackets(nack_packets, DedupInfo()));
// Check that we get the first NACK burst.
mock_transport_.AddExpectedSize(kNackSize, 10);
@@ -214,7 +220,7 @@ TEST_F(PacedSenderTest, PaceWithNack) {
task_runner_->RunTasks();
// Add second NACK request.
- EXPECT_TRUE(paced_sender_->ResendPackets(nack_packets, base::TimeDelta()));
+ EXPECT_TRUE(paced_sender_->ResendPackets(nack_packets, DedupInfo()));
// Check that we get the next NACK burst.
mock_transport_.AddExpectedSize(kNackSize, 10);
@@ -388,8 +394,7 @@ TEST_F(PacedSenderTest, SendPriority) {
// Resend video packets. This is queued and will be sent
// earlier than normal video packets.
- EXPECT_TRUE(paced_sender_->ResendPackets(
- resend_packets, base::TimeDelta()));
+ EXPECT_TRUE(paced_sender_->ResendPackets(resend_packets, DedupInfo()));
// Roll the clock. Queued packets will be sent in this order:
// 1. RTCP packet x 1.
@@ -400,5 +405,62 @@ TEST_F(PacedSenderTest, SendPriority) {
EXPECT_TRUE(RunUntilEmpty(4));
}
+TEST_F(PacedSenderTest, GetLastByteSent) {
+ mock_transport_.AddExpectedSize(kSize1, 4);
+
+ SendPacketVector packets1 = CreateSendPacketVector(kSize1, 1, true);
+ SendPacketVector packets2 = CreateSendPacketVector(kSize1, 1, false);
+
+ EXPECT_TRUE(paced_sender_->SendPackets(packets1));
+ EXPECT_EQ(static_cast<int64>(kSize1),
+ paced_sender_->GetLastByteSentForPacket(packets1[0].first));
+ EXPECT_EQ(static_cast<int64>(kSize1),
+ paced_sender_->GetLastByteSentForSsrc(kAudioSsrc));
+ EXPECT_EQ(0, paced_sender_->GetLastByteSentForSsrc(kVideoSsrc));
+
+ EXPECT_TRUE(paced_sender_->SendPackets(packets2));
+ EXPECT_EQ(static_cast<int64>(2 * kSize1),
+ paced_sender_->GetLastByteSentForPacket(packets2[0].first));
+ EXPECT_EQ(static_cast<int64>(kSize1),
+ paced_sender_->GetLastByteSentForSsrc(kAudioSsrc));
+ EXPECT_EQ(static_cast<int64>(2 * kSize1),
+ paced_sender_->GetLastByteSentForSsrc(kVideoSsrc));
+
+ EXPECT_TRUE(paced_sender_->ResendPackets(packets1, DedupInfo()));
+ EXPECT_EQ(static_cast<int64>(3 * kSize1),
+ paced_sender_->GetLastByteSentForPacket(packets1[0].first));
+ EXPECT_EQ(static_cast<int64>(3 * kSize1),
+ paced_sender_->GetLastByteSentForSsrc(kAudioSsrc));
+ EXPECT_EQ(static_cast<int64>(2 * kSize1),
+ paced_sender_->GetLastByteSentForSsrc(kVideoSsrc));
+
+ EXPECT_TRUE(paced_sender_->ResendPackets(packets2, DedupInfo()));
+ EXPECT_EQ(static_cast<int64>(4 * kSize1),
+ paced_sender_->GetLastByteSentForPacket(packets2[0].first));
+ EXPECT_EQ(static_cast<int64>(3 * kSize1),
+ paced_sender_->GetLastByteSentForSsrc(kAudioSsrc));
+ EXPECT_EQ(static_cast<int64>(4 * kSize1),
+ paced_sender_->GetLastByteSentForSsrc(kVideoSsrc));
+}
+
+TEST_F(PacedSenderTest, DedupWithResendInterval) {
+ mock_transport_.AddExpectedSize(kSize1, 2);
+
+ SendPacketVector packets = CreateSendPacketVector(kSize1, 1, true);
+ EXPECT_TRUE(paced_sender_->SendPackets(packets));
+ testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
+
+ DedupInfo dedup_info;
+ dedup_info.resend_interval = base::TimeDelta::FromMilliseconds(20);
+
+ // This packet will not be sent.
+ EXPECT_TRUE(paced_sender_->ResendPackets(packets, dedup_info));
+ EXPECT_EQ(static_cast<int64>(kSize1), mock_transport_.GetBytesSent());
+
+ dedup_info.resend_interval = base::TimeDelta::FromMilliseconds(5);
+ EXPECT_TRUE(paced_sender_->ResendPackets(packets, dedup_info));
+ EXPECT_EQ(static_cast<int64>(2 * kSize1), mock_transport_.GetBytesSent());
+}
+
} // namespace cast
} // namespace media
diff --git a/media/cast/net/rtcp/mock_rtcp_receiver_feedback.cc b/media/cast/net/rtcp/mock_rtcp_receiver_feedback.cc
deleted file mode 100644
index d9818ff8f2..0000000000
--- a/media/cast/net/rtcp/mock_rtcp_receiver_feedback.cc
+++ /dev/null
@@ -1,15 +0,0 @@
-// 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/net/rtcp/mock_rtcp_receiver_feedback.h"
-
-namespace media {
-namespace cast {
-
-MockRtcpReceiverFeedback::MockRtcpReceiverFeedback() {}
-
-MockRtcpReceiverFeedback::~MockRtcpReceiverFeedback() {}
-
-} // namespace cast
-} // namespace media
diff --git a/media/cast/net/rtcp/mock_rtcp_receiver_feedback.h b/media/cast/net/rtcp/mock_rtcp_receiver_feedback.h
deleted file mode 100644
index ae6b96e56d..0000000000
--- a/media/cast/net/rtcp/mock_rtcp_receiver_feedback.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// 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_MOCK_RTCP_RECEIVER_FEEDBACK_H_
-#define MEDIA_CAST_RTCP_MOCK_RTCP_RECEIVER_FEEDBACK_H_
-
-#include <vector>
-
-#include "media/cast/net/cast_transport_defines.h"
-#include "media/cast/net/rtcp/rtcp_defines.h"
-#include "media/cast/net/rtcp/rtcp_receiver.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace media {
-namespace cast {
-
-// TODO(hclam): Should be renamed to MockRtcpMessageHandler.
-class MockRtcpReceiverFeedback : public RtcpMessageHandler {
- public:
- MockRtcpReceiverFeedback();
- virtual ~MockRtcpReceiverFeedback();
-
- MOCK_METHOD1(OnReceivedSenderReport,
- void(const RtcpSenderInfo& remote_sender_info));
-
- MOCK_METHOD1(OnReceiverReferenceTimeReport,
- void(const RtcpReceiverReferenceTimeReport& remote_time_report));
-
- MOCK_METHOD0(OnReceivedSendReportRequest, void());
-
- MOCK_METHOD1(OnReceivedReceiverLog,
- void(const RtcpReceiverLogMessage& receiver_log));
-
- MOCK_METHOD2(OnReceivedDelaySinceLastReport,
- void(uint32 last_report, uint32 delay_since_last_report));
-
- MOCK_METHOD1(OnReceivedCastFeedback,
- void(const RtcpCastMessage& cast_message));
-};
-
-} // namespace cast
-} // namespace media
-
-#endif // MEDIA_CAST_RTCP_MOCK_RTCP_RECEIVER_FEEDBACK_H_
diff --git a/media/cast/net/rtcp/mock_rtcp_sender_feedback.cc b/media/cast/net/rtcp/mock_rtcp_sender_feedback.cc
deleted file mode 100644
index 2c51c7448d..0000000000
--- a/media/cast/net/rtcp/mock_rtcp_sender_feedback.cc
+++ /dev/null
@@ -1,15 +0,0 @@
-// 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/net/rtcp/mock_rtcp_sender_feedback.h"
-
-namespace media {
-namespace cast {
-
-MockRtcpSenderFeedback::MockRtcpSenderFeedback() {}
-
-MockRtcpSenderFeedback::~MockRtcpSenderFeedback() {}
-
-} // namespace cast
-} // namespace media
diff --git a/media/cast/net/rtcp/mock_rtcp_sender_feedback.h b/media/cast/net/rtcp/mock_rtcp_sender_feedback.h
deleted file mode 100644
index a6af0aaa3e..0000000000
--- a/media/cast/net/rtcp/mock_rtcp_sender_feedback.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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_MOCK_RTCP_SENDER_FEEDBACK_H_
-#define MEDIA_CAST_RTCP_MOCK_RTCP_SENDER_FEEDBACK_H_
-
-#include <vector>
-
-#include "media/cast/net/rtcp/rtcp_receiver.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace media {
-namespace cast {
-
-class MockRtcpSenderFeedback : public RtcpSenderFeedback {
- public:
- MockRtcpSenderFeedback();
- virtual ~MockRtcpSenderFeedback();
-
- MOCK_METHOD1(OnReceivedCastFeedback,
- void(const RtcpCastMessage& cast_feedback));
-};
-
-} // namespace cast
-} // namespace media
-
-#endif // MEDIA_CAST_RTCP_MOCK_RTCP_SENDER_FEEDBACK_H_
diff --git a/media/cast/net/rtcp/rtcp.cc b/media/cast/net/rtcp/rtcp.cc
index ac413d39c9..6b7718dde9 100644
--- a/media/cast/net/rtcp/rtcp.cc
+++ b/media/cast/net/rtcp/rtcp.cc
@@ -9,7 +9,6 @@
#include "media/cast/cast_environment.h"
#include "media/cast/net/cast_transport_defines.h"
#include "media/cast/net/rtcp/rtcp_defines.h"
-#include "media/cast/net/rtcp/rtcp_receiver.h"
#include "media/cast/net/rtcp/rtcp_sender.h"
#include "media/cast/net/rtcp/rtcp_utility.h"
@@ -19,48 +18,40 @@ namespace media {
namespace cast {
static const int32 kMaxRttMs = 10000; // 10 seconds.
+// Reject packets that are older than 0.5 seconds older than
+// the newest packet we've seen so far. This protect internal
+// states from crazy routers. (Based on RRTR)
+static const int32 kOutOfOrderMaxAgeMs = 500;
+
+namespace {
+
+// 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()));
+}
-class Rtcp::RtcpMessageHandlerImpl : public RtcpMessageHandler {
- public:
- explicit RtcpMessageHandlerImpl(Rtcp* rtcp)
- : rtcp_(rtcp) {}
-
- virtual void OnReceivedSenderReport(
- const 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) {
- rtcp_->OnReceivedLipSyncInfo(remote_sender_info.rtp_timestamp,
- remote_sender_info.ntp_seconds,
- remote_sender_info.ntp_fraction);
- }
- }
-
- virtual void OnReceiverReferenceTimeReport(
- const RtcpReceiverReferenceTimeReport& remote_time_report) OVERRIDE {
- rtcp_->OnReceivedNtp(remote_time_report.ntp_seconds,
- remote_time_report.ntp_fraction);
- }
-
- virtual void OnReceivedReceiverLog(const RtcpReceiverLogMessage& receiver_log)
- OVERRIDE {
- rtcp_->OnReceivedReceiverLog(receiver_log);
- }
+} // namespace
- virtual void OnReceivedDelaySinceLastReport(
- uint32 last_report,
- uint32 delay_since_last_report) OVERRIDE {
- rtcp_->OnReceivedDelaySinceLastReport(last_report, delay_since_last_report);
- }
-
- virtual void OnReceivedCastFeedback(
- const RtcpCastMessage& cast_message) OVERRIDE {
- rtcp_->OnReceivedCastFeedback(cast_message);
- }
-
- private:
- Rtcp* rtcp_;
-};
Rtcp::Rtcp(const RtcpCastMessageCallback& cast_callback,
const RtcpRttCallback& rtt_callback,
@@ -76,75 +67,146 @@ Rtcp::Rtcp(const RtcpCastMessageCallback& cast_callback,
rtcp_sender_(new RtcpSender(packet_sender, local_ssrc)),
local_ssrc_(local_ssrc),
remote_ssrc_(remote_ssrc),
- handler_(new RtcpMessageHandlerImpl(this)),
- rtcp_receiver_(new RtcpReceiver(handler_.get(), local_ssrc)),
last_report_truncated_ntp_(0),
local_clock_ahead_by_(ClockDriftSmoother::GetDefaultTimeConstant()),
lip_sync_rtp_timestamp_(0),
lip_sync_ntp_timestamp_(0),
min_rtt_(TimeDelta::FromMilliseconds(kMaxRttMs)),
number_of_rtt_in_avg_(0) {
- rtcp_receiver_->SetRemoteSSRC(remote_ssrc);
-
- // This value is the same in FrameReceiver.
- rtcp_receiver_->SetCastReceiverEventHistorySize(
- kReceiverRtcpEventHistorySize);
}
Rtcp::~Rtcp() {}
+bool Rtcp::IsRtcpPacket(const uint8* packet, size_t length) {
+ if (length < kMinLengthOfRtcp) {
+ LOG(ERROR) << "Invalid RTCP packet received.";
+ return false;
+ }
+
+ uint8 packet_type = packet[1];
+ return packet_type >= kPacketTypeLow && packet_type <= kPacketTypeHigh;
+}
+
+uint32 Rtcp::GetSsrcOfSender(const uint8* rtcp_buffer, size_t length) {
+ if (length < kMinLengthOfRtcp)
+ return 0;
+ uint32 ssrc_of_sender;
+ 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;
+}
+
bool Rtcp::IncomingRtcpPacket(const uint8* data, size_t length) {
// Check if this is a valid RTCP packet.
- if (!RtcpReceiver::IsRtcpPacket(data, length)) {
+ if (!IsRtcpPacket(data, length)) {
VLOG(1) << "Rtcp@" << this << "::IncomingRtcpPacket() -- "
<< "Received an invalid (non-RTCP?) packet.";
return false;
}
// Check if this packet is to us.
- uint32 ssrc_of_sender = RtcpReceiver::GetSsrcOfSender(data, length);
+ uint32 ssrc_of_sender = GetSsrcOfSender(data, length);
if (ssrc_of_sender != remote_ssrc_) {
return false;
}
// Parse this packet.
- RtcpParser rtcp_parser(data, length);
- if (!rtcp_parser.IsValid()) {
- // Silently ignore packet.
- VLOG(1) << "Received invalid RTCP packet";
- return false;
+ RtcpParser parser(local_ssrc_, remote_ssrc_);
+ base::BigEndianReader reader(reinterpret_cast<const char*>(data), length);
+ if (parser.Parse(&reader)) {
+ if (parser.has_receiver_reference_time_report()) {
+ base::TimeTicks t = ConvertNtpToTimeTicks(
+ parser.receiver_reference_time_report().ntp_seconds,
+ parser.receiver_reference_time_report().ntp_fraction);
+ if (t > largest_seen_timestamp_) {
+ largest_seen_timestamp_ = t;
+ } else if ((largest_seen_timestamp_ - t).InMilliseconds() >
+ kOutOfOrderMaxAgeMs) {
+ // Reject packet, it is too old.
+ VLOG(1) << "Rejecting RTCP packet as it is too old ("
+ << (largest_seen_timestamp_ - t).InMilliseconds()
+ << " ms)";
+ return true;
+ }
+
+ OnReceivedNtp(parser.receiver_reference_time_report().ntp_seconds,
+ parser.receiver_reference_time_report().ntp_fraction);
+ }
+ if (parser.has_sender_report()) {
+ OnReceivedNtp(parser.sender_report().ntp_seconds,
+ parser.sender_report().ntp_fraction);
+ OnReceivedLipSyncInfo(parser.sender_report().rtp_timestamp,
+ parser.sender_report().ntp_seconds,
+ parser.sender_report().ntp_fraction);
+ }
+ if (parser.has_receiver_log()) {
+ if (DedupeReceiverLog(parser.mutable_receiver_log())) {
+ OnReceivedReceiverLog(parser.receiver_log());
+ }
+ }
+ if (parser.has_last_report()) {
+ OnReceivedDelaySinceLastReport(parser.last_report(),
+ parser.delay_since_last_report());
+ }
+ if (parser.has_cast_message()) {
+ parser.mutable_cast_message()->ack_frame_id =
+ ack_frame_id_wrap_helper_.MapTo32bitsFrameId(
+ parser.mutable_cast_message()->ack_frame_id);
+ OnReceivedCastFeedback(parser.cast_message());
+ }
}
- rtcp_receiver_->IncomingRtcpPacket(&rtcp_parser);
return true;
}
+bool Rtcp::DedupeReceiverLog(RtcpReceiverLogMessage* receiver_log) {
+ RtcpReceiverLogMessage::iterator i = receiver_log->begin();
+ while (i != receiver_log->end()) {
+ RtcpReceiverEventLogMessages* messages = &i->event_log_messages_;
+ RtcpReceiverEventLogMessages::iterator j = messages->begin();
+ while (j != messages->end()) {
+ ReceiverEventKey key = GetReceiverEventKey(i->rtp_timestamp_,
+ j->event_timestamp,
+ j->type,
+ j->packet_id);
+ RtcpReceiverEventLogMessages::iterator tmp = j;
+ ++j;
+ if (receiver_event_key_set_.insert(key).second) {
+ receiver_event_key_queue_.push(key);
+ if (receiver_event_key_queue_.size() > kReceiverRtcpEventHistorySize) {
+ receiver_event_key_set_.erase(receiver_event_key_queue_.front());
+ receiver_event_key_queue_.pop();
+ }
+ } else {
+ messages->erase(tmp);
+ }
+ }
+
+ RtcpReceiverLogMessage::iterator tmp = i;
+ ++i;
+ if (messages->empty()) {
+ receiver_log->erase(tmp);
+ }
+ }
+ return !receiver_log->empty();
+}
+
void Rtcp::SendRtcpFromRtpReceiver(
const RtcpCastMessage* cast_message,
base::TimeDelta target_delay,
const ReceiverRtcpEventSubscriber::RtcpEventMultiMap* rtcp_events,
RtpReceiverStatistics* rtp_receiver_statistics) {
- uint32 packet_type_flags = 0;
-
base::TimeTicks now = clock_->NowTicks();
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 |= kRtcpRrtr;
ConvertTimeTicksToNtp(now, &rrtr.ntp_seconds, &rrtr.ntp_fraction);
SaveLastSentNtpTime(now, rrtr.ntp_seconds, rrtr.ntp_fraction);
- if (cast_message) {
- packet_type_flags |= kRtcpCast;
- }
- if (rtcp_events) {
- packet_type_flags |= kRtcpReceiverLog;
- }
- // If RTCP is in compound mode then we always send a RR.
if (rtp_receiver_statistics) {
- packet_type_flags |= kRtcpRr;
-
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) {
@@ -166,19 +228,18 @@ void Rtcp::SendRtcpFromRtpReceiver(
report_block.delay_since_last_sr = 0;
}
}
- rtcp_sender_->SendRtcpFromRtpReceiver(packet_type_flags,
- &report_block,
- &rrtr,
- cast_message,
- rtcp_events,
- target_delay);
+ rtcp_sender_->SendRtcpFromRtpReceiver(
+ rtp_receiver_statistics ? &report_block : NULL,
+ &rrtr,
+ cast_message,
+ rtcp_events,
+ target_delay);
}
void Rtcp::SendRtcpFromRtpSender(base::TimeTicks current_time,
uint32 current_time_as_rtp_timestamp,
uint32 send_packet_count,
size_t send_octet_count) {
- uint32 packet_type_flags = kRtcpSr;
uint32 current_ntp_seconds = 0;
uint32 current_ntp_fractions = 0;
ConvertTimeTicksToNtp(current_time, &current_ntp_seconds,
@@ -186,26 +247,6 @@ void Rtcp::SendRtcpFromRtpSender(base::TimeTicks current_time,
SaveLastSentNtpTime(current_time, current_ntp_seconds,
current_ntp_fractions);
- RtcpDlrrReportBlock dlrr;
- if (!time_last_report_received_.is_null()) {
- packet_type_flags |= kRtcpDlrr;
- dlrr.last_rr = last_report_truncated_ntp_;
- uint32 delay_seconds = 0;
- uint32 delay_fraction = 0;
- base::TimeDelta delta = current_time - time_last_report_received_;
- // TODO(hclam): DLRR is not used by any receiver. Consider removing
- // it. There is one race condition in the computation of the time for
- // DLRR: current time is submitted to this method while
- // |time_last_report_received_| is updated just before that. This can
- // happen if current time is not submitted synchronously.
- if (delta < base::TimeDelta())
- delta = base::TimeDelta();
- ConvertTimeToFractions(delta.InMicroseconds(), &delay_seconds,
- &delay_fraction);
-
- dlrr.delay_since_last_rr = ConvertToNtpDiff(delay_seconds, delay_fraction);
- }
-
RtcpSenderInfo sender_info;
sender_info.ntp_seconds = current_ntp_seconds;
sender_info.ntp_fraction = current_ntp_fractions;
@@ -213,7 +254,7 @@ void Rtcp::SendRtcpFromRtpSender(base::TimeTicks current_time,
sender_info.send_packet_count = send_packet_count;
sender_info.send_octet_count = send_octet_count;
- rtcp_sender_->SendRtcpFromRtpSender(packet_type_flags, sender_info, dlrr);
+ rtcp_sender_->SendRtcpFromRtpSender(sender_info);
}
void Rtcp::OnReceivedNtp(uint32 ntp_seconds, uint32 ntp_fraction) {
diff --git a/media/cast/net/rtcp/rtcp.h b/media/cast/net/rtcp/rtcp.h
index 439b963502..a1fdc9e489 100644
--- a/media/cast/net/rtcp/rtcp.h
+++ b/media/cast/net/rtcp/rtcp.h
@@ -107,6 +107,10 @@ class Rtcp {
void OnReceivedReceiverLog(const RtcpReceiverLogMessage& receiver_log);
+ static bool IsRtcpPacket(const uint8* packet, size_t length);
+ static uint32 GetSsrcOfSender(const uint8* rtcp_buffer, size_t length);
+ const base::TimeDelta& rtt() const { return rtt_; }
+
protected:
void OnReceivedNtp(uint32 ntp_seconds, uint32 ntp_fraction);
void OnReceivedLipSyncInfo(uint32 rtp_timestamp,
@@ -114,8 +118,6 @@ class Rtcp {
uint32 ntp_fraction);
private:
- class RtcpMessageHandlerImpl;
-
void OnReceivedDelaySinceLastReport(uint32 last_report,
uint32 delay_since_last_report);
@@ -128,6 +130,10 @@ class Rtcp {
uint32 last_ntp_seconds,
uint32 last_ntp_fraction);
+ // Remove duplicate events in |receiver_log|.
+ // Returns true if any events remain.
+ bool DedupeReceiverLog(RtcpReceiverLogMessage* receiver_log);
+
const RtcpCastMessageCallback cast_callback_;
const RtcpRttCallback rtt_callback_;
const RtcpLogMessageCallback log_callback_;
@@ -135,8 +141,6 @@ class Rtcp {
const scoped_ptr<RtcpSender> rtcp_sender_;
const uint32 local_ssrc_;
const uint32 remote_ssrc_;
- const scoped_ptr<RtcpMessageHandlerImpl> handler_;
- const scoped_ptr<RtcpReceiver> rtcp_receiver_;
RtcpSendTimeMap last_reports_sent_map_;
RtcpSendTimeQueue last_reports_sent_queue_;
@@ -167,6 +171,16 @@ class Rtcp {
int number_of_rtt_in_avg_;
base::TimeDelta avg_rtt_;
+ base::TimeTicks largest_seen_timestamp_;
+
+ // For extending received ACK frame IDs from 8-bit to 32-bit.
+ FrameIdWrapHelper ack_frame_id_wrap_helper_;
+
+ // Maintains a history of receiver events.
+ 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(Rtcp);
};
diff --git a/media/cast/net/rtcp/rtcp_builder.cc b/media/cast/net/rtcp/rtcp_builder.cc
deleted file mode 100644
index f2626d3582..0000000000
--- a/media/cast/net/rtcp/rtcp_builder.cc
+++ /dev/null
@@ -1,123 +0,0 @@
-// 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/net/rtcp/rtcp_builder.h"
-
-#include <algorithm>
-#include <string>
-#include <vector>
-
-#include "base/big_endian.h"
-#include "base/logging.h"
-#include "media/cast/net/cast_transport_defines.h"
-#include "media/cast/net/pacing/paced_sender.h"
-
-namespace media {
-namespace cast {
-
-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) {
- if (packet_type_flags & kRtcpRr ||
- packet_type_flags & kRtcpRrtr ||
- packet_type_flags & kRtcpCast ||
- packet_type_flags & kRtcpReceiverLog ||
- packet_type_flags & kRtcpNack) {
- NOTREACHED() << "Invalid argument";
- }
- ssrc_ = sending_ssrc;
- 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 & 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;
-}
-
-/*
- 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 cast
-} // namespace media
diff --git a/media/cast/net/rtcp/rtcp_builder.h b/media/cast/net/rtcp/rtcp_builder.h
deleted file mode 100644
index b07ace1fe3..0000000000
--- a/media/cast/net/rtcp/rtcp_builder.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// 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_NET_RTCP_RTCP_BUILDER_H_
-#define MEDIA_CAST_NET_RTCP_RTCP_BUILDER_H_
-
-#include <list>
-#include <string>
-#include <vector>
-
-#include "media/cast/net/cast_transport_defines.h"
-#include "media/cast/net/pacing/paced_sender.h"
-
-namespace media {
-namespace cast {
-
-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);
-
- private:
- bool BuildSR(const RtcpSenderInfo& sender_info, Packet* packet) const;
- bool BuildDlrrRb(const RtcpDlrrReportBlock& dlrr,
- Packet* packet) const;
-
- PacedSender* const transport_; // Not owned by this class.
- uint32 ssrc_;
-
- DISALLOW_COPY_AND_ASSIGN(RtcpBuilder);
-};
-
-} // namespace cast
-} // namespace media
-
-#endif // MEDIA_CAST_NET_RTCP_RTCP_BUILDER_H_
diff --git a/media/cast/net/rtcp/rtcp_builder_unittest.cc b/media/cast/net/rtcp/rtcp_builder_unittest.cc
deleted file mode 100644
index 7e1fcd457f..0000000000
--- a/media/cast/net/rtcp/rtcp_builder_unittest.cc
+++ /dev/null
@@ -1,159 +0,0 @@
-// 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/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/paced_sender.h"
-#include "media/cast/net/rtcp/rtcp_builder.h"
-#include "media/cast/net/rtcp/rtcp_utility.h"
-#include "media/cast/net/rtcp/test_rtcp_packet_builder.h"
-#include "media/cast/test/fake_single_thread_task_runner.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace media {
-namespace cast {
-
-namespace {
-static const uint32 kSendingSsrc = 0x12345678;
-} // 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)) {
- }
-
- 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.
- TestRtcpPacketBuilder p;
- p.AddSr(kSendingSsrc, 0);
- test_transport_.SetExpectedRtcpPacket(p.Packet(), p.Length());
-
- rtcp_builder_->SendRtcpFromRtpSender(RtcpBuilder::kRtcpSr,
- &sender_info,
- NULL,
- NULL,
- kSendingSsrc);
-
- 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 + dlrr.
- TestRtcpPacketBuilder p1;
- p1.AddSr(kSendingSsrc, 0);
- p1.AddSdesCname(kSendingSsrc);
- 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);
-
- 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 + + dlrr + sender log.
- TestRtcpPacketBuilder p;
- p.AddSr(kSendingSsrc, 0);
- p.AddSdesCname(kSendingSsrc);
- 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);
-
- EXPECT_EQ(1, test_transport_.packet_count());
-}
-
-} // namespace cast
-} // namespace media
diff --git a/media/cast/net/rtcp/rtcp_receiver.cc b/media/cast/net/rtcp/rtcp_receiver.cc
deleted file mode 100644
index 4ba9986a17..0000000000
--- a/media/cast/net/rtcp/rtcp_receiver.cc
+++ /dev/null
@@ -1,414 +0,0 @@
-// 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/net/rtcp/rtcp_receiver.h"
-
-#include "base/big_endian.h"
-#include "base/logging.h"
-#include "media/cast/net/cast_transport_defines.h"
-#include "media/cast/net/rtcp/rtcp_utility.h"
-
-namespace {
-
-// 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
-
-namespace media {
-namespace cast {
-
-RtcpReceiver::RtcpReceiver(RtcpMessageHandler* handler,
- uint32 local_ssrc)
- : ssrc_(local_ssrc),
- remote_ssrc_(0),
- handler_(handler),
- receiver_event_history_size_(0) {
- DCHECK(handler_);
-}
-
-RtcpReceiver::~RtcpReceiver() {}
-
-// static
-bool RtcpReceiver::IsRtcpPacket(const uint8* packet, size_t length) {
- if (length < kMinLengthOfRtcp) {
- LOG(ERROR) << "Invalid RTCP packet received.";
- return false;
- }
-
- uint8 packet_type = packet[1];
- if (packet_type >= kPacketTypeLow &&
- packet_type <= kPacketTypeHigh) {
- return true;
- }
- return false;
-}
-
-// static
-uint32 RtcpReceiver::GetSsrcOfSender(const uint8* rtcp_buffer, size_t length) {
- if (length < kMinLengthOfRtcp)
- return 0;
- uint32 ssrc_of_sender;
- 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;
-}
-
-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) {
- RtcpFieldTypes field_type = rtcp_parser->Begin();
- while (field_type != kRtcpNotValidCode) {
- // Each "case" is responsible for iterate the parser to the next top
- // level packet.
- switch (field_type) {
- case kRtcpSrCode:
- HandleSenderReport(rtcp_parser);
- break;
- case kRtcpRrCode:
- HandleReceiverReport(rtcp_parser);
- break;
- case kRtcpXrCode:
- HandleXr(rtcp_parser);
- break;
- case kRtcpPayloadSpecificAppCode:
- HandlePayloadSpecificApp(rtcp_parser);
- break;
- case kRtcpApplicationSpecificCastReceiverLogCode:
- HandleApplicationSpecificCastReceiverLog(rtcp_parser);
- break;
- case kRtcpPayloadSpecificCastCode:
- case kRtcpPayloadSpecificCastNackItemCode:
- case kRtcpApplicationSpecificCastReceiverLogFrameCode:
- case kRtcpApplicationSpecificCastReceiverLogEventCode:
- case kRtcpNotValidCode:
- case kRtcpReportBlockItemCode:
- case kRtcpXrRrtrCode:
- case kRtcpXrDlrrCode:
- case kRtcpXrUnknownItemCode:
- rtcp_parser->Iterate();
- NOTREACHED() << "Invalid state";
- break;
- }
- field_type = rtcp_parser->FieldType();
- }
-}
-
-void RtcpReceiver::HandleSenderReport(RtcpParser* rtcp_parser) {
- RtcpFieldTypes rtcp_field_type = rtcp_parser->FieldType();
- const RtcpField& rtcp_field = rtcp_parser->Field();
-
- DCHECK(rtcp_field_type == kRtcpSrCode) << "Invalid state";
-
- // Synchronization source identifier for the originator of this SR packet.
- uint32 remote_ssrc = rtcp_field.sender_report.sender_ssrc;
-
- VLOG(2) << "Cast RTCP received SR from SSRC " << remote_ssrc;
-
- if (remote_ssrc_ == remote_ssrc) {
- 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.send_packet_count =
- rtcp_field.sender_report.sender_packet_count;
- remote_sender_info.send_octet_count =
- rtcp_field.sender_report.sender_octet_count;
- handler_->OnReceivedSenderReport(remote_sender_info);
- }
- rtcp_field_type = rtcp_parser->Iterate();
- while (rtcp_field_type == kRtcpReportBlockItemCode) {
- HandleReportBlock(&rtcp_field, remote_ssrc);
- rtcp_field_type = rtcp_parser->Iterate();
- }
-}
-
-void RtcpReceiver::HandleReceiverReport(RtcpParser* rtcp_parser) {
- RtcpFieldTypes rtcp_field_type = rtcp_parser->FieldType();
- const RtcpField& rtcp_field = rtcp_parser->Field();
-
- DCHECK(rtcp_field_type == kRtcpRrCode) << "Invalid state";
-
- uint32 remote_ssrc = rtcp_field.receiver_report.sender_ssrc;
-
- VLOG(2) << "Cast RTCP received RR from SSRC " << remote_ssrc;
-
- rtcp_field_type = rtcp_parser->Iterate();
- while (rtcp_field_type == kRtcpReportBlockItemCode) {
- HandleReportBlock(&rtcp_field, remote_ssrc);
- rtcp_field_type = rtcp_parser->Iterate();
- }
-}
-
-void RtcpReceiver::HandleReportBlock(const RtcpField* rtcp_field,
- uint32 remote_ssrc) {
- // This will be called once per report block in the Rtcp packet.
- // We filter out all report blocks that are not for us.
- // Each packet has max 31 RR blocks.
- //
- // We can calculate RTT if we send a send report and get a report block back.
-
- // |rtcp_field.ReportBlockItem.ssrc| is the ssrc identifier of the source to
- // which the information in this reception report block pertains.
-
- const RtcpFieldReportBlockItem& rb = rtcp_field->report_block_item;
-
- // Filter out all report blocks that are not for us.
- if (rb.ssrc != ssrc_) {
- // This block is not for us ignore it.
- return;
- }
- VLOG(2) << "Cast RTCP received RB from SSRC " << remote_ssrc;
-
- RtcpReportBlock report_block;
- report_block.remote_ssrc = remote_ssrc;
- report_block.media_ssrc = rb.ssrc;
- report_block.fraction_lost = rb.fraction_lost;
- report_block.cumulative_lost = rb.cumulative_number_of_packets_lost;
- report_block.extended_high_sequence_number =
- rb.extended_highest_sequence_number;
- report_block.jitter = rb.jitter;
- report_block.last_sr = rb.last_sender_report;
- report_block.delay_since_last_sr = rb.delay_last_sender_report;
- handler_->OnReceivedDelaySinceLastReport(
- rb.last_sender_report, rb.delay_last_sender_report);
-}
-
-void RtcpReceiver::HandleXr(RtcpParser* rtcp_parser) {
- RtcpFieldTypes rtcp_field_type = rtcp_parser->FieldType();
- const RtcpField& rtcp_field = rtcp_parser->Field();
-
- DCHECK(rtcp_field_type == kRtcpXrCode) << "Invalid state";
-
- uint32 remote_ssrc = rtcp_field.extended_report.sender_ssrc;
- rtcp_field_type = rtcp_parser->Iterate();
-
- while (rtcp_field_type == kRtcpXrDlrrCode ||
- rtcp_field_type == kRtcpXrRrtrCode ||
- rtcp_field_type == kRtcpXrUnknownItemCode) {
- if (rtcp_field_type == kRtcpXrRrtrCode) {
- HandleRrtr(rtcp_parser, remote_ssrc);
- } else if (rtcp_field_type == kRtcpXrDlrrCode) {
- HandleDlrr(rtcp_parser);
- }
- rtcp_field_type = rtcp_parser->Iterate();
- }
-}
-
-void RtcpReceiver::HandleRrtr(RtcpParser* rtcp_parser, uint32 remote_ssrc) {
- if (remote_ssrc_ != remote_ssrc) {
- // Not to us.
- return;
- }
- const RtcpField& rtcp_field = rtcp_parser->Field();
- RtcpReceiverReferenceTimeReport remote_time_report;
- remote_time_report.remote_ssrc = remote_ssrc;
- remote_time_report.ntp_seconds = rtcp_field.rrtr.ntp_most_significant;
- remote_time_report.ntp_fraction = rtcp_field.rrtr.ntp_least_significant;
- handler_->OnReceiverReferenceTimeReport(remote_time_report);
-}
-
-void RtcpReceiver::HandleDlrr(RtcpParser* rtcp_parser) {
- const RtcpField& rtcp_field = rtcp_parser->Field();
- if (remote_ssrc_ != rtcp_field.dlrr.receivers_ssrc) {
- // Not to us.
- return;
- }
- handler_->OnReceivedDelaySinceLastReport(
- rtcp_field.dlrr.last_receiver_report,
- rtcp_field.dlrr.delay_last_receiver_report);
-}
-
-void RtcpReceiver::HandlePayloadSpecificApp(RtcpParser* rtcp_parser) {
- const RtcpField& rtcp_field = rtcp_parser->Field();
- uint32 remote_ssrc = rtcp_field.application_specific.sender_ssrc;
- if (remote_ssrc_ != remote_ssrc) {
- // Message not to us. Iterate until we have passed this message.
- RtcpFieldTypes field_type;
- do {
- field_type = rtcp_parser->Iterate();
- } while (field_type == kRtcpPayloadSpecificCastCode ||
- field_type == kRtcpPayloadSpecificCastNackItemCode);
- return;
- }
-
- RtcpFieldTypes packet_type = rtcp_parser->Iterate();
- switch (packet_type) {
- case kRtcpPayloadSpecificCastCode:
- packet_type = rtcp_parser->Iterate();
- if (packet_type == kRtcpPayloadSpecificCastCode) {
- HandlePayloadSpecificCastItem(rtcp_parser);
- }
- break;
- default:
- return;
- }
-}
-
-void RtcpReceiver::HandleApplicationSpecificCastReceiverLog(
- RtcpParser* rtcp_parser) {
- const RtcpField& rtcp_field = rtcp_parser->Field();
-
- uint32 remote_ssrc = rtcp_field.cast_receiver_log.sender_ssrc;
- if (remote_ssrc_ != remote_ssrc) {
- // Message not to us. Iterate until we have passed this message.
- RtcpFieldTypes field_type;
- do {
- field_type = rtcp_parser->Iterate();
- } while (field_type == kRtcpApplicationSpecificCastReceiverLogFrameCode ||
- field_type == kRtcpApplicationSpecificCastReceiverLogEventCode);
- return;
- }
- RtcpReceiverLogMessage receiver_log;
- RtcpFieldTypes field_type = rtcp_parser->Iterate();
- while (field_type == kRtcpApplicationSpecificCastReceiverLogFrameCode) {
- RtcpReceiverFrameLogMessage frame_log(
- rtcp_field.cast_receiver_log.rtp_timestamp);
-
- field_type = rtcp_parser->Iterate();
- while (field_type == kRtcpApplicationSpecificCastReceiverLogEventCode) {
- HandleApplicationSpecificCastReceiverEventLog(
- rtcp_field.cast_receiver_log.rtp_timestamp,
- rtcp_parser,
- &frame_log.event_log_messages_);
- field_type = rtcp_parser->Iterate();
- }
-
- if (!frame_log.event_log_messages_.empty())
- receiver_log.push_back(frame_log);
- }
-
- if (!receiver_log.empty())
- handler_->OnReceivedReceiverLog(receiver_log);
-}
-
-void RtcpReceiver::HandleApplicationSpecificCastReceiverEventLog(
- uint32 frame_rtp_timestamp,
- RtcpParser* rtcp_parser,
- RtcpReceiverEventLogMessages* event_log_messages) {
- const RtcpField& rtcp_field = rtcp_parser->Field();
-
- 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);
-
- // 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);
- }
- }
-
- 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) {
- const RtcpField& rtcp_field = rtcp_parser->Field();
- 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) {
- const RtcpField& rtcp_field = rtcp_parser->Field();
- HandlePayloadSpecificCastNackItem(
- &rtcp_field, &cast_message.missing_frames_and_packets);
- packet_type = rtcp_parser->Iterate();
- }
- handler_->OnReceivedCastFeedback(cast_message);
-}
-
-void RtcpReceiver::HandlePayloadSpecificCastNackItem(
- const RtcpField* rtcp_field,
- MissingFramesAndPacketsMap* missing_frames_and_packets) {
-
- MissingFramesAndPacketsMap::iterator frame_it =
- missing_frames_and_packets->find(rtcp_field->cast_nack_item.frame_id);
-
- if (frame_it == missing_frames_and_packets->end()) {
- // First missing packet in a frame.
- PacketIdSet empty_set;
- std::pair<MissingFramesAndPacketsMap::iterator, bool> ret =
- missing_frames_and_packets->insert(std::pair<uint8, PacketIdSet>(
- rtcp_field->cast_nack_item.frame_id, empty_set));
- frame_it = ret.first;
- DCHECK(frame_it != missing_frames_and_packets->end()) << "Invalid state";
- }
- 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;
- }
- uint8 bitmask = rtcp_field->cast_nack_item.bitmask;
-
- if (bitmask) {
- for (int i = 1; i <= 8; ++i) {
- if (bitmask & 1) {
- frame_it->second.insert(packet_id + i);
- }
- bitmask = bitmask >> 1;
- }
- }
-}
-
-} // namespace cast
-} // namespace media
diff --git a/media/cast/net/rtcp/rtcp_receiver.h b/media/cast/net/rtcp/rtcp_receiver.h
deleted file mode 100644
index e7d432e390..0000000000
--- a/media/cast/net/rtcp/rtcp_receiver.h
+++ /dev/null
@@ -1,101 +0,0 @@
-// 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_RTCP_RECEIVER_H_
-#define MEDIA_CAST_RTCP_RTCP_RECEIVER_H_
-
-#include <queue>
-
-#include "base/containers/hash_tables.h"
-#include "media/cast/net/cast_transport_defines.h"
-#include "media/cast/net/rtcp/rtcp.h"
-#include "media/cast/net/rtcp/rtcp_defines.h"
-#include "media/cast/net/rtcp/rtcp_utility.h"
-
-namespace media {
-namespace cast {
-
-// Interface for receiving RTCP messages.
-class RtcpMessageHandler {
- public:
- virtual void OnReceivedSenderReport(
- const RtcpSenderInfo& remote_sender_info) = 0;
-
- virtual void OnReceiverReferenceTimeReport(
- const RtcpReceiverReferenceTimeReport& remote_time_report) = 0;
-
- virtual void OnReceivedReceiverLog(
- const RtcpReceiverLogMessage& receiver_log) = 0;
-
- virtual void OnReceivedDelaySinceLastReport(
- uint32 last_report,
- uint32 delay_since_last_report) = 0;
-
- virtual void OnReceivedCastFeedback(
- const RtcpCastMessage& cast_message) = 0;
-
- virtual ~RtcpMessageHandler() {}
-};
-
-class RtcpReceiver {
- public:
- RtcpReceiver(RtcpMessageHandler* handler, uint32 local_ssrc);
- virtual ~RtcpReceiver();
-
- static bool IsRtcpPacket(const uint8* rtcp_buffer, size_t length);
-
- static uint32 GetSsrcOfSender(const uint8* rtcp_buffer, size_t length);
-
- 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:
- void HandleSenderReport(RtcpParser* rtcp_parser);
-
- void HandleReceiverReport(RtcpParser* rtcp_parser);
-
- void HandleReportBlock(const RtcpField* rtcp_field, uint32 remote_ssrc);
-
- void HandleXr(RtcpParser* rtcp_parser);
- void HandleRrtr(RtcpParser* rtcp_parser, uint32 remote_ssrc);
- void HandleDlrr(RtcpParser* rtcp_parser);
-
- void HandlePayloadSpecificApp(RtcpParser* rtcp_parser);
- void HandlePayloadSpecificCastItem(RtcpParser* rtcp_parser);
- void HandlePayloadSpecificCastNackItem(
- const RtcpField* rtcp_field,
- MissingFramesAndPacketsMap* missing_frames_and_packets);
-
- void HandleApplicationSpecificCastReceiverLog(RtcpParser* rtcp_parser);
- void HandleApplicationSpecificCastReceiverEventLog(
- uint32 frame_rtp_timestamp,
- RtcpParser* rtcp_parser,
- RtcpReceiverEventLogMessages* event_log_messages);
-
- const uint32 ssrc_;
- uint32 remote_ssrc_;
-
- // Not owned by this class.
- RtcpMessageHandler* const handler_;
-
- 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);
-};
-
-} // namespace cast
-} // namespace media
-
-#endif // MEDIA_CAST_RTCP_RTCP_RECEIVER_H_
diff --git a/media/cast/net/rtcp/rtcp_receiver_unittest.cc b/media/cast/net/rtcp/rtcp_receiver_unittest.cc
deleted file mode 100644
index a32edbd224..0000000000
--- a/media/cast/net/rtcp/rtcp_receiver_unittest.cc
+++ /dev/null
@@ -1,493 +0,0 @@
-// 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/scoped_ptr.h"
-#include "base/test/simple_test_tick_clock.h"
-#include "media/cast/cast_environment.h"
-#include "media/cast/net/cast_transport_defines.h"
-#include "media/cast/net/rtcp/mock_rtcp_receiver_feedback.h"
-#include "media/cast/net/rtcp/rtcp_receiver.h"
-#include "media/cast/net/rtcp/rtcp_utility.h"
-#include "media/cast/net/rtcp/test_rtcp_packet_builder.h"
-#include "media/cast/test/fake_single_thread_task_runner.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace media {
-namespace cast {
-
-using testing::_;
-
-static const uint32 kSenderSsrc = 0x10203;
-static const uint32 kSourceSsrc = 0x40506;
-static const uint32 kUnknownSsrc = 0xDEAD;
-static const base::TimeDelta kTargetDelay =
- base::TimeDelta::FromMilliseconds(100);
-
-namespace {
-
-class RtcpMessageVerification : public MockRtcpReceiverFeedback {
- public:
- RtcpMessageVerification()
- : called_on_received_sender_log_(false),
- called_on_received_receiver_log_(false),
- called_on_received_cast_message_(false) {}
-
- 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();
- 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_);
- EXPECT_EQ(expected_it->event_log_messages_.size(),
- incoming_it->event_log_messages_.size());
-
- RtcpReceiverEventLogMessages::const_iterator event_incoming_it =
- incoming_it->event_log_messages_.begin();
- RtcpReceiverEventLogMessages::const_iterator event_expected_it =
- expected_it->event_log_messages_.begin();
- for (; event_incoming_it != incoming_it->event_log_messages_.end();
- ++event_incoming_it, ++event_expected_it) {
- 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 == PACKET_RECEIVED) {
- EXPECT_EQ(event_expected_it->packet_id, event_incoming_it->packet_id);
- } else {
- EXPECT_EQ(event_expected_it->delay_delta,
- event_incoming_it->delay_delta);
- }
- }
- expected_receiver_log_.pop_front();
- expected_it = expected_receiver_log_.begin();
- }
- called_on_received_receiver_log_ = true;
- }
-
- virtual void OnReceivedCastFeedback(const RtcpCastMessage& cast_message)
- OVERRIDE {
- EXPECT_EQ(cast_message.media_ssrc, kSenderSsrc);
- EXPECT_EQ(cast_message.ack_frame_id, kAckFrameId);
-
- MissingFramesAndPacketsMap::const_iterator frame_it =
- cast_message.missing_frames_and_packets.begin();
-
- EXPECT_TRUE(frame_it != cast_message.missing_frames_and_packets.end());
- EXPECT_EQ(kLostFrameId, frame_it->first);
- EXPECT_EQ(frame_it->second.size(), 1UL);
- EXPECT_EQ(*frame_it->second.begin(), kRtcpCastAllPacketsLost);
- ++frame_it;
- EXPECT_TRUE(frame_it != cast_message.missing_frames_and_packets.end());
- EXPECT_EQ(kFrameIdWithLostPackets, frame_it->first);
- EXPECT_EQ(3UL, frame_it->second.size());
- PacketIdSet::const_iterator packet_it = frame_it->second.begin();
- EXPECT_EQ(kLostPacketId1, *packet_it);
- ++packet_it;
- EXPECT_EQ(kLostPacketId2, *packet_it);
- ++packet_it;
- EXPECT_EQ(kLostPacketId3, *packet_it);
- ++frame_it;
- EXPECT_TRUE(frame_it == cast_message.missing_frames_and_packets.end());
- called_on_received_cast_message_ = true;
- }
-
- bool OnReceivedReceiverLogCalled() const {
- return called_on_received_receiver_log_ && expected_receiver_log_.empty();
- }
-
- bool OnReceivedCastFeedbackCalled() const {
- return called_on_received_cast_message_;
- }
-
- void SetExpectedReceiverLog(const RtcpReceiverLogMessage& receiver_log) {
- expected_receiver_log_ = receiver_log;
- }
-
- private:
- RtcpReceiverLogMessage expected_receiver_log_;
- bool called_on_received_sender_log_;
- bool called_on_received_receiver_log_;
- bool called_on_received_cast_message_;
-
- DISALLOW_COPY_AND_ASSIGN(RtcpMessageVerification);
-};
-
-} // namespace
-
-class RtcpReceiverTest : public ::testing::Test {
- protected:
- RtcpReceiverTest()
- : testing_clock_(new base::SimpleTestTickClock()),
- task_runner_(new test::FakeSingleThreadTaskRunner(
- testing_clock_.get())),
- rtcp_receiver_(new RtcpReceiver(&mock_receiver_feedback_,
- kSourceSsrc)) {
- EXPECT_CALL(mock_receiver_feedback_, OnReceivedSenderReport(_)).Times(0);
- EXPECT_CALL(mock_receiver_feedback_, OnReceiverReferenceTimeReport(_))
- .Times(0);
- EXPECT_CALL(mock_receiver_feedback_, OnReceivedCastFeedback(_)).Times(0);
- EXPECT_CALL(mock_receiver_feedback_, OnReceivedDelaySinceLastReport(_, _))
- .Times(0);
-
- expected_sender_info_.ntp_seconds = kNtpHigh;
- expected_sender_info_.ntp_fraction = kNtpLow;
- expected_sender_info_.rtp_timestamp = kRtpTimestamp;
- expected_sender_info_.send_packet_count = kSendPacketCount;
- expected_sender_info_.send_octet_count = kSendOctetCount;
-
- expected_report_block_.remote_ssrc = kSenderSsrc;
- expected_report_block_.media_ssrc = kSourceSsrc;
- expected_report_block_.fraction_lost = kLoss >> 24;
- expected_report_block_.cumulative_lost = kLoss & 0xffffff;
- expected_report_block_.extended_high_sequence_number = kExtendedMax;
- expected_report_block_.jitter = kTestJitter;
- expected_report_block_.last_sr = kLastSr;
- expected_report_block_.delay_since_last_sr = kDelayLastSr;
- expected_receiver_reference_report_.remote_ssrc = kSenderSsrc;
- expected_receiver_reference_report_.ntp_seconds = kNtpHigh;
- 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);
- }
-
- scoped_ptr<base::SimpleTestTickClock> testing_clock_;
- scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
- MockRtcpReceiverFeedback mock_receiver_feedback_;
- scoped_ptr<RtcpReceiver> rtcp_receiver_;
- RtcpSenderInfo expected_sender_info_;
- RtcpReportBlock expected_report_block_;
- RtcpReceiverReferenceTimeReport expected_receiver_reference_report_;
-
- DISALLOW_COPY_AND_ASSIGN(RtcpReceiverTest);
-};
-
-TEST_F(RtcpReceiverTest, BrokenPacketIsIgnored) {
- const uint8 bad_packet[] = {0, 0, 0, 0};
- InjectRtcpPacket(bad_packet, sizeof(bad_packet));
-}
-
-TEST_F(RtcpReceiverTest, InjectSenderReportPacket) {
- TestRtcpPacketBuilder p;
- p.AddSr(kSenderSsrc, 0);
-
- // Expected to be ignored since the sender ssrc does not match our
- // remote ssrc.
- 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.Data(), p.Length());
-}
-
-TEST_F(RtcpReceiverTest, InjectReceiveReportPacket) {
- TestRtcpPacketBuilder p1;
- p1.AddRr(kSenderSsrc, 1);
- p1.AddRb(kUnknownSsrc);
-
- // Expected to be ignored since the source ssrc does not match our
- // local ssrc.
- InjectRtcpPacket(p1.Data(), p1.Length());
-
- EXPECT_CALL(mock_receiver_feedback_,
- OnReceivedDelaySinceLastReport(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.Data(), p2.Length());
-}
-
-TEST_F(RtcpReceiverTest, InjectSenderReportWithReportBlockPacket) {
- TestRtcpPacketBuilder p1;
- p1.AddSr(kSenderSsrc, 1);
- p1.AddRb(kUnknownSsrc);
-
- // Sender report expected to be ignored since the sender ssrc does not match
- // our remote ssrc.
- // Report block expected to be ignored since the source ssrc does not match
- // our local ssrc.
- InjectRtcpPacket(p1.Data(), p1.Length());
-
- EXPECT_CALL(mock_receiver_feedback_,
- OnReceivedSenderReport(expected_sender_info_)).Times(1);
- rtcp_receiver_->SetRemoteSSRC(kSenderSsrc);
-
- // Sender report expected to be pass through since the sender ssrc match our
- // remote ssrc.
- // Report block expected to be ignored since the source ssrc does not match
- // our local ssrc.
- InjectRtcpPacket(p1.Data(), p1.Length());
-
- EXPECT_CALL(mock_receiver_feedback_, OnReceivedSenderReport(_)).Times(0);
- EXPECT_CALL(mock_receiver_feedback_,
- OnReceivedDelaySinceLastReport(kLastSr, kDelayLastSr)).Times(1);
-
- rtcp_receiver_->SetRemoteSSRC(0);
-
- TestRtcpPacketBuilder p2;
- p2.AddSr(kSenderSsrc, 1);
- p2.AddRb(kSourceSsrc);
-
- // Sender report expected to be ignored since the sender ssrc does not match
- // our remote ssrc.
- // Receiver report expected to be pass through since the sender ssrc match
- // our local ssrc.
- InjectRtcpPacket(p2.Data(), p2.Length());
-
- EXPECT_CALL(mock_receiver_feedback_,
- OnReceivedSenderReport(expected_sender_info_)).Times(1);
- EXPECT_CALL(mock_receiver_feedback_,
- OnReceivedDelaySinceLastReport(kLastSr, kDelayLastSr)).Times(1);
-
- rtcp_receiver_->SetRemoteSSRC(kSenderSsrc);
-
- // Sender report expected to be pass through since the sender ssrc match our
- // remote ssrc.
- // Receiver report expected to be pass through since the sender ssrc match
- // our local ssrc.
- InjectRtcpPacket(p2.Data(), p2.Length());
-}
-
-TEST_F(RtcpReceiverTest, InjectSenderReportPacketWithDlrr) {
- TestRtcpPacketBuilder p;
- p.AddSr(kSenderSsrc, 0);
- p.AddXrHeader(kSenderSsrc);
- p.AddXrUnknownBlock();
- p.AddXrExtendedDlrrBlock(kSenderSsrc);
- p.AddXrUnknownBlock();
-
- // Expected to be ignored since the source ssrc does not match our
- // local ssrc.
- InjectRtcpPacket(p.Data(), p.Length());
-
- EXPECT_CALL(mock_receiver_feedback_,
- OnReceivedSenderReport(expected_sender_info_)).Times(1);
- EXPECT_CALL(mock_receiver_feedback_,
- OnReceivedDelaySinceLastReport(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.Data(), p.Length());
-}
-
-TEST_F(RtcpReceiverTest, InjectReceiverReportPacketWithRrtr) {
- TestRtcpPacketBuilder p1;
- p1.AddRr(kSenderSsrc, 1);
- p1.AddRb(kUnknownSsrc);
- p1.AddXrHeader(kSenderSsrc);
- p1.AddXrRrtrBlock();
-
- // Expected to be ignored since the source ssrc does not match our
- // local ssrc.
- InjectRtcpPacket(p1.Data(), p1.Length());
-
- EXPECT_CALL(mock_receiver_feedback_,
- OnReceivedDelaySinceLastReport(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);
-
- TestRtcpPacketBuilder p2;
- p2.AddRr(kSenderSsrc, 1);
- p2.AddRb(kSourceSsrc);
- p2.AddXrHeader(kSenderSsrc);
- p2.AddXrRrtrBlock();
-
- // Expected to be pass through since the sender ssrc match our local ssrc.
- InjectRtcpPacket(p2.Data(), p2.Length());
-}
-
-TEST_F(RtcpReceiverTest, InjectReceiverReportPacketWithIntraFrameRequest) {
- TestRtcpPacketBuilder p1;
- p1.AddRr(kSenderSsrc, 1);
- p1.AddRb(kUnknownSsrc);
-
- // Expected to be ignored since the source ssrc does not match our
- // local ssrc.
- InjectRtcpPacket(p1.Data(), p1.Length());
-
- EXPECT_CALL(mock_receiver_feedback_,
- OnReceivedDelaySinceLastReport(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.Data(), p2.Length());
-}
-
-TEST_F(RtcpReceiverTest, InjectReceiverReportPacketWithCastFeedback) {
- TestRtcpPacketBuilder p1;
- p1.AddRr(kSenderSsrc, 1);
- p1.AddRb(kUnknownSsrc);
- p1.AddCast(kSenderSsrc, kUnknownSsrc, kTargetDelay);
-
- // Expected to be ignored since the source ssrc does not match our
- // local ssrc.
- InjectRtcpPacket(p1.Data(), p1.Length());
-
- EXPECT_CALL(mock_receiver_feedback_,
- OnReceivedDelaySinceLastReport(kLastSr, kDelayLastSr)).Times(1);
- EXPECT_CALL(mock_receiver_feedback_, OnReceivedCastFeedback(_)).Times(1);
-
- // Enable receiving the cast feedback.
- rtcp_receiver_->SetRemoteSSRC(kSenderSsrc);
-
- TestRtcpPacketBuilder p2;
- p2.AddRr(kSenderSsrc, 1);
- p2.AddRb(kSourceSsrc);
- p2.AddCast(kSenderSsrc, kSourceSsrc, kTargetDelay);
-
- // Expected to be pass through since the sender ssrc match our local ssrc.
- InjectRtcpPacket(p2.Data(), p2.Length());
-}
-
-TEST_F(RtcpReceiverTest, InjectReceiverReportPacketWithCastVerification) {
- RtcpMessageVerification verification;
- RtcpReceiver rtcp_receiver(&verification, kSourceSsrc);
-
- EXPECT_CALL(verification,
- OnReceivedDelaySinceLastReport(kLastSr, kDelayLastSr)).Times(1);
-
- // Enable receiving the cast feedback.
- rtcp_receiver.SetRemoteSSRC(kSenderSsrc);
-
- TestRtcpPacketBuilder p;
- p.AddRr(kSenderSsrc, 1);
- p.AddRb(kSourceSsrc);
- p.AddCast(kSenderSsrc, kSourceSsrc, kTargetDelay);
-
- // Expected to be pass through since the sender ssrc match our local ssrc.
- RtcpParser rtcp_parser(p.Data(), p.Length());
- rtcp_receiver.IncomingRtcpPacket(&rtcp_parser);
-
- EXPECT_TRUE(verification.OnReceivedCastFeedbackCalled());
-}
-
-TEST_F(RtcpReceiverTest, InjectReceiverReportWithReceiverLogVerificationBase) {
- static const uint32 kTimeBaseMs = 12345678;
- static const uint32 kTimeDelayMs = 10;
- static const uint32 kDelayDeltaMs = 123;
- base::SimpleTestTickClock testing_clock;
- testing_clock.Advance(base::TimeDelta::FromMilliseconds(kTimeBaseMs));
-
- RtcpMessageVerification verification;
- RtcpReceiver rtcp_receiver(&verification,
- kSourceSsrc);
- rtcp_receiver.SetRemoteSSRC(kSenderSsrc);
- rtcp_receiver.SetCastReceiverEventHistorySize(100);
-
- RtcpReceiverLogMessage receiver_log;
- RtcpReceiverFrameLogMessage frame_log(kRtpTimestamp);
- RtcpReceiverEventLogMessage event_log;
-
- 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 = 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);
-
- verification.SetExpectedReceiverLog(receiver_log);
-
- TestRtcpPacketBuilder p;
- p.AddRr(kSenderSsrc, 1);
- p.AddRb(kSourceSsrc);
- p.AddReceiverLog(kSenderSsrc);
- 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(verification,
- OnReceivedDelaySinceLastReport(kLastSr, kDelayLastSr)).Times(1);
-
- RtcpParser rtcp_parser(p.Data(), p.Length());
- rtcp_receiver.IncomingRtcpPacket(&rtcp_parser);
-
- EXPECT_TRUE(verification.OnReceivedReceiverLogCalled());
-}
-
-TEST_F(RtcpReceiverTest, InjectReceiverReportWithReceiverLogVerificationMulti) {
- static const uint32 kTimeBaseMs = 12345678;
- static const uint32 kTimeDelayMs = 10;
- static const uint32 kDelayDeltaMs = 123;
- base::SimpleTestTickClock testing_clock;
- testing_clock.Advance(base::TimeDelta::FromMilliseconds(kTimeBaseMs));
-
- RtcpMessageVerification verification;
- RtcpReceiver rtcp_receiver(&verification,
- kSourceSsrc);
- rtcp_receiver.SetRemoteSSRC(kSenderSsrc);
-
- RtcpReceiverLogMessage receiver_log;
-
- for (int j = 0; j < 100; ++j) {
- RtcpReceiverFrameLogMessage frame_log(kRtpTimestamp);
- RtcpReceiverEventLogMessage event_log;
- 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);
- receiver_log.push_back(frame_log);
- testing_clock.Advance(base::TimeDelta::FromMilliseconds(kTimeDelayMs));
- }
-
- verification.SetExpectedReceiverLog(receiver_log);
-
- TestRtcpPacketBuilder p;
- p.AddRr(kSenderSsrc, 1);
- p.AddRb(kSourceSsrc);
- p.AddReceiverLog(kSenderSsrc);
- for (int i = 0; i < 100; ++i) {
- p.AddReceiverFrameLog(kRtpTimestamp, 1, kTimeBaseMs + i * kTimeDelayMs);
- p.AddReceiverEventLog(kDelayDeltaMs, FRAME_ACK_SENT, 0);
- }
-
- EXPECT_CALL(verification,
- OnReceivedDelaySinceLastReport(kLastSr, kDelayLastSr)).Times(1);
-
- RtcpParser rtcp_parser(p.Data(), p.Length());
- rtcp_receiver.IncomingRtcpPacket(&rtcp_parser);
-
- EXPECT_TRUE(verification.OnReceivedReceiverLogCalled());
-}
-
-} // namespace cast
-} // namespace media
diff --git a/media/cast/net/rtcp/rtcp_sender.cc b/media/cast/net/rtcp/rtcp_sender.cc
index 244c4d3983..4ca6eb581b 100644
--- a/media/cast/net/rtcp/rtcp_sender.cc
+++ b/media/cast/net/rtcp/rtcp_sender.cc
@@ -154,32 +154,21 @@ RtcpSender::RtcpSender(PacedPacketSender* outgoing_transport,
RtcpSender::~RtcpSender() {}
void RtcpSender::SendRtcpFromRtpReceiver(
- uint32 packet_type_flags,
const RtcpReportBlock* report_block,
const RtcpReceiverReferenceTimeReport* rrtr,
const RtcpCastMessage* cast_message,
const ReceiverRtcpEventSubscriber::RtcpEventMultiMap* rtcp_events,
base::TimeDelta target_delay) {
- if (packet_type_flags & kRtcpDlrr) {
- NOTREACHED() << "Invalid argument";
- }
PacketRef packet(new base::RefCountedData<Packet>);
packet->data.reserve(kMaxIpPacketSize);
- if (packet_type_flags & kRtcpRr) {
+ if (report_block)
BuildRR(report_block, &packet->data);
- }
- if (packet_type_flags & kRtcpRrtr) {
- DCHECK(rrtr) << "Invalid argument";
+ if (rrtr)
BuildRrtr(rrtr, &packet->data);
- }
- if (packet_type_flags & kRtcpCast) {
- DCHECK(cast_message) << "Invalid argument";
+ if (cast_message)
BuildCast(cast_message, target_delay, &packet->data);
- }
- if (packet_type_flags & kRtcpReceiverLog) {
- DCHECK(rtcp_events) << "Invalid argument";
+ if (rtcp_events)
BuildReceiverLog(*rtcp_events, &packet->data);
- }
if (packet->data.empty()) {
NOTREACHED() << "Empty packet.";
@@ -190,23 +179,11 @@ void RtcpSender::SendRtcpFromRtpReceiver(
}
void RtcpSender::SendRtcpFromRtpSender(
- uint32 packet_type_flags,
- const RtcpSenderInfo& sender_info,
- const RtcpDlrrReportBlock& dlrr) {
- if (packet_type_flags & kRtcpRr ||
- packet_type_flags & kRtcpRrtr ||
- packet_type_flags & kRtcpCast ||
- packet_type_flags & kRtcpReceiverLog) {
- NOTREACHED() << "Invalid argument";
- }
+ const RtcpSenderInfo& sender_info) {
PacketRef packet(new base::RefCountedData<Packet>);
packet->data.reserve(kMaxIpPacketSize);
- if (packet_type_flags & kRtcpSr) {
- BuildSR(sender_info, &packet->data);
- }
- if (packet_type_flags & kRtcpDlrr) {
- BuildDlrrRb(dlrr, &packet->data);
- }
+ BuildSR(sender_info, &packet->data);
+
if (packet->data.empty()) {
NOTREACHED() << "Empty packet.";
return; // Sanity - don't send empty packets.
diff --git a/media/cast/net/rtcp/rtcp_sender.h b/media/cast/net/rtcp/rtcp_sender.h
index bfbd0cf3b6..06b11d4844 100644
--- a/media/cast/net/rtcp/rtcp_sender.h
+++ b/media/cast/net/rtcp/rtcp_sender.h
@@ -51,7 +51,6 @@ class RtcpSender {
// TODO(hclam): This method should be to build a packet instead of
// sending it.
void SendRtcpFromRtpReceiver(
- uint32 packet_type_flags,
const RtcpReportBlock* report_block,
const RtcpReceiverReferenceTimeReport* rrtr,
const RtcpCastMessage* cast_message,
@@ -60,9 +59,7 @@ class RtcpSender {
// TODO(hclam): This method should be to build a packet instead of
// sending it.
- void SendRtcpFromRtpSender(uint32 packet_type_flags,
- const RtcpSenderInfo& sender_info,
- const RtcpDlrrReportBlock& dlrr);
+ void SendRtcpFromRtpSender(const RtcpSenderInfo& sender_info);
private:
void BuildRR(const RtcpReportBlock* report_block,
diff --git a/media/cast/net/rtcp/rtcp_sender_unittest.cc b/media/cast/net/rtcp/rtcp_sender_unittest.cc
index a9419e4c53..6dbea4d94e 100644
--- a/media/cast/net/rtcp/rtcp_sender_unittest.cc
+++ b/media/cast/net/rtcp/rtcp_sender_unittest.cc
@@ -62,7 +62,7 @@ class TestRtcpTransport : public PacedPacketSender {
}
virtual bool ResendPackets(
const SendPacketVector& packets,
- base::TimeDelta dedupe_window) OVERRIDE {
+ const DedupInfo& dedup_info) OVERRIDE {
return false;
}
@@ -105,16 +105,6 @@ class RtcpSenderTest : public ::testing::Test {
};
TEST_F(RtcpSenderTest, RtcpReceiverReport) {
- // Empty receiver report.
- TestRtcpPacketBuilder p1;
- p1.AddRr(kSendingSsrc, 0);
- test_transport_.SetExpectedRtcpPacket(p1.GetPacket());
-
- rtcp_sender_->SendRtcpFromRtpReceiver(
- kRtcpRr, NULL, NULL, NULL, NULL, kDefaultDelay);
-
- EXPECT_EQ(1, test_transport_.packet_count());
-
// Receiver report with report block.
TestRtcpPacketBuilder p2;
p2.AddRr(kSendingSsrc, 1);
@@ -124,9 +114,9 @@ TEST_F(RtcpSenderTest, RtcpReceiverReport) {
RtcpReportBlock report_block = GetReportBlock();
rtcp_sender_->SendRtcpFromRtpReceiver(
- kRtcpRr, &report_block, NULL, NULL, NULL, kDefaultDelay);
+ &report_block, NULL, NULL, NULL, kDefaultDelay);
- EXPECT_EQ(2, test_transport_.packet_count());
+ EXPECT_EQ(1, test_transport_.packet_count());
}
TEST_F(RtcpSenderTest, RtcpReceiverReportWithRrtr) {
@@ -145,7 +135,6 @@ TEST_F(RtcpSenderTest, RtcpReceiverReportWithRrtr) {
rrtr.ntp_fraction = kNtpLow;
rtcp_sender_->SendRtcpFromRtpReceiver(
- kRtcpRr | kRtcpRrtr,
&report_block,
&rrtr,
NULL,
@@ -177,7 +166,6 @@ TEST_F(RtcpSenderTest, RtcpReceiverReportWithCast) {
missing_packets;
rtcp_sender_->SendRtcpFromRtpReceiver(
- kRtcpRr | kRtcpCast,
&report_block,
NULL,
&cast_message,
@@ -214,7 +202,6 @@ TEST_F(RtcpSenderTest, RtcpReceiverReportWithRrtraAndCastMessage) {
missing_packets;
rtcp_sender_->SendRtcpFromRtpReceiver(
- kRtcpRr | kRtcpRrtr | kRtcpCast,
&report_block,
&rrtr,
&cast_message,
@@ -257,8 +244,6 @@ TEST_F(RtcpSenderTest, RtcpReceiverReportWithRrtrCastMessageAndLog) {
ReceiverRtcpEventSubscriber::RtcpEventMultiMap rtcp_events;
rtcp_sender_->SendRtcpFromRtpReceiver(
- kRtcpRr | kRtcpRrtr | kRtcpCast |
- kRtcpReceiverLog,
&report_block,
&rrtr,
&cast_message,
@@ -294,8 +279,6 @@ TEST_F(RtcpSenderTest, RtcpReceiverReportWithRrtrCastMessageAndLog) {
EXPECT_EQ(2u, rtcp_events.size());
rtcp_sender_->SendRtcpFromRtpReceiver(
- kRtcpRr | kRtcpRrtr | kRtcpCast |
- kRtcpReceiverLog,
&report_block,
&rrtr,
&cast_message,
@@ -362,7 +345,6 @@ TEST_F(RtcpSenderTest, RtcpReceiverReportWithOversizedFrameLog) {
event_subscriber.GetRtcpEventsAndReset(&rtcp_events);
rtcp_sender_->SendRtcpFromRtpReceiver(
- kRtcpRr | kRtcpReceiverLog,
&report_block,
NULL,
NULL,
@@ -418,7 +400,6 @@ TEST_F(RtcpSenderTest, RtcpReceiverReportWithTooManyLogFrames) {
event_subscriber.GetRtcpEventsAndReset(&rtcp_events);
rtcp_sender_->SendRtcpFromRtpReceiver(
- kRtcpRr | kRtcpReceiverLog,
&report_block,
NULL,
NULL,
@@ -468,7 +449,6 @@ TEST_F(RtcpSenderTest, RtcpReceiverReportWithOldLogFrames) {
event_subscriber.GetRtcpEventsAndReset(&rtcp_events);
rtcp_sender_->SendRtcpFromRtpReceiver(
- kRtcpRr | kRtcpReceiverLog,
&report_block,
NULL,
NULL,
@@ -526,7 +506,6 @@ TEST_F(RtcpSenderTest, RtcpReceiverReportRedundancy) {
event_subscriber.GetRtcpEventsAndReset(&rtcp_events);
rtcp_sender_->SendRtcpFromRtpReceiver(
- kRtcpRr | kRtcpReceiverLog,
&report_block,
NULL,
NULL,
@@ -549,45 +528,12 @@ TEST_F(RtcpSenderTest, RtcpSenderReport) {
sender_info.send_packet_count = kSendPacketCount;
sender_info.send_octet_count = kSendOctetCount;
- RtcpDlrrReportBlock dlrr_rb;
- dlrr_rb.last_rr = kLastRr;
- dlrr_rb.delay_since_last_rr = kDelayLastRr;
-
// Sender report.
TestRtcpPacketBuilder p;
p.AddSr(kSendingSsrc, 0);
test_transport_.SetExpectedRtcpPacket(p.GetPacket().Pass());
- rtcp_sender_->SendRtcpFromRtpSender(kRtcpSr,
- sender_info,
- dlrr_rb);
-
- EXPECT_EQ(1, 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 + dlrr.
- TestRtcpPacketBuilder p1;
- p1.AddSr(kSendingSsrc, 0);
- p1.AddXrHeader(kSendingSsrc);
- p1.AddXrDlrrBlock(kSendingSsrc);
- test_transport_.SetExpectedRtcpPacket(p1.GetPacket().Pass());
-
- RtcpDlrrReportBlock dlrr_rb;
- dlrr_rb.last_rr = kLastRr;
- dlrr_rb.delay_since_last_rr = kDelayLastRr;
-
- rtcp_sender_->SendRtcpFromRtpSender(
- kRtcpSr | kRtcpDlrr,
- sender_info,
- dlrr_rb);
+ rtcp_sender_->SendRtcpFromRtpSender(sender_info);
EXPECT_EQ(1, test_transport_.packet_count());
}
diff --git a/media/cast/net/rtcp/rtcp_unittest.cc b/media/cast/net/rtcp/rtcp_unittest.cc
index 5ea9ee101e..baa0699bdd 100644
--- a/media/cast/net/rtcp/rtcp_unittest.cc
+++ b/media/cast/net/rtcp/rtcp_unittest.cc
@@ -9,7 +9,6 @@
#include "media/cast/net/cast_transport_config.h"
#include "media/cast/net/cast_transport_sender_impl.h"
#include "media/cast/net/pacing/paced_sender.h"
-#include "media/cast/net/rtcp/mock_rtcp_receiver_feedback.h"
#include "media/cast/net/rtcp/rtcp.h"
#include "media/cast/net/rtcp/test_rtcp_packet_builder.h"
#include "media/cast/test/fake_single_thread_task_runner.h"
@@ -99,8 +98,7 @@ class LocalRtcpTransport : public PacedPacketSender {
}
virtual bool ResendPackets(
- const SendPacketVector& packets,
- base::TimeDelta dedupe_window) OVERRIDE {
+ const SendPacketVector& packets, const DedupInfo& dedup_info) OVERRIDE {
return false;
}
@@ -272,25 +270,13 @@ TEST_F(RtcpTest, RttReducedSizeRtcp) {
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));
rtcp_sender.SendRtcpFromRtpSender(testing_clock_->NowTicks(), 1, 1, 1);
RunTasks(33);
rtcp_receiver.SendRtcpFromRtpReceiver(NULL, base::TimeDelta(), NULL, &stats_);
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, 1, 1);
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) {
@@ -322,7 +308,6 @@ TEST_F(RtcpTest, 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));
rtcp_sender.SendRtcpFromRtpSender(testing_clock_->NowTicks(), 1, 1, 1);
RunTasks(33);
@@ -331,7 +316,6 @@ TEST_F(RtcpTest, Rtt) {
EXPECT_TRUE(rtcp_sender.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
RunTasks(33);
- EXPECT_FALSE(rtcp_receiver.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
RunTasks(33);
EXPECT_NEAR(2 * kAddedDelay, rtt.InMilliseconds(), 2);
@@ -341,11 +325,6 @@ TEST_F(RtcpTest, Rtt) {
rtcp_sender.SendRtcpFromRtpSender(testing_clock_->NowTicks(), 2, 1, 1);
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();
@@ -359,13 +338,6 @@ TEST_F(RtcpTest, Rtt) {
rtcp_sender.SendRtcpFromRtpSender(testing_clock_->NowTicks(), 3, 1, 1);
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(), 2);
- EXPECT_NEAR(2 * kAddedDelay, max_rtt.InMilliseconds(), 2);
rtcp_receiver.SendRtcpFromRtpReceiver(NULL, base::TimeDelta(), NULL, &stats_);
EXPECT_TRUE(rtcp_sender.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
@@ -413,11 +385,6 @@ TEST_F(RtcpTest, RttWithPacketLoss) {
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_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_to_sender_.set_short_delay();
sender_to_receiver_.set_short_delay();
@@ -427,8 +394,6 @@ TEST_F(RtcpTest, RttWithPacketLoss) {
rtcp_sender.SendRtcpFromRtpSender(testing_clock_->NowTicks(), 1, 1, 1);
RunTasks(33);
- EXPECT_TRUE(rtcp_receiver.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt));
- EXPECT_NEAR(kAddedDelay + kAddedShortDelay, rtt.InMilliseconds(), 2);
}
TEST_F(RtcpTest, NtpAndTime) {
diff --git a/media/cast/net/rtcp/rtcp_utility.cc b/media/cast/net/rtcp/rtcp_utility.cc
index 4f99cd4174..a1a6a48c1d 100644
--- a/media/cast/net/rtcp/rtcp_utility.cc
+++ b/media/cast/net/rtcp/rtcp_utility.cc
@@ -4,198 +4,66 @@
#include "media/cast/net/rtcp/rtcp_utility.h"
-#include "base/big_endian.h"
#include "base/logging.h"
#include "media/cast/net/cast_transport_defines.h"
namespace media {
namespace cast {
-RtcpParser::RtcpParser(const uint8* rtcpData, size_t rtcpDataLength)
- : rtcp_data_begin_(rtcpData),
- rtcp_data_end_(rtcpData + rtcpDataLength),
- valid_packet_(false),
- rtcp_data_(rtcpData),
- rtcp_block_end_(NULL),
- state_(kStateTopLevel),
- number_of_blocks_(0),
- field_type_(kRtcpNotValidCode) {
- memset(&field_, 0, sizeof(field_));
- Validate();
+RtcpParser::RtcpParser(uint32 local_ssrc, uint32 remote_ssrc) :
+ local_ssrc_(local_ssrc),
+ remote_ssrc_(remote_ssrc),
+ has_sender_report_(false),
+ has_last_report_(false),
+ has_cast_message_(false),
+ has_receiver_reference_time_report_(false) {
}
RtcpParser::~RtcpParser() {}
-RtcpFieldTypes RtcpParser::FieldType() const { return field_type_; }
-
-const RtcpField& RtcpParser::Field() const { return field_; }
-
-RtcpFieldTypes RtcpParser::Begin() {
- rtcp_data_ = rtcp_data_begin_;
- return Iterate();
-}
-
-RtcpFieldTypes RtcpParser::Iterate() {
- // Reset packet type
- field_type_ = kRtcpNotValidCode;
-
- if (!IsValid())
- return kRtcpNotValidCode;
-
- switch (state_) {
- case kStateTopLevel:
- IterateTopLevel();
- break;
- case kStateReportBlock:
- IterateReportBlockItem();
- break;
- case kStateApplicationSpecificCastReceiverFrameLog:
- IterateCastReceiverLogFrame();
- break;
- case kStateApplicationSpecificCastReceiverEventLog:
- IterateCastReceiverLogEvent();
- break;
- case kStateExtendedReportBlock:
- IterateExtendedReportItem();
- break;
- case kStateExtendedReportDelaySinceLastReceiverReport:
- IterateExtendedReportDelaySinceLastReceiverReportItem();
- break;
- case kStatePayloadSpecificApplication:
- IteratePayloadSpecificAppItem();
- break;
- case kStatePayloadSpecificCast:
- IteratePayloadSpecificCastItem();
- break;
- case kStatePayloadSpecificCastNack:
- IteratePayloadSpecificCastNackItem();
- break;
- }
- return field_type_;
-}
-
-void RtcpParser::IterateTopLevel() {
- for (;;) {
+bool RtcpParser::Parse(base::BigEndianReader* reader) {
+ while (reader->remaining()) {
RtcpCommonHeader header;
+ if (!ParseCommonHeader(reader, &header))
+ return false;
- bool success = RtcpParseCommonHeader(rtcp_data_, rtcp_data_end_, &header);
- if (!success)
- return;
-
- rtcp_block_end_ = rtcp_data_ + header.length_in_octets;
-
- if (rtcp_block_end_ > rtcp_data_end_)
- return; // Bad block!
+ base::StringPiece tmp;
+ if (!reader->ReadPiece(&tmp, header.length_in_octets - 4))
+ return false;
+ base::BigEndianReader chunk(tmp.data(), tmp.size());
switch (header.PT) {
case kPacketTypeSenderReport:
- // number of Report blocks
- number_of_blocks_ = header.IC;
- ParseSR();
- return;
+ if (!ParseSR(&chunk, header))
+ return false;
+ break;
+
case kPacketTypeReceiverReport:
- // number of Report blocks
- number_of_blocks_ = header.IC;
- ParseRR();
- return;
+ if (!ParseRR(&chunk, header))
+ return false;
+ break;
+
case kPacketTypeApplicationDefined:
- if (!ParseApplicationDefined(header.IC)) {
- // Nothing supported found, continue to next block!
- break;
- }
- return;
- case kPacketTypeGenericRtpFeedback: // Fall through!
+ if (!ParseApplicationDefined(&chunk, header))
+ return false;
+ break;
+
case kPacketTypePayloadSpecific:
- if (!ParseFeedBackCommon(header)) {
- // Nothing supported found, continue to next block!
- break;
- }
- return;
+ if (!ParseFeedbackCommon(&chunk, header))
+ return false;
+ break;
+
case kPacketTypeXr:
- if (!ParseExtendedReport()) {
- break; // Nothing supported found, continue to next block!
- }
- return;
- default:
- // Not supported! Skip!
- EndCurrentBlock();
+ if (!ParseExtendedReport(&chunk, header))
+ return false;
break;
}
}
+ return true;
}
-void RtcpParser::IterateReportBlockItem() {
- bool success = ParseReportBlockItem();
- if (!success)
- Iterate();
-}
-
-void RtcpParser::IterateExtendedReportItem() {
- bool success = ParseExtendedReportItem();
- if (!success)
- Iterate();
-}
-
-void RtcpParser::IterateExtendedReportDelaySinceLastReceiverReportItem() {
- bool success = ParseExtendedReportDelaySinceLastReceiverReport();
- if (!success)
- Iterate();
-}
-
-void RtcpParser::IteratePayloadSpecificAppItem() {
- bool success = ParsePayloadSpecificAppItem();
- if (!success)
- Iterate();
-}
-
-void RtcpParser::IteratePayloadSpecificCastItem() {
- bool success = ParsePayloadSpecificCastItem();
- if (!success)
- Iterate();
-}
-
-void RtcpParser::IteratePayloadSpecificCastNackItem() {
- bool success = ParsePayloadSpecificCastNackItem();
- if (!success)
- Iterate();
-}
-
-void RtcpParser::IterateCastReceiverLogFrame() {
- bool success = ParseCastReceiverLogFrameItem();
- if (!success)
- Iterate();
-}
-
-void RtcpParser::IterateCastReceiverLogEvent() {
- bool success = ParseCastReceiverLogEventItem();
- if (!success)
- Iterate();
-}
-
-void RtcpParser::Validate() {
- if (rtcp_data_ == NULL)
- return; // NOT VALID
-
- RtcpCommonHeader header;
- bool success =
- RtcpParseCommonHeader(rtcp_data_begin_, rtcp_data_end_, &header);
-
- if (!success)
- return; // NOT VALID!
-
- valid_packet_ = true;
-}
-
-bool RtcpParser::IsValid() const { return valid_packet_; }
-
-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;
-
+bool RtcpParser::ParseCommonHeader(base::BigEndianReader* reader,
+ RtcpCommonHeader* parsed_header) {
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -204,448 +72,265 @@ bool RtcpParser::RtcpParseCommonHeader(const uint8* data_begin,
//
// Common header for all Rtcp packets, 4 octets.
- if ((data_end - data_begin) < 4)
+ uint8 byte;
+ if (!reader->ReadU8(&byte))
return false;
+ parsed_header->V = byte >> 6;
+ parsed_header->P = ((byte & 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];
+ // Check if RTP version field == 2.
+ if (parsed_header->V != 2)
+ return false;
- parsed_header->length_in_octets =
- ((data_begin[2] << 8) + data_begin[3] + 1) * 4;
+ parsed_header->IC = byte & 0x1f;
+ if (!reader->ReadU8(&parsed_header->PT))
+ return false;
- if (parsed_header->length_in_octets == 0)
+ uint16 bytes;
+ if (!reader->ReadU16(&bytes))
return false;
- // Check if RTP version field == 2.
- if (parsed_header->V != 2)
+ parsed_header->length_in_octets = (static_cast<size_t>(bytes) + 1) * 4;
+
+ if (parsed_header->length_in_octets == 0)
return false;
return true;
}
-bool RtcpParser::ParseRR() {
- ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
- if (length < 8)
+bool RtcpParser::ParseSR(base::BigEndianReader* reader,
+ const RtcpCommonHeader& header) {
+ uint32 sender_ssrc;
+ if (!reader->ReadU32(&sender_ssrc))
return false;
- field_type_ = kRtcpRrCode;
+ if (sender_ssrc != remote_ssrc_)
+ return true;
+
+ uint32 tmp;
+ if (!reader->ReadU32(&sender_report_.ntp_seconds) ||
+ !reader->ReadU32(&sender_report_.ntp_fraction) ||
+ !reader->ReadU32(&sender_report_.rtp_timestamp) ||
+ !reader->ReadU32(&sender_report_.send_packet_count) ||
+ !reader->ReadU32(&tmp))
+ return false;
+ sender_report_.send_octet_count = tmp;
+ has_sender_report_ = true;
- 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_;
- rtcp_data_ += 8;
+ for (size_t block = 0; block < header.IC; block++)
+ if (!ParseReportBlock(reader))
+ return false;
- // State transition
- state_ = kStateReportBlock;
return true;
}
-bool RtcpParser::ParseSR() {
- ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
- if (length < 28) {
- EndCurrentBlock();
+bool RtcpParser::ParseRR(base::BigEndianReader* reader,
+ const RtcpCommonHeader& header) {
+ uint32 receiver_ssrc;
+ if (!reader->ReadU32(&receiver_ssrc))
return false;
- }
- field_type_ = kRtcpSrCode;
-
- 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);
- big_endian_reader.ReadU32(&field_.sender_report.ntp_least_significant);
- big_endian_reader.ReadU32(&field_.sender_report.rtp_timestamp);
- big_endian_reader.ReadU32(&field_.sender_report.sender_packet_count);
- big_endian_reader.ReadU32(&field_.sender_report.sender_octet_count);
- field_.sender_report.number_of_report_blocks = number_of_blocks_;
- rtcp_data_ += 28;
-
- if (number_of_blocks_ != 0) {
- // State transition.
- state_ = kStateReportBlock;
- } else {
- // Don't go to state report block item if 0 report blocks.
- state_ = kStateTopLevel;
- EndCurrentBlock();
- }
+
+ if (receiver_ssrc != remote_ssrc_)
+ return true;
+
+ for (size_t block = 0; block < header.IC; block++)
+ if (!ParseReportBlock(reader))
+ return false;
+
return true;
}
-bool RtcpParser::ParseReportBlockItem() {
- ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
- if (length < 24 || number_of_blocks_ <= 0) {
- state_ = kStateTopLevel;
- EndCurrentBlock();
+bool RtcpParser::ParseReportBlock(base::BigEndianReader* reader) {
+ uint32 ssrc, last_report, delay;
+ if (!reader->ReadU32(&ssrc) ||
+ !reader->Skip(12) ||
+ !reader->ReadU32(&last_report) ||
+ !reader->ReadU32(&delay))
return false;
+
+ if (ssrc == local_ssrc_) {
+ last_report_ = last_report;
+ delay_since_last_report_ = delay;
+ has_last_report_ = true;
}
- 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);
-
- uint8 temp_number_of_packets_lost;
- big_endian_reader.ReadU8(&temp_number_of_packets_lost);
- field_.report_block_item.cumulative_number_of_packets_lost =
- temp_number_of_packets_lost << 16;
- big_endian_reader.ReadU8(&temp_number_of_packets_lost);
- field_.report_block_item.cumulative_number_of_packets_lost +=
- temp_number_of_packets_lost << 8;
- big_endian_reader.ReadU8(&temp_number_of_packets_lost);
- field_.report_block_item.cumulative_number_of_packets_lost +=
- temp_number_of_packets_lost;
-
- big_endian_reader.ReadU32(
- &field_.report_block_item.extended_highest_sequence_number);
- big_endian_reader.ReadU32(&field_.report_block_item.jitter);
- big_endian_reader.ReadU32(&field_.report_block_item.last_sender_report);
- big_endian_reader.ReadU32(&field_.report_block_item.delay_last_sender_report);
- rtcp_data_ += 24;
-
- number_of_blocks_--;
- field_type_ = kRtcpReportBlockItemCode;
return true;
}
-bool RtcpParser::ParseApplicationDefined(uint8 subtype) {
- ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
- if (length < 16 || subtype != kReceiverLogSubtype) {
- state_ = kStateTopLevel;
- EndCurrentBlock();
- return false;
- }
-
+bool RtcpParser::ParseApplicationDefined(base::BigEndianReader* reader,
+ const RtcpCommonHeader& header) {
uint32 sender_ssrc;
uint32 name;
+ if (!reader->ReadU32(&sender_ssrc) ||
+ !reader->ReadU32(&name))
+ return false;
- 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);
+ if (sender_ssrc != remote_ssrc_)
+ return true;
- if (name != kCast) {
- state_ = kStateTopLevel;
- EndCurrentBlock();
+ if (name != kCast)
return false;
- }
- rtcp_data_ += 12;
- switch (subtype) {
+
+ switch (header.IC /* subtype */ ) {
case kReceiverLogSubtype:
- state_ = kStateApplicationSpecificCastReceiverFrameLog;
- field_type_ = kRtcpApplicationSpecificCastReceiverLogCode;
- field_.cast_receiver_log.sender_ssrc = sender_ssrc;
+ if (!ParseCastReceiverLogFrameItem(reader))
+ return false;
break;
- default:
- NOTREACHED();
}
return true;
}
-bool RtcpParser::ParseCastReceiverLogFrameItem() {
- ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
- if (length < 12) {
- state_ = kStateTopLevel;
- EndCurrentBlock();
- return false;
- }
- uint32 rtp_timestamp;
- uint32 data;
- base::BigEndianReader big_endian_reader(
- reinterpret_cast<const char*>(rtcp_data_), length);
- big_endian_reader.ReadU32(&rtp_timestamp);
- big_endian_reader.ReadU32(&data);
-
- rtcp_data_ += 8;
-
- field_.cast_receiver_log.rtp_timestamp = rtp_timestamp;
- // We have 24 LSB of the event timestamp base on the wire.
- field_.cast_receiver_log.event_timestamp_base = data & 0xffffff;
-
- number_of_blocks_ = 1 + static_cast<uint8>(data >> 24);
- state_ = kStateApplicationSpecificCastReceiverEventLog;
- field_type_ = kRtcpApplicationSpecificCastReceiverLogFrameCode;
- return true;
-}
+bool RtcpParser::ParseCastReceiverLogFrameItem(
+ base::BigEndianReader* reader) {
-bool RtcpParser::ParseCastReceiverLogEventItem() {
- ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
- if (length < 4) {
- state_ = kStateTopLevel;
- EndCurrentBlock();
- return false;
- }
- if (number_of_blocks_ == 0) {
- // Continue parsing the next receiver frame event.
- state_ = kStateApplicationSpecificCastReceiverFrameLog;
- return false;
- }
- number_of_blocks_--;
-
- uint16 delay_delta_or_packet_id;
- uint16 event_type_and_timestamp_delta;
- 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);
-
- rtcp_data_ += 4;
-
- field_.cast_receiver_log.event =
- static_cast<uint8>(event_type_and_timestamp_delta >> 12);
- // 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;
-
- field_type_ = kRtcpApplicationSpecificCastReceiverLogEventCode;
- return true;
-}
+ while (reader->remaining()) {
+ uint32 rtp_timestamp;
+ uint32 data;
+ if (!reader->ReadU32(&rtp_timestamp) ||
+ !reader->ReadU32(&data))
+ return false;
-bool RtcpParser::ParseFeedBackCommon(const RtcpCommonHeader& header) {
- DCHECK((header.PT == kPacketTypeGenericRtpFeedback) ||
- (header.PT == kPacketTypePayloadSpecific))
- << "Invalid state";
+ // We have 24 LSB of the event timestamp base on the wire.
+ base::TimeTicks event_timestamp_base = base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(data & 0xffffff);
- ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
+ size_t num_events = 1 + static_cast<uint8>(data >> 24);
- if (length < 12) { // 4 * 3, RFC4585 section 6.1
- EndCurrentBlock();
- return false;
- }
+ RtcpReceiverFrameLogMessage frame_log(rtp_timestamp);
+ for (size_t event = 0; event < num_events; event++) {
+ uint16 delay_delta_or_packet_id;
+ uint16 event_type_and_timestamp_delta;
+ if (!reader->ReadU16(&delay_delta_or_packet_id) ||
+ !reader->ReadU16(&event_type_and_timestamp_delta))
+ return false;
- uint32 sender_ssrc;
- uint32 media_ssrc;
- 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 == kPacketTypePayloadSpecific) {
- // Payload specific feedback
- switch (header.IC) {
- case 1:
- // PLI
- break;
- case 2:
- // SLI.
- break;
- case 3:
- // RPSI.
- break;
- case 4:
- // FIR.
- break;
- case 15:
- field_type_ = kRtcpPayloadSpecificAppCode;
- field_.application_specific.sender_ssrc = sender_ssrc;
- field_.application_specific.media_ssrc = media_ssrc;
- state_ = kStatePayloadSpecificApplication;
- return true;
- default:
- break;
+ RtcpReceiverEventLogMessage event_log;
+ event_log.type = TranslateToLogEventFromWireFormat(
+ static_cast<uint8>(event_type_and_timestamp_delta >> 12));
+ event_log.event_timestamp =
+ event_timestamp_base +
+ base::TimeDelta::FromMilliseconds(
+ event_type_and_timestamp_delta & 0xfff);
+ if (event_log.type == PACKET_RECEIVED) {
+ event_log.packet_id = delay_delta_or_packet_id;
+ } else {
+ event_log.delay_delta = base::TimeDelta::FromMilliseconds(
+ delay_delta_or_packet_id);
+ }
+ frame_log.event_log_messages_.push_back(event_log);
}
- EndCurrentBlock();
- return false;
- } else {
- DCHECK(false) << "Invalid state";
- EndCurrentBlock();
- return false;
- }
-}
-
-bool RtcpParser::ParsePayloadSpecificAppItem() {
- ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
-
- if (length < 4) {
- state_ = kStateTopLevel;
- EndCurrentBlock();
- return false;
- }
- uint32 name;
- base::BigEndianReader big_endian_reader(
- reinterpret_cast<const char*>(rtcp_data_), length);
- big_endian_reader.ReadU32(&name);
- rtcp_data_ += 4;
-
- if (name == kCast) {
- field_type_ = kRtcpPayloadSpecificCastCode;
- state_ = kStatePayloadSpecificCast;
- return true;
+ receiver_log_.push_back(frame_log);
}
- state_ = kStateTopLevel;
- EndCurrentBlock();
- return false;
-}
-bool RtcpParser::ParsePayloadSpecificCastItem() {
- ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
- if (length < 4) {
- state_ = kStateTopLevel;
- EndCurrentBlock();
- return false;
- }
- field_type_ = kRtcpPayloadSpecificCastCode;
-
- 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;
-
- if (field_.cast_item.number_of_lost_fields != 0) {
- // State transition
- state_ = kStatePayloadSpecificCastNack;
- } else {
- // Don't go to state cast nack item if got 0 fields.
- state_ = kStateTopLevel;
- EndCurrentBlock();
- }
return true;
}
-bool RtcpParser::ParsePayloadSpecificCastNackItem() {
- ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
- if (length < 4) {
- state_ = kStateTopLevel;
- EndCurrentBlock();
- return false;
+// RFC 4585.
+bool RtcpParser::ParseFeedbackCommon(base::BigEndianReader* reader,
+ const RtcpCommonHeader& header) {
+ // See RTC 4585 Section 6.4 for application specific feedback messages.
+ if (header.IC != 15) {
+ return true;
}
- field_type_ = kRtcpPayloadSpecificCastNackItemCode;
-
- 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);
-
- rtcp_data_ += 4;
- return true;
-}
-
-bool RtcpParser::ParseExtendedReport() {
- ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
- if (length < 8)
+ uint32 remote_ssrc;
+ uint32 media_ssrc;
+ if (!reader->ReadU32(&remote_ssrc) ||
+ !reader->ReadU32(&media_ssrc))
return false;
- field_type_ = kRtcpXrCode;
+ if (remote_ssrc != remote_ssrc_)
+ return true;
- 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);
+ uint32 name;
+ if (!reader->ReadU32(&name))
+ return false;
- rtcp_data_ += 8;
+ if (name != kCast) {
+ return true;
+ }
- state_ = kStateExtendedReportBlock;
- return true;
-}
+ cast_message_.media_ssrc = remote_ssrc;
-bool RtcpParser::ParseExtendedReportItem() {
- ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
- if (length < 4) {
- state_ = kStateTopLevel;
- EndCurrentBlock();
+ uint8 last_frame_id;
+ uint8 number_of_lost_fields;
+ if (!reader->ReadU8(&last_frame_id) ||
+ !reader->ReadU8(&number_of_lost_fields) ||
+ !reader->ReadU16(&cast_message_.target_delay_ms))
return false;
- }
- uint8 block_type;
- uint16 block_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);
-
- rtcp_data_ += 4;
-
- switch (block_type) {
- case 4: // RRTR. RFC3611 Section 4.4.
- if (block_length != 2) {
- // Invalid block length.
- state_ = kStateTopLevel;
- EndCurrentBlock();
- return false;
- }
- return ParseExtendedReportReceiverReferenceTimeReport();
- case 5: // DLRR. RFC3611 Section 4.5.
- if (block_length % 3 != 0) {
- // Invalid block length.
- state_ = kStateTopLevel;
- EndCurrentBlock();
- return false;
- }
- if (block_length >= 3) {
- number_of_blocks_ = block_length / 3;
- state_ = kStateExtendedReportDelaySinceLastReceiverReport;
- return ParseExtendedReportDelaySinceLastReceiverReport();
- }
- return true;
- default:
- if (length < block_length * 4) {
- state_ = kStateTopLevel;
- EndCurrentBlock();
- return false;
+ // Please note, this frame_id is still only 8-bit!
+ cast_message_.ack_frame_id = last_frame_id;
+
+ for (size_t i = 0; i < number_of_lost_fields; i++) {
+ uint8 frame_id;
+ uint16 packet_id;
+ uint8 bitmask;
+ if (!reader->ReadU8(&frame_id) ||
+ !reader->ReadU16(&packet_id) ||
+ !reader->ReadU8(&bitmask))
+ return false;
+ cast_message_.missing_frames_and_packets[frame_id].insert(packet_id);
+ if (packet_id != kRtcpCastAllPacketsLost) {
+ while (bitmask) {
+ packet_id++;
+ if (bitmask & 1)
+ cast_message_.missing_frames_and_packets[frame_id].insert(packet_id);
+ bitmask >>= 1;
}
- field_type_ = kRtcpXrUnknownItemCode;
- rtcp_data_ += block_length * 4;
- return true;
+ }
}
+
+ has_cast_message_ = true;
+ return true;
}
-bool RtcpParser::ParseExtendedReportReceiverReferenceTimeReport() {
- ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
- if (length < 8) {
- state_ = kStateTopLevel;
- EndCurrentBlock();
+bool RtcpParser::ParseExtendedReport(base::BigEndianReader* reader,
+ const RtcpCommonHeader& header) {
+ uint32 remote_ssrc;
+ if (!reader->ReadU32(&remote_ssrc))
return false;
- }
- 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);
+ // Is it for us?
+ if (remote_ssrc != remote_ssrc_)
+ return true;
+
+ while (reader->remaining()) {
+ uint8 block_type;
+ uint16 block_length;
+ if (!reader->ReadU8(&block_type) ||
+ !reader->Skip(1) ||
+ !reader->ReadU16(&block_length))
+ return false;
+
+ switch (block_type) {
+ case 4: // RRTR. RFC3611 Section 4.4.
+ if (block_length != 2)
+ return false;
+ if (!ParseExtendedReportReceiverReferenceTimeReport(reader,
+ remote_ssrc))
+ return false;
+ break;
- rtcp_data_ += 8;
+ default:
+ // Skip unknown item.
+ if (!reader->Skip(block_length * 4))
+ return false;
+ }
+ }
- field_type_ = kRtcpXrRrtrCode;
return true;
}
-bool RtcpParser::ParseExtendedReportDelaySinceLastReceiverReport() {
- ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
- if (length < 12) {
- state_ = kStateTopLevel;
- EndCurrentBlock();
+bool RtcpParser::ParseExtendedReportReceiverReferenceTimeReport(
+ base::BigEndianReader* reader,
+ uint32 remote_ssrc) {
+ receiver_reference_time_report_.remote_ssrc = remote_ssrc;
+ if(!reader->ReadU32(&receiver_reference_time_report_.ntp_seconds) ||
+ !reader->ReadU32(&receiver_reference_time_report_.ntp_fraction))
return false;
- }
- if (number_of_blocks_ == 0) {
- // Continue parsing the extended report block.
- state_ = kStateExtendedReportBlock;
- return false;
- }
-
- 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);
-
- rtcp_data_ += 12;
- number_of_blocks_--;
- field_type_ = kRtcpXrDlrrCode;
+ has_receiver_reference_time_report_ = true;
return true;
}
@@ -692,7 +377,6 @@ CastLoggingEvent TranslateToLogEventFromWireFormat(uint8 event) {
// 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;
}
}
diff --git a/media/cast/net/rtcp/rtcp_utility.h b/media/cast/net/rtcp/rtcp_utility.h
index a20fd8009c..8fd8edcd6a 100644
--- a/media/cast/net/rtcp/rtcp_utility.h
+++ b/media/cast/net/rtcp/rtcp_utility.h
@@ -5,6 +5,7 @@
#ifndef MEDIA_CAST_RTCP_RTCP_UTILITY_H_
#define MEDIA_CAST_RTCP_RTCP_UTILITY_H_
+#include "base/big_endian.h"
#include "media/cast/cast_config.h"
#include "media/cast/cast_defines.h"
#include "media/cast/logging/logging_defines.h"
@@ -23,203 +24,85 @@ static const uint8 kReceiverLogSubtype = 2;
static const size_t kRtcpMaxReceiverLogMessages = 256;
static const size_t kRtcpMaxCastLossFields = 100;
-struct RtcpFieldReceiverReport {
- // RFC 3550.
- uint32 sender_ssrc;
- uint8 number_of_report_blocks;
-};
-
-struct RtcpFieldSenderReport {
- // RFC 3550.
- uint32 sender_ssrc;
- uint8 number_of_report_blocks;
- uint32 ntp_most_significant;
- uint32 ntp_least_significant;
- uint32 rtp_timestamp;
- uint32 sender_packet_count;
- uint32 sender_octet_count;
-};
-
-struct RtcpFieldReportBlockItem {
- // RFC 3550.
- uint32 ssrc;
- uint8 fraction_lost;
- uint32 cumulative_number_of_packets_lost;
- uint32 extended_highest_sequence_number;
- uint32 jitter;
- uint32 last_sender_report;
- uint32 delay_last_sender_report;
-};
-
-struct RtcpFieldXr {
- // RFC 3611.
- uint32 sender_ssrc;
-};
-
-struct RtcpFieldXrRrtr {
- // RFC 3611.
- uint32 ntp_most_significant;
- uint32 ntp_least_significant;
-};
-
-struct RtcpFieldXrDlrr {
- // RFC 3611.
- uint32 receivers_ssrc;
- uint32 last_receiver_report;
- uint32 delay_last_receiver_report;
-};
-
-struct RtcpFieldPayloadSpecificApplication {
- uint32 sender_ssrc;
- uint32 media_ssrc;
-};
-
-struct RtcpFieldPayloadSpecificCastItem {
- uint8 last_frame_id;
- uint8 number_of_lost_fields;
- uint16 target_delay_ms;
-};
-
-struct RtcpFieldPayloadSpecificCastNackItem {
- uint8 frame_id;
- uint16 packet_id;
- uint8 bitmask;
-};
-
-struct RtcpFieldApplicationSpecificCastReceiverLogItem {
- uint32 sender_ssrc;
- uint32 rtp_timestamp;
- uint32 event_timestamp_base;
- uint8 event;
- union {
- uint16 packet_id;
- int16 delay_delta;
- } delay_delta_or_packet_id;
- uint16 event_timestamp_delta;
-};
-
-union RtcpField {
- RtcpFieldReceiverReport receiver_report;
- RtcpFieldSenderReport sender_report;
- RtcpFieldReportBlockItem report_block_item;
-
- RtcpFieldXr extended_report;
- RtcpFieldXrRrtr rrtr;
- RtcpFieldXrDlrr dlrr;
-
- RtcpFieldPayloadSpecificApplication application_specific;
- RtcpFieldPayloadSpecificCastItem cast_item;
- RtcpFieldPayloadSpecificCastNackItem cast_nack_item;
-
- RtcpFieldApplicationSpecificCastReceiverLogItem cast_receiver_log;
-};
-
-enum RtcpFieldTypes {
- kRtcpNotValidCode,
-
- // RFC 3550.
- kRtcpRrCode,
- kRtcpSrCode,
- kRtcpReportBlockItemCode,
-
- // RFC 3611.
- kRtcpXrCode,
- kRtcpXrRrtrCode,
- kRtcpXrDlrrCode,
- kRtcpXrUnknownItemCode,
-
- // RFC 4585.
- kRtcpPayloadSpecificAppCode,
-
- // Application specific.
- kRtcpPayloadSpecificCastCode,
- kRtcpPayloadSpecificCastNackItemCode,
- kRtcpApplicationSpecificCastReceiverLogCode,
- kRtcpApplicationSpecificCastReceiverLogFrameCode,
- kRtcpApplicationSpecificCastReceiverLogEventCode,
-};
-
struct RtcpCommonHeader {
uint8 V; // Version.
bool P; // Padding.
uint8 IC; // Item count / subtype.
uint8 PT; // Packet Type.
- uint16 length_in_octets;
+ size_t length_in_octets;
};
class RtcpParser {
public:
- RtcpParser(const uint8* rtcp_data, size_t rtcp_length);
+ RtcpParser(uint32 local_ssrc, uint32 remote_ssrc);
~RtcpParser();
- RtcpFieldTypes FieldType() const;
- const RtcpField& Field() const;
+ bool Parse(base::BigEndianReader* reader);
- bool IsValid() const;
+ bool has_sender_report() const { return has_sender_report_; }
+ const RtcpSenderInfo& sender_report() const {
+ return sender_report_;
+ }
- RtcpFieldTypes Begin();
- RtcpFieldTypes Iterate();
+ bool has_last_report() const { return has_last_report_; }
+ uint32 last_report() const { return last_report_; }
+ uint32 delay_since_last_report() const { return delay_since_last_report_; }
- private:
- enum ParseState {
- kStateTopLevel, // Top level packet
- kStateReportBlock, // Sender/Receiver report report blocks.
- kStateApplicationSpecificCastReceiverFrameLog,
- kStateApplicationSpecificCastReceiverEventLog,
- kStateExtendedReportBlock,
- kStateExtendedReportDelaySinceLastReceiverReport,
- kStatePayloadSpecificApplication,
- kStatePayloadSpecificCast, // Application specific Cast.
- kStatePayloadSpecificCastNack, // Application specific Nack for Cast.
- };
-
- bool RtcpParseCommonHeader(const uint8* begin,
- const uint8* end,
- RtcpCommonHeader* parsed_header) const;
-
- void IterateTopLevel();
- void IterateReportBlockItem();
- void IterateCastReceiverLogFrame();
- void IterateCastReceiverLogEvent();
- void IterateExtendedReportItem();
- void IterateExtendedReportDelaySinceLastReceiverReportItem();
- void IteratePayloadSpecificAppItem();
- void IteratePayloadSpecificCastItem();
- void IteratePayloadSpecificCastNackItem();
-
- void Validate();
- void EndCurrentBlock();
-
- bool ParseRR();
- bool ParseSR();
- bool ParseReportBlockItem();
-
- bool ParseApplicationDefined(uint8 subtype);
- bool ParseCastReceiverLogFrameItem();
- bool ParseCastReceiverLogEventItem();
-
- bool ParseExtendedReport();
- bool ParseExtendedReportItem();
- bool ParseExtendedReportReceiverReferenceTimeReport();
- bool ParseExtendedReportDelaySinceLastReceiverReport();
-
- bool ParseFeedBackCommon(const RtcpCommonHeader& header);
- bool ParsePayloadSpecificAppItem();
- bool ParsePayloadSpecificCastItem();
- bool ParsePayloadSpecificCastNackItem();
+ bool has_receiver_log() const { return !receiver_log_.empty(); }
+ const RtcpReceiverLogMessage& receiver_log() const { return receiver_log_; }
+ RtcpReceiverLogMessage* mutable_receiver_log() { return & receiver_log_; }
- private:
- const uint8* const rtcp_data_begin_;
- const uint8* const rtcp_data_end_;
+ bool has_cast_message() const { return has_cast_message_; }
+ const RtcpCastMessage& cast_message() const { return cast_message_; }
+ RtcpCastMessage* mutable_cast_message() { return &cast_message_; }
- bool valid_packet_;
- const uint8* rtcp_data_;
- const uint8* rtcp_block_end_;
+ bool has_receiver_reference_time_report() const {
+ return has_receiver_reference_time_report_;
+ }
+ const RtcpReceiverReferenceTimeReport&
+ receiver_reference_time_report() const {
+ return receiver_reference_time_report_;
+ }
- ParseState state_;
- uint8 number_of_blocks_;
- RtcpFieldTypes field_type_;
- RtcpField field_;
+ private:
+ bool ParseCommonHeader(base::BigEndianReader* reader,
+ RtcpCommonHeader* parsed_header);
+ bool ParseSR(base::BigEndianReader* reader,
+ const RtcpCommonHeader& header);
+ bool ParseRR(base::BigEndianReader* reader,
+ const RtcpCommonHeader& header);
+ bool ParseReportBlock(base::BigEndianReader* reader);
+ bool ParseApplicationDefined(base::BigEndianReader* reader,
+ const RtcpCommonHeader& header);
+ bool ParseCastReceiverLogFrameItem(base::BigEndianReader* reader);
+ bool ParseFeedbackCommon(base::BigEndianReader* reader,
+ const RtcpCommonHeader& header);
+ bool ParseExtendedReport(base::BigEndianReader* reader,
+ const RtcpCommonHeader& header);
+ bool ParseExtendedReportReceiverReferenceTimeReport(
+ base::BigEndianReader* reader,
+ uint32 remote_ssrc);
+ bool ParseExtendedReportDelaySinceLastReceiverReport(
+ base::BigEndianReader* reader);
+
+ uint32 local_ssrc_;
+ uint32 remote_ssrc_;
+
+ bool has_sender_report_;
+ RtcpSenderInfo sender_report_;
+
+ uint32 last_report_;
+ uint32 delay_since_last_report_;
+ bool has_last_report_;
+
+ // |receiver_log_| is a vector vector, no need for has_*.
+ RtcpReceiverLogMessage receiver_log_;
+
+ bool has_cast_message_;
+ RtcpCastMessage cast_message_;
+
+ bool has_receiver_reference_time_report_;
+ RtcpReceiverReferenceTimeReport receiver_reference_time_report_;
DISALLOW_COPY_AND_ASSIGN(RtcpParser);
};
diff --git a/media/cast/net/rtcp/rtcp_utility_unittest.cc b/media/cast/net/rtcp/rtcp_utility_unittest.cc
new file mode 100644
index 0000000000..ed2ab8d663
--- /dev/null
+++ b/media/cast/net/rtcp/rtcp_utility_unittest.cc
@@ -0,0 +1,402 @@
+// 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/scoped_ptr.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "media/cast/cast_environment.h"
+#include "media/cast/net/cast_transport_defines.h"
+#include "media/cast/net/rtcp/rtcp_utility.h"
+#include "media/cast/net/rtcp/test_rtcp_packet_builder.h"
+#include "media/cast/test/fake_single_thread_task_runner.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+namespace cast {
+
+static const uint32 kSenderSsrc = 0x10203;
+static const uint32 kSourceSsrc = 0x40506;
+static const uint32 kUnknownSsrc = 0xDEAD;
+static const base::TimeDelta kTargetDelay =
+ base::TimeDelta::FromMilliseconds(100);
+
+class RtcpParserTest : public ::testing::Test {
+ protected:
+ RtcpParserTest()
+ : testing_clock_(new base::SimpleTestTickClock()),
+ task_runner_(new test::FakeSingleThreadTaskRunner(
+ testing_clock_.get())) {
+ }
+
+ bool HasAnything(const RtcpParser& parser) {
+ return parser.has_sender_report() ||
+ parser.has_last_report() ||
+ parser.has_receiver_log() ||
+ parser.has_cast_message() ||
+ parser.has_receiver_reference_time_report();
+ }
+
+ void ExpectSenderInfo(const RtcpParser& parser) {
+ EXPECT_TRUE(parser.has_sender_report());
+ EXPECT_EQ(kNtpHigh, parser.sender_report().ntp_seconds);
+ EXPECT_EQ(kNtpLow, parser.sender_report().ntp_fraction);
+ EXPECT_EQ(kRtpTimestamp, parser.sender_report().rtp_timestamp);
+ EXPECT_EQ(kSendPacketCount, parser.sender_report().send_packet_count);
+ EXPECT_EQ(kSendOctetCount, parser.sender_report().send_octet_count);
+ }
+
+ void ExpectLastReport(const RtcpParser& parser) {
+ EXPECT_TRUE(parser.has_last_report());
+ EXPECT_EQ(kLastSr, parser.last_report());
+ EXPECT_EQ(kDelayLastSr, parser.delay_since_last_report());
+ }
+
+ void ExpectReceiverReference(const RtcpParser& parser) {
+ EXPECT_TRUE(parser.has_receiver_reference_time_report());
+ EXPECT_EQ(kSenderSsrc, parser.receiver_reference_time_report().remote_ssrc);
+ EXPECT_EQ(kNtpHigh, parser.receiver_reference_time_report().ntp_seconds);
+ EXPECT_EQ(kNtpLow, parser.receiver_reference_time_report().ntp_fraction);
+ }
+
+ void ExpectCastFeedback(const RtcpParser& parser) {
+ EXPECT_TRUE(parser.has_cast_message());
+ EXPECT_EQ(kSenderSsrc, parser.cast_message().media_ssrc);
+ EXPECT_EQ(kAckFrameId, parser.cast_message().ack_frame_id);
+
+ MissingFramesAndPacketsMap::const_iterator frame_it =
+ parser.cast_message().missing_frames_and_packets.begin();
+
+ EXPECT_TRUE(
+ frame_it != parser.cast_message().missing_frames_and_packets.end());
+ EXPECT_EQ(kLostFrameId, frame_it->first);
+ EXPECT_EQ(frame_it->second.size(), 1UL);
+ EXPECT_EQ(*frame_it->second.begin(), kRtcpCastAllPacketsLost);
+ ++frame_it;
+ EXPECT_TRUE(
+ frame_it != parser.cast_message().missing_frames_and_packets.end());
+ EXPECT_EQ(kFrameIdWithLostPackets, frame_it->first);
+ EXPECT_EQ(3UL, frame_it->second.size());
+ PacketIdSet::const_iterator packet_it = frame_it->second.begin();
+ EXPECT_EQ(kLostPacketId1, *packet_it);
+ ++packet_it;
+ EXPECT_EQ(kLostPacketId2, *packet_it);
+ ++packet_it;
+ EXPECT_EQ(kLostPacketId3, *packet_it);
+ ++frame_it;
+ EXPECT_TRUE(
+ frame_it == parser.cast_message().missing_frames_and_packets.end());
+ }
+
+ void ExpectReceiverLog(const RtcpParser& parser,
+ const RtcpReceiverLogMessage& expected_receiver_log) {
+ EXPECT_TRUE(parser.has_receiver_log());
+ EXPECT_EQ(expected_receiver_log.size(), parser.receiver_log().size());
+ RtcpReceiverLogMessage::const_iterator expected_it =
+ expected_receiver_log.begin();
+ RtcpReceiverLogMessage::const_iterator incoming_it =
+ parser.receiver_log().begin();
+ for (; incoming_it != parser.receiver_log().end();
+ ++incoming_it, ++expected_it) {
+ EXPECT_EQ(expected_it->rtp_timestamp_, incoming_it->rtp_timestamp_);
+ EXPECT_EQ(expected_it->event_log_messages_.size(),
+ incoming_it->event_log_messages_.size());
+
+ RtcpReceiverEventLogMessages::const_iterator event_incoming_it =
+ incoming_it->event_log_messages_.begin();
+ RtcpReceiverEventLogMessages::const_iterator event_expected_it =
+ expected_it->event_log_messages_.begin();
+ for (; event_incoming_it != incoming_it->event_log_messages_.end();
+ ++event_incoming_it, ++event_expected_it) {
+ 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 == PACKET_RECEIVED) {
+ EXPECT_EQ(event_expected_it->packet_id, event_incoming_it->packet_id);
+ } else {
+ EXPECT_EQ(event_expected_it->delay_delta,
+ event_incoming_it->delay_delta);
+ }
+ }
+ }
+ }
+
+ scoped_ptr<base::SimpleTestTickClock> testing_clock_;
+ scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(RtcpParserTest);
+};
+
+TEST_F(RtcpParserTest, BrokenPacketIsIgnored) {
+ const char bad_packet[] = {0, 0, 0, 0};
+ RtcpParser parser(kSourceSsrc, kSenderSsrc);
+ base::BigEndianReader reader(bad_packet, sizeof(bad_packet));
+ EXPECT_FALSE(parser.Parse(&reader));
+}
+
+TEST_F(RtcpParserTest, UnknownBlockIgnored) {
+ // Only unknown data, nothing happens.
+ TestRtcpPacketBuilder p;
+ p.AddUnknownBlock();
+ RtcpParser parser1(kSourceSsrc, 0);
+ EXPECT_TRUE(parser1.Parse(p.Reader()));
+ EXPECT_FALSE(HasAnything(parser1));
+
+ // Add valid sender report *after* unknown data - should work fine.
+ p.AddSr(kSenderSsrc, 0);
+ RtcpParser parser2(kSourceSsrc, kSenderSsrc);
+ EXPECT_TRUE(parser2.Parse(p.Reader()));
+ ExpectSenderInfo(parser2);
+}
+
+TEST_F(RtcpParserTest, InjectSenderReportPacket) {
+ TestRtcpPacketBuilder p;
+ p.AddSr(kSenderSsrc, 0);
+
+ // Expected to be ignored since the sender ssrc does not match our
+ // remote ssrc.
+ RtcpParser parser1(kSourceSsrc, 0);
+ EXPECT_TRUE(parser1.Parse(p.Reader()));
+ EXPECT_FALSE(HasAnything(parser1));
+
+ // Expected to be pass through since the sender ssrc match our remote ssrc.
+ RtcpParser parser2(kSourceSsrc, kSenderSsrc);
+ EXPECT_TRUE(parser2.Parse(p.Reader()));
+ ExpectSenderInfo(parser2);
+}
+
+TEST_F(RtcpParserTest, InjectReceiveReportPacket) {
+ TestRtcpPacketBuilder p1;
+ p1.AddRr(kSenderSsrc, 1);
+ p1.AddRb(kUnknownSsrc);
+
+ // Expected to be ignored since the source ssrc does not match our
+ // local ssrc.
+ RtcpParser parser1(kSourceSsrc, kSenderSsrc);
+ EXPECT_TRUE(parser1.Parse(p1.Reader()));
+ EXPECT_FALSE(HasAnything(parser1));
+
+ TestRtcpPacketBuilder p2;
+ p2.AddRr(kSenderSsrc, 1);
+ p2.AddRb(kSourceSsrc);
+
+ // Expected to be pass through since the sender ssrc match our local ssrc.
+ RtcpParser parser2(kSourceSsrc, kSenderSsrc);
+ EXPECT_TRUE(parser2.Parse(p2.Reader()));
+ ExpectLastReport(parser2);
+}
+
+TEST_F(RtcpParserTest, InjectSenderReportWithReportBlockPacket) {
+ TestRtcpPacketBuilder p1;
+ p1.AddSr(kSenderSsrc, 1);
+ p1.AddRb(kUnknownSsrc);
+
+ // Sender report expected to be ignored since the sender ssrc does not match
+ // our remote ssrc.
+ // Report block expected to be ignored since the source ssrc does not match
+ // our local ssrc.
+ RtcpParser parser1(kSourceSsrc, 0);
+ EXPECT_TRUE(parser1.Parse(p1.Reader()));
+ EXPECT_FALSE(HasAnything(parser1));
+
+ // Sender report expected to be pass through since the sender ssrc match our
+ // remote ssrc.
+ // Report block expected to be ignored since the source ssrc does not match
+ // our local ssrc.
+ RtcpParser parser2(kSourceSsrc, kSenderSsrc);
+ EXPECT_TRUE(parser2.Parse(p1.Reader()));
+ ExpectSenderInfo(parser2);
+ EXPECT_FALSE(parser2.has_last_report());
+
+ // Sender report expected to be ignored since the sender ssrc does not match
+ // our remote ssrc.
+ // Report block expected to be ignored too since it's a part of the
+ // sender report.
+ TestRtcpPacketBuilder p2;
+ p2.AddSr(kSenderSsrc, 1);
+ p2.AddRb(kSourceSsrc);
+
+ RtcpParser parser3(kSourceSsrc, 0);
+ EXPECT_TRUE(parser3.Parse(p2.Reader()));
+ EXPECT_FALSE(parser3.has_last_report());
+
+ // Sender report expected to be pass through since the sender ssrc match our
+ // remote ssrc.
+ // Report block expected to be pass through since the sender ssrc match
+ // our local ssrc.
+ RtcpParser parser4(kSourceSsrc, kSenderSsrc);
+ EXPECT_TRUE(parser4.Parse(p2.Reader()));
+ ExpectSenderInfo(parser4);
+ ExpectLastReport(parser4);
+}
+
+TEST_F(RtcpParserTest, InjectSenderReportPacketWithDlrr) {
+ TestRtcpPacketBuilder p;
+ p.AddSr(kSenderSsrc, 0);
+ p.AddXrHeader(kSenderSsrc);
+ p.AddXrUnknownBlock();
+ p.AddXrExtendedDlrrBlock(kSenderSsrc);
+ p.AddXrUnknownBlock();
+
+ // Expected to be ignored since the source ssrc does not match our
+ // local ssrc.
+ RtcpParser parser1(kSourceSsrc, 0);
+ EXPECT_TRUE(parser1.Parse(p.Reader()));
+ EXPECT_FALSE(HasAnything(parser1));
+
+ // Expected to be pass through since the sender ssrc match our local ssrc.
+ RtcpParser parser2(kSourceSsrc, kSenderSsrc);
+ EXPECT_TRUE(parser2.Parse(p.Reader()));
+ ExpectSenderInfo(parser2);
+ // DLRRs are ignored.
+ EXPECT_FALSE(parser2.has_last_report());
+}
+
+TEST_F(RtcpParserTest, InjectReceiverReportPacketWithRrtr) {
+ TestRtcpPacketBuilder p1;
+ p1.AddRr(kSenderSsrc, 1);
+ p1.AddRb(kUnknownSsrc);
+ p1.AddXrHeader(kSenderSsrc);
+ p1.AddXrRrtrBlock();
+
+ // Expected to be ignored since the source ssrc does not match our
+ // local ssrc.
+ RtcpParser parser1(kSourceSsrc, 0);
+ EXPECT_TRUE(parser1.Parse(p1.Reader()));
+ EXPECT_FALSE(HasAnything(parser1));
+
+ TestRtcpPacketBuilder p2;
+ p2.AddRr(kSenderSsrc, 1);
+ p2.AddRb(kSourceSsrc);
+ p2.AddXrHeader(kSenderSsrc);
+ p2.AddXrRrtrBlock();
+
+ // Expected to be pass through since the sender ssrc match our local ssrc.
+ RtcpParser parser2(kSourceSsrc, kSenderSsrc);
+ EXPECT_TRUE(parser2.Parse(p2.Reader()));
+ ExpectLastReport(parser2);
+ ExpectReceiverReference(parser2);
+}
+
+TEST_F(RtcpParserTest, InjectReceiverReportPacketWithIntraFrameRequest) {
+ TestRtcpPacketBuilder p1;
+ p1.AddRr(kSenderSsrc, 1);
+ p1.AddRb(kUnknownSsrc);
+
+ // Expected to be ignored since the source ssrc does not match our
+ // local ssrc.
+ RtcpParser parser1(kSourceSsrc, 0);
+ EXPECT_TRUE(parser1.Parse(p1.Reader()));
+ EXPECT_FALSE(HasAnything(parser1));
+
+ TestRtcpPacketBuilder p2;
+ p2.AddRr(kSenderSsrc, 1);
+ p2.AddRb(kSourceSsrc);
+
+ RtcpParser parser2(kSourceSsrc, kSenderSsrc);
+ EXPECT_TRUE(parser2.Parse(p2.Reader()));
+ ExpectLastReport(parser2);
+}
+
+TEST_F(RtcpParserTest, InjectReceiverReportPacketWithCastFeedback) {
+ TestRtcpPacketBuilder p1;
+ p1.AddRr(kSenderSsrc, 1);
+ p1.AddRb(kUnknownSsrc);
+ p1.AddCast(kSenderSsrc, kUnknownSsrc, kTargetDelay);
+
+ // Expected to be ignored since the source ssrc does not match our
+ // local ssrc.
+ RtcpParser parser1(kSourceSsrc, 0);
+ EXPECT_TRUE(parser1.Parse(p1.Reader()));
+ EXPECT_FALSE(HasAnything(parser1));
+
+ TestRtcpPacketBuilder p2;
+ p2.AddRr(kSenderSsrc, 1);
+ p2.AddRb(kSourceSsrc);
+ p2.AddCast(kSenderSsrc, kSourceSsrc, kTargetDelay);
+
+ // Expected to be pass through since the sender ssrc match our local ssrc.
+ RtcpParser parser2(kSourceSsrc, kSenderSsrc);
+ EXPECT_TRUE(parser2.Parse(p2.Reader()));
+ ExpectLastReport(parser2);
+ ExpectCastFeedback(parser2);
+}
+
+TEST_F(RtcpParserTest, InjectReceiverReportWithReceiverLogVerificationBase) {
+ static const uint32 kTimeBaseMs = 12345678;
+ static const uint32 kTimeDelayMs = 10;
+ static const uint32 kDelayDeltaMs = 123;
+ base::SimpleTestTickClock testing_clock;
+ testing_clock.Advance(base::TimeDelta::FromMilliseconds(kTimeBaseMs));
+
+ RtcpReceiverLogMessage receiver_log;
+ RtcpReceiverFrameLogMessage frame_log(kRtpTimestamp);
+ RtcpReceiverEventLogMessage event_log;
+
+ 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 = 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);
+
+ TestRtcpPacketBuilder p;
+ p.AddRr(kSenderSsrc, 1);
+ p.AddRb(kSourceSsrc);
+ p.AddReceiverLog(kSenderSsrc);
+ p.AddReceiverFrameLog(kRtpTimestamp, 3, kTimeBaseMs);
+ p.AddReceiverEventLog(kDelayDeltaMs, FRAME_ACK_SENT, 0);
+ p.AddReceiverEventLog(kLostPacketId1, PACKET_RECEIVED, kTimeDelayMs);
+ p.AddReceiverEventLog(kLostPacketId2, PACKET_RECEIVED, kTimeDelayMs);
+
+ RtcpParser parser(kSourceSsrc, kSenderSsrc);
+ EXPECT_TRUE(parser.Parse(p.Reader()));
+ ExpectReceiverLog(parser, receiver_log);
+}
+
+TEST_F(RtcpParserTest, InjectReceiverReportWithReceiverLogVerificationMulti) {
+ static const uint32 kTimeBaseMs = 12345678;
+ static const uint32 kTimeDelayMs = 10;
+ static const uint32 kDelayDeltaMs = 123;
+ base::SimpleTestTickClock testing_clock;
+ testing_clock.Advance(base::TimeDelta::FromMilliseconds(kTimeBaseMs));
+
+ RtcpReceiverLogMessage receiver_log;
+
+ for (int j = 0; j < 100; ++j) {
+ RtcpReceiverFrameLogMessage frame_log(kRtpTimestamp);
+ RtcpReceiverEventLogMessage event_log;
+ 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);
+ receiver_log.push_back(frame_log);
+ testing_clock.Advance(base::TimeDelta::FromMilliseconds(kTimeDelayMs));
+ }
+
+ TestRtcpPacketBuilder p;
+ p.AddRr(kSenderSsrc, 1);
+ p.AddRb(kSourceSsrc);
+ p.AddReceiverLog(kSenderSsrc);
+ for (int i = 0; i < 100; ++i) {
+ p.AddReceiverFrameLog(kRtpTimestamp, 1, kTimeBaseMs + i * kTimeDelayMs);
+ p.AddReceiverEventLog(kDelayDeltaMs, FRAME_ACK_SENT, 0);
+ }
+
+ RtcpParser parser(kSourceSsrc, kSenderSsrc);
+ EXPECT_TRUE(parser.Parse(p.Reader()));
+ ExpectReceiverLog(parser, receiver_log);
+}
+
+} // namespace cast
+} // namespace media
diff --git a/media/cast/net/rtcp/test_rtcp_packet_builder.cc b/media/cast/net/rtcp/test_rtcp_packet_builder.cc
index 046cc0446d..32e1883f45 100644
--- a/media/cast/net/rtcp/test_rtcp_packet_builder.cc
+++ b/media/cast/net/rtcp/test_rtcp_packet_builder.cc
@@ -12,7 +12,8 @@ namespace cast {
TestRtcpPacketBuilder::TestRtcpPacketBuilder()
: ptr_of_length_(NULL),
- big_endian_writer_(reinterpret_cast<char*>(buffer_), kMaxIpPacketSize) {}
+ big_endian_writer_(reinterpret_cast<char*>(buffer_), kMaxIpPacketSize),
+ big_endian_reader_(NULL, 0) {}
void TestRtcpPacketBuilder::AddSr(uint32 sender_ssrc,
int number_of_report_blocks) {
@@ -69,6 +70,13 @@ void TestRtcpPacketBuilder::AddXrUnknownBlock() {
big_endian_writer_.WriteU32(0);
}
+void TestRtcpPacketBuilder::AddUnknownBlock() {
+ AddRtcpHeader(99, 0);
+ big_endian_writer_.WriteU32(42);
+ big_endian_writer_.WriteU32(42);
+ big_endian_writer_.WriteU32(42);
+}
+
void TestRtcpPacketBuilder::AddXrDlrrBlock(uint32 sender_ssrc) {
big_endian_writer_.WriteU8(5); // Block type.
big_endian_writer_.WriteU8(0); // Reserved.
@@ -184,6 +192,12 @@ const uint8* TestRtcpPacketBuilder::Data() {
return buffer_;
}
+base::BigEndianReader* TestRtcpPacketBuilder::Reader() {
+ big_endian_reader_ = base::BigEndianReader(
+ reinterpret_cast<const char *>(Data()), Length());
+ return &big_endian_reader_;
+}
+
void TestRtcpPacketBuilder::PatchLengthField() {
if (ptr_of_length_) {
// Back-patch the packet length. The client must have taken
diff --git a/media/cast/net/rtcp/test_rtcp_packet_builder.h b/media/cast/net/rtcp/test_rtcp_packet_builder.h
index afbf4c6253..3fab3b5069 100644
--- a/media/cast/net/rtcp/test_rtcp_packet_builder.h
+++ b/media/cast/net/rtcp/test_rtcp_packet_builder.h
@@ -19,18 +19,18 @@ namespace cast {
namespace {
// Sender report.
-static const int kNtpHigh = 0x01020304;
-static const int kNtpLow = 0x05060708;
-static const int kRtpTimestamp = 0x10203040;
-static const int kSendPacketCount = 987;
-static const int kSendOctetCount = 87654;
+static const uint32 kNtpHigh = 0x01020304;
+static const uint32 kNtpLow = 0x05060708;
+static const uint32 kRtpTimestamp = 0x10203040;
+static const uint32 kSendPacketCount = 987;
+static const uint32 kSendOctetCount = 87654;
// Report block.
static const int kLoss = 0x01000123;
static const int kExtendedMax = 0x15678;
static const int kTestJitter = 0x10203;
-static const int kLastSr = 0x34561234;
-static const int kDelayLastSr = 1000;
+static const uint32 kLastSr = 0x34561234;
+static const uint32 kDelayLastSr = 1000;
// DLRR block.
static const int kLastRr = 0x34561234;
@@ -65,6 +65,7 @@ class TestRtcpPacketBuilder {
void AddXrExtendedDlrrBlock(uint32 sender_ssrc);
void AddXrRrtrBlock();
void AddXrUnknownBlock();
+ void AddUnknownBlock();
void AddNack(uint32 sender_ssrc, uint32 media_ssrc);
void AddSendReportRequest(uint32 sender_ssrc, uint32 media_ssrc);
@@ -83,6 +84,7 @@ class TestRtcpPacketBuilder {
scoped_ptr<Packet> GetPacket();
const uint8* Data();
int Length() { return kMaxIpPacketSize - big_endian_writer_.remaining(); }
+ base::BigEndianReader* Reader();
private:
void AddRtcpHeader(int payload, int format_or_count);
@@ -93,6 +95,7 @@ class TestRtcpPacketBuilder {
uint8 buffer_[kMaxIpPacketSize];
char* ptr_of_length_;
base::BigEndianWriter big_endian_writer_;
+ base::BigEndianReader big_endian_reader_;
DISALLOW_COPY_AND_ASSIGN(TestRtcpPacketBuilder);
};
diff --git a/media/cast/net/rtp/frame_buffer.cc b/media/cast/net/rtp/frame_buffer.cc
index a419ab6a46..4a911635f9 100644
--- a/media/cast/net/rtp/frame_buffer.cc
+++ b/media/cast/net/rtp/frame_buffer.cc
@@ -13,6 +13,7 @@ FrameBuffer::FrameBuffer()
: frame_id_(0),
max_packet_id_(0),
num_packets_received_(0),
+ new_playout_delay_ms_(0),
is_key_frame_(false),
total_data_size_(0),
last_referenced_frame_id_(0),
@@ -28,6 +29,7 @@ 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;
+ new_playout_delay_ms_ = rtp_header.new_playout_delay_ms;
if (is_key_frame_)
DCHECK_EQ(rtp_header.frame_id, rtp_header.reference_frame_id);
last_referenced_frame_id_ = rtp_header.reference_frame_id;
@@ -73,6 +75,7 @@ bool FrameBuffer::AssembleEncodedFrame(EncodedFrame* frame) const {
frame->frame_id = frame_id_;
frame->referenced_frame_id = last_referenced_frame_id_;
frame->rtp_timestamp = rtp_timestamp_;
+ frame->new_playout_delay_ms = new_playout_delay_ms_;
// Build the data vector.
frame->data.clear();
diff --git a/media/cast/net/rtp/frame_buffer.h b/media/cast/net/rtp/frame_buffer.h
index 1309a8785a..8ccc2092c0 100644
--- a/media/cast/net/rtp/frame_buffer.h
+++ b/media/cast/net/rtp/frame_buffer.h
@@ -39,6 +39,7 @@ class FrameBuffer {
uint32 frame_id_;
uint16 max_packet_id_;
uint16 num_packets_received_;
+ uint16 new_playout_delay_ms_;
bool is_key_frame_;
size_t total_data_size_;
uint32 last_referenced_frame_id_;
diff --git a/media/cast/net/rtp/rtp_defines.h b/media/cast/net/rtp/rtp_defines.h
new file mode 100644
index 0000000000..56df66d4d2
--- /dev/null
+++ b/media/cast/net/rtp/rtp_defines.h
@@ -0,0 +1,20 @@
+// 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.
+
+namespace media {
+namespace cast {
+
+static const uint16 kRtpHeaderLength = 12;
+static const uint16 kCastHeaderLength = 7;
+static const uint8 kRtpExtensionBitMask = 0x10;
+static const uint8 kCastKeyFrameBitMask = 0x80;
+static const uint8 kCastReferenceFrameIdBitMask = 0x40;
+static const uint8 kRtpMarkerBitMask = 0x80;
+static const uint8 kCastExtensionCountmask = 0x3f;
+
+// Cast RTP extensions.
+static const uint8 kCastRtpExtensionAdaptiveLatency = 1;
+
+} // namespace cast
+} // namespace media
diff --git a/media/cast/net/rtp/rtp_packetizer.cc b/media/cast/net/rtp/rtp_packetizer.cc
index dcfcc8bba4..c2f5a17bd5 100644
--- a/media/cast/net/rtp/rtp_packetizer.cc
+++ b/media/cast/net/rtp/rtp_packetizer.cc
@@ -7,16 +7,11 @@
#include "base/big_endian.h"
#include "base/logging.h"
#include "media/cast/net/pacing/paced_sender.h"
+#include "media/cast/net/rtp/rtp_defines.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;
-static const uint8 kRtpMarkerBitMask = 0x80;
-
RtpPacketizerConfig::RtpPacketizerConfig()
: payload_type(-1),
max_payload_length(kMaxIpPacketSize - 28), // Default is IP-v4/UDP.
@@ -47,7 +42,7 @@ uint16 RtpPacketizer::NextSequenceNumber() {
}
void RtpPacketizer::SendFrameAsPackets(const EncodedFrame& frame) {
- uint16 rtp_header_length = kCommonRtpHeaderLength + kCastRtpHeaderLength;
+ uint16 rtp_header_length = kRtpHeaderLength + kCastHeaderLength;
uint16 max_length = config_.max_payload_length - rtp_header_length - 1;
rtp_timestamp_ = frame.rtp_timestamp;
@@ -73,9 +68,15 @@ void RtpPacketizer::SendFrameAsPackets(const EncodedFrame& frame) {
// 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);
+ uint8 num_extensions = 0;
+ if (frame.new_playout_delay_ms)
+ num_extensions++;
+ uint8 byte0 = kCastReferenceFrameIdBitMask;
+ if (frame.dependency == EncodedFrame::KEY)
+ byte0 |= kCastKeyFrameBitMask;
+ DCHECK_LE(num_extensions, kCastExtensionCountmask);
+ byte0 |= num_extensions;
+ packet->data.push_back(byte0);
packet->data.push_back(static_cast<uint8>(frame.frame_id));
size_t start_size = packet->data.size();
packet->data.resize(start_size + 4);
@@ -84,6 +85,14 @@ void RtpPacketizer::SendFrameAsPackets(const EncodedFrame& frame) {
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));
+ if (frame.new_playout_delay_ms) {
+ packet->data.push_back(kCastRtpExtensionAdaptiveLatency << 2);
+ packet->data.push_back(2); // 2 bytes
+ packet->data.push_back(
+ static_cast<uint8>(frame.new_playout_delay_ms >> 8));
+ packet->data.push_back(
+ static_cast<uint8>(frame.new_playout_delay_ms));
+ }
// Copy payload data.
packet->data.insert(packet->data.end(),
diff --git a/media/cast/net/rtp/rtp_packetizer_unittest.cc b/media/cast/net/rtp/rtp_packetizer_unittest.cc
index 0d93d91927..5e0d388cab 100644
--- a/media/cast/net/rtp/rtp_packetizer_unittest.cc
+++ b/media/cast/net/rtp/rtp_packetizer_unittest.cc
@@ -72,6 +72,10 @@ class TestRtpPacketTransport : public PacketSender {
return true;
}
+ virtual int64 GetBytesSent() OVERRIDE {
+ return 0;
+ }
+
size_t number_of_packets_received() const { return packets_sent_; }
void set_expected_number_of_packets(size_t expected_number_of_packets) {
diff --git a/media/cast/net/rtp/rtp_parser.cc b/media/cast/net/rtp/rtp_parser.cc
index bed1d7c8d8..1d68cd15a1 100644
--- a/media/cast/net/rtp/rtp_parser.cc
+++ b/media/cast/net/rtp/rtp_parser.cc
@@ -7,17 +7,11 @@
#include "base/big_endian.h"
#include "base/logging.h"
#include "media/cast/cast_defines.h"
+#include "media/cast/net/rtp/rtp_defines.h"
namespace media {
namespace cast {
-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(uint32 expected_sender_ssrc, uint8 expected_payload_type)
: expected_sender_ssrc_(expected_sender_ssrc),
expected_payload_type_(expected_payload_type) {}
@@ -92,6 +86,22 @@ bool RtpParser::ParsePacket(const uint8* packet,
return false;
}
+ for (int i = 0; i < (bits & kCastExtensionCountmask); i++) {
+ uint16 type_and_size;
+ if (!reader.ReadU16(&type_and_size))
+ return false;
+ base::StringPiece tmp;
+ if (!reader.ReadPiece(&tmp, type_and_size & 0x3ff))
+ return false;
+ base::BigEndianReader chunk(tmp.data(), tmp.size());
+ switch (type_and_size >> 10) {
+ case kCastRtpExtensionAdaptiveLatency:
+ if (!chunk.ReadU16(&header->new_playout_delay_ms))
+ return false;
+
+ }
+ }
+
// Only the lower 8 bits of the |frame_id| were serialized, so do some magic
// to restore the upper 24 bits.
//
diff --git a/media/cast/net/rtp/rtp_receiver_defines.cc b/media/cast/net/rtp/rtp_receiver_defines.cc
index 9b20b5f904..325b3058f6 100644
--- a/media/cast/net/rtp/rtp_receiver_defines.cc
+++ b/media/cast/net/rtp/rtp_receiver_defines.cc
@@ -17,7 +17,8 @@ RtpCastHeader::RtpCastHeader()
frame_id(0),
packet_id(0),
max_packet_id(0),
- reference_frame_id(0) {}
+ reference_frame_id(0),
+ new_playout_delay_ms(0) {}
RtpPayloadFeedback::~RtpPayloadFeedback() {}
diff --git a/media/cast/net/rtp/rtp_receiver_defines.h b/media/cast/net/rtp/rtp_receiver_defines.h
index 86fbd2296f..23a1849403 100644
--- a/media/cast/net/rtp/rtp_receiver_defines.h
+++ b/media/cast/net/rtp/rtp_receiver_defines.h
@@ -28,6 +28,8 @@ struct RtpCastHeader {
uint16 packet_id;
uint16 max_packet_id;
uint32 reference_frame_id;
+
+ uint16 new_playout_delay_ms;
};
class RtpPayloadFeedback {
diff --git a/media/cast/net/rtp/rtp_sender.cc b/media/cast/net/rtp/rtp_sender.cc
index 0f88c444ec..bf1200fd33 100644
--- a/media/cast/net/rtp/rtp_sender.cc
+++ b/media/cast/net/rtp/rtp_sender.cc
@@ -58,8 +58,7 @@ void RtpSender::SendFrame(const EncodedFrame& frame) {
void RtpSender::ResendPackets(
const MissingFramesAndPacketsMap& missing_frames_and_packets,
- bool cancel_rtx_if_not_in_list,
- base::TimeDelta dedupe_window) {
+ bool cancel_rtx_if_not_in_list, const DedupInfo& dedup_info) {
DCHECK(storage_);
// Iterate over all frames in the list.
for (MissingFramesAndPacketsMap::const_iterator it =
@@ -113,10 +112,40 @@ void RtpSender::ResendPackets(
transport_->CancelSendingPacket(it->first);
}
}
- transport_->ResendPackets(packets_to_resend, dedupe_window);
+ transport_->ResendPackets(packets_to_resend, dedup_info);
}
}
+void RtpSender::CancelSendingFrames(const std::vector<uint32>& frame_ids) {
+ for (std::vector<uint32>::const_iterator i = frame_ids.begin();
+ i != frame_ids.end(); ++i) {
+ const SendPacketVector* stored_packets = storage_->GetFrame8(*i & 0xFF);
+ if (!stored_packets)
+ continue;
+ for (SendPacketVector::const_iterator j = stored_packets->begin();
+ j != stored_packets->end(); ++j) {
+ transport_->CancelSendingPacket(j->first);
+ }
+ }
+}
+
+void RtpSender::ResendFrameForKickstart(uint32 frame_id,
+ base::TimeDelta dedupe_window) {
+ // Send the last packet of the 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(frame_id, missing));
+
+ // Sending this extra packet is to kick-start the session. There is
+ // no need to optimize re-transmission for this case.
+ DedupInfo dedup_info;
+ dedup_info.resend_interval = dedupe_window;
+ ResendPackets(missing_frames_and_packets, false, dedup_info);
+}
+
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.
@@ -127,5 +156,13 @@ void RtpSender::UpdateSequenceNumber(Packet* packet) {
big_endian_writer.WriteU16(packetizer_->NextSequenceNumber());
}
+int64 RtpSender::GetLastByteSentForFrame(uint32 frame_id) {
+ const SendPacketVector* stored_packets = storage_->GetFrame8(frame_id & 0xFF);
+ if (!stored_packets)
+ return 0;
+ PacketKey last_packet_key = stored_packets->rbegin()->first;
+ return transport_->GetLastByteSentForPacket(last_packet_key);
+}
+
} // namespace cast
} // namespace media
diff --git a/media/cast/net/rtp/rtp_sender.h b/media/cast/net/rtp/rtp_sender.h
index 4dd7966a68..2875114aa3 100644
--- a/media/cast/net/rtp/rtp_sender.h
+++ b/media/cast/net/rtp/rtp_sender.h
@@ -46,7 +46,17 @@ class RtpSender {
void ResendPackets(const MissingFramesAndPacketsMap& missing_packets,
bool cancel_rtx_if_not_in_list,
- base::TimeDelta dedupe_window);
+ const DedupInfo& dedup_info);
+
+ // Returns the total number of bytes sent to the socket when the specified
+ // frame was just sent.
+ // Returns 0 if the frame cannot be found or the frame was only sent
+ // partially.
+ int64 GetLastByteSentForFrame(uint32 frame_id);
+
+ void CancelSendingFrames(const std::vector<uint32>& frame_ids);
+
+ void ResendFrameForKickstart(uint32 frame_id, base::TimeDelta dedupe_window);
size_t send_packet_count() const {
return packetizer_ ? packetizer_->send_packet_count() : 0;
diff --git a/media/cast/net/udp_transport.cc b/media/cast/net/udp_transport.cc
index 00bd822a4f..d6ca677680 100644
--- a/media/cast/net/udp_transport.cc
+++ b/media/cast/net/udp_transport.cc
@@ -55,6 +55,7 @@ UdpTransport::UdpTransport(
client_connected_(false),
next_dscp_value_(net::DSCP_NO_CHANGE),
status_callback_(status_callback),
+ bytes_sent_(0),
weak_factory_(this) {
DCHECK(!IsEmpty(local_end_point) || !IsEmpty(remote_end_point));
}
@@ -160,6 +161,9 @@ void UdpTransport::ReceiveNextPacket(int length_or_status) {
bool UdpTransport::SendPacket(PacketRef packet, const base::Closure& cb) {
DCHECK(io_thread_proxy_->RunsTasksOnCurrentThread());
+ // Increase byte count no matter the packet was sent or dropped.
+ bytes_sent_ += packet->data.size();
+
DCHECK(!send_pending_);
if (send_pending_) {
VLOG(1) << "Cannot send because of pending IO.";
@@ -214,6 +218,10 @@ bool UdpTransport::SendPacket(PacketRef packet, const base::Closure& cb) {
return true;
}
+int64 UdpTransport::GetBytesSent() {
+ return bytes_sent_;
+}
+
void UdpTransport::OnSent(const scoped_refptr<net::IOBuffer>& buf,
PacketRef packet,
const base::Closure& cb,
diff --git a/media/cast/net/udp_transport.h b/media/cast/net/udp_transport.h
index 951e9c12d3..d88f2f3734 100644
--- a/media/cast/net/udp_transport.h
+++ b/media/cast/net/udp_transport.h
@@ -53,6 +53,7 @@ class UdpTransport : public PacketSender {
// PacketSender implementations.
virtual bool SendPacket(PacketRef packet,
const base::Closure& cb) OVERRIDE;
+ virtual int64 GetBytesSent() OVERRIDE;
private:
// Requests and processes packets from |udp_socket_|. This method is called
@@ -82,6 +83,7 @@ class UdpTransport : public PacketSender {
net::IPEndPoint recv_addr_;
PacketReceiverCallback packet_receiver_;
const CastTransportStatusCallback status_callback_;
+ int bytes_sent_;
// NOTE: Weak pointers must be invalidated before all other member variables.
base::WeakPtrFactory<UdpTransport> weak_factory_;
diff --git a/media/cast/receiver/cast_receiver_impl.cc b/media/cast/receiver/cast_receiver_impl.cc
index 91821bbb73..36669b9e62 100644
--- a/media/cast/receiver/cast_receiver_impl.cc
+++ b/media/cast/receiver/cast_receiver_impl.cc
@@ -10,7 +10,6 @@
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
-#include "media/cast/net/rtcp/rtcp_receiver.h"
#include "media/cast/receiver/audio_decoder.h"
#include "media/cast/receiver/video_decoder.h"
@@ -52,8 +51,8 @@ void CastReceiverImpl::DispatchReceivedPacket(scoped_ptr<Packet> packet) {
const size_t length = packet->size();
uint32 ssrc_of_sender;
- if (RtcpReceiver::IsRtcpPacket(data, length)) {
- ssrc_of_sender = RtcpReceiver::GetSsrcOfSender(data, length);
+ 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;
diff --git a/media/cast/receiver/frame_receiver.cc b/media/cast/receiver/frame_receiver.cc
index 2670372e78..0e794cdb27 100644
--- a/media/cast/receiver/frame_receiver.cc
+++ b/media/cast/receiver/frame_receiver.cc
@@ -11,7 +11,6 @@
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "media/cast/cast_environment.h"
-#include "media/cast/net/rtcp/rtcp_receiver.h"
namespace {
const int kMinSchedulingDelayMs = 1;
@@ -74,7 +73,7 @@ void FrameReceiver::RequestEncodedFrame(
bool FrameReceiver::ProcessPacket(scoped_ptr<Packet> packet) {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
- if (RtcpReceiver::IsRtcpPacket(&packet->front(), packet->size())) {
+ if (Rtcp::IsRtcpPacket(&packet->front(), packet->size())) {
rtcp_.IncomingRtcpPacket(&packet->front(), packet->size());
} else {
RtpCastHeader rtp_header;
@@ -203,8 +202,7 @@ void FrameReceiver::EmitAvailableEncodedFrames() {
}
const base::TimeTicks now = cast_environment_->Clock()->NowTicks();
- const base::TimeTicks playout_time =
- GetPlayoutTime(encoded_frame->rtp_timestamp);
+ const base::TimeTicks playout_time = GetPlayoutTime(*encoded_frame);
// If we have multiple decodable frames, and the current frame is
// too old, then skip it and decode the next frame instead.
@@ -252,6 +250,10 @@ void FrameReceiver::EmitAvailableEncodedFrames() {
// At this point, we have a decrypted EncodedFrame ready to be emitted.
encoded_frame->reference_time = playout_time;
framer_.ReleaseFrame(encoded_frame->frame_id);
+ if (encoded_frame->new_playout_delay_ms) {
+ target_playout_delay_ = base::TimeDelta::FromMilliseconds(
+ encoded_frame->new_playout_delay_ms);
+ }
cast_environment_->PostTask(CastEnvironment::MAIN,
FROM_HERE,
base::Bind(frame_request_queue_.front(),
@@ -267,13 +269,18 @@ void FrameReceiver::EmitAvailableEncodedFramesAfterWaiting() {
EmitAvailableEncodedFrames();
}
-base::TimeTicks FrameReceiver::GetPlayoutTime(uint32 rtp_timestamp) const {
+base::TimeTicks FrameReceiver::GetPlayoutTime(const EncodedFrame& frame) const {
+ base::TimeDelta target_playout_delay = target_playout_delay_;
+ if (frame.new_playout_delay_ms) {
+ target_playout_delay = base::TimeDelta::FromMilliseconds(
+ frame.new_playout_delay_ms);
+ }
return lip_sync_reference_time_ +
lip_sync_drift_.Current() +
RtpDeltaToTimeDelta(
- static_cast<int32>(rtp_timestamp - lip_sync_rtp_timestamp_),
+ static_cast<int32>(frame.rtp_timestamp - lip_sync_rtp_timestamp_),
rtp_timebase_) +
- target_playout_delay_;
+ target_playout_delay;
}
void FrameReceiver::ScheduleNextCastMessage() {
diff --git a/media/cast/receiver/frame_receiver.h b/media/cast/receiver/frame_receiver.h
index 2ddeeb980c..695c8d0a9b 100644
--- a/media/cast/receiver/frame_receiver.h
+++ b/media/cast/receiver/frame_receiver.h
@@ -92,7 +92,7 @@ class FrameReceiver : public RtpPayloadFeedback,
// 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;
+ base::TimeTicks GetPlayoutTime(const EncodedFrame& frame) const;
// Schedule timing for the next cast message.
void ScheduleNextCastMessage();
@@ -130,7 +130,7 @@ class FrameReceiver : public RtpPayloadFeedback,
// transmit/retransmit, receive, decode, and render; given its run-time
// environment (sender/receiver hardware performance, network conditions,
// etc.).
- const base::TimeDelta target_playout_delay_;
+ 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
diff --git a/media/cast/sender/audio_sender.cc b/media/cast/sender/audio_sender.cc
index 704621da38..4bf93b30cd 100644
--- a/media/cast/sender/audio_sender.cc
+++ b/media/cast/sender/audio_sender.cc
@@ -23,16 +23,6 @@ const int kMinSchedulingDelayMs = 1;
// well.
const int kAudioFrameRate = 100;
-// 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 + 2 * target_delay * kAudioFrameRate / base::TimeDelta::FromSeconds(1);
- return std::min(kMaxUnackedFrames, frames);
-}
} // namespace
AudioSender::AudioSender(scoped_refptr<CastEnvironment> cast_environment,
@@ -43,9 +33,9 @@ AudioSender::AudioSender(scoped_refptr<CastEnvironment> cast_environment,
transport_sender,
base::TimeDelta::FromMilliseconds(audio_config.rtcp_interval),
audio_config.frequency,
- audio_config.ssrc),
- target_playout_delay_(audio_config.target_playout_delay),
- max_unacked_frames_(GetMaxUnackedFrames(target_playout_delay_)),
+ audio_config.ssrc,
+ kAudioFrameRate * 2.0, // We lie to increase max outstanding frames.
+ audio_config.target_playout_delay),
configured_encoder_bitrate_(audio_config.bitrate),
num_aggressive_rtcp_reports_sent_(0),
last_sent_frame_id_(0),
@@ -152,6 +142,10 @@ void AudioSender::SendEncodedAudioFrame(
SendRtcpReport(is_last_aggressive_report);
}
+ if (send_target_playout_delay_) {
+ encoded_frame->new_playout_delay_ms =
+ target_playout_delay_.InMilliseconds();
+ }
transport_sender_->InsertCodedAudioFrame(*encoded_frame);
}
@@ -221,10 +215,6 @@ void AudioSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) {
// Only count duplicated ACKs if there is no NACK request in between.
// This is to avoid aggresive resend.
duplicate_ack_counter_ = 0;
-
- // A NACK is also used to cancel pending re-transmissions.
- transport_sender_->ResendPackets(
- true, cast_feedback.missing_frames_and_packets, false, min_rtt_);
}
const base::TimeTicks now = cast_environment_->Clock()->NowTicks();
@@ -244,14 +234,12 @@ void AudioSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) {
<< " 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;
+ std::vector<uint32> cancel_sending_frames;
while (latest_acked_frame_id_ != cast_feedback.ack_frame_id) {
latest_acked_frame_id_++;
- missing_frames_and_packets[latest_acked_frame_id_] = missing;
+ cancel_sending_frames.push_back(latest_acked_frame_id_);
}
- transport_sender_->ResendPackets(
- true, missing_frames_and_packets, true, base::TimeDelta());
+ transport_sender_->CancelSendingFrames(ssrc_, cancel_sending_frames);
latest_acked_frame_id_ = cast_feedback.ack_frame_id;
}
}
@@ -274,20 +262,8 @@ void AudioSender::ResendForKickstart() {
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();
-
- // 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_);
+ transport_sender_->ResendFrameForKickstart(ssrc_, last_sent_frame_id_);
}
} // namespace cast
diff --git a/media/cast/sender/audio_sender.h b/media/cast/sender/audio_sender.h
index 02582aebf3..e07b892a73 100644
--- a/media/cast/sender/audio_sender.h
+++ b/media/cast/sender/audio_sender.h
@@ -73,18 +73,6 @@ class AudioSender : public FrameSender,
// Called by the |audio_encoder_| with the next EncodedFrame to send.
void SendEncodedAudioFrame(scoped_ptr<EncodedFrame> audio_frame);
- // 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_;
-
- // 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_;
diff --git a/media/cast/sender/audio_sender_unittest.cc b/media/cast/sender/audio_sender_unittest.cc
index 287630db1a..6c99af9d3c 100644
--- a/media/cast/sender/audio_sender_unittest.cc
+++ b/media/cast/sender/audio_sender_unittest.cc
@@ -13,7 +13,6 @@
#include "media/cast/cast_environment.h"
#include "media/cast/net/cast_transport_config.h"
#include "media/cast/net/cast_transport_sender_impl.h"
-#include "media/cast/net/rtcp/rtcp_receiver.h"
#include "media/cast/sender/audio_sender.h"
#include "media/cast/test/fake_single_thread_task_runner.h"
#include "media/cast/test/utility/audio_utility.h"
@@ -28,7 +27,7 @@ class TestPacketSender : public PacketSender {
virtual bool SendPacket(PacketRef packet,
const base::Closure& cb) OVERRIDE {
- if (RtcpReceiver::IsRtcpPacket(&packet->data[0], packet->data.size())) {
+ 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
@@ -42,6 +41,10 @@ class TestPacketSender : public PacketSender {
return true;
}
+ virtual int64 GetBytesSent() OVERRIDE {
+ return 0;
+ }
+
int number_of_rtp_packets() const { return number_of_rtp_packets_; }
int number_of_rtcp_packets() const { return number_of_rtcp_packets_; }
diff --git a/media/cast/sender/congestion_control.cc b/media/cast/sender/congestion_control.cc
index 70fcfe9dc9..9efe50adbf 100644
--- a/media/cast/sender/congestion_control.cc
+++ b/media/cast/sender/congestion_control.cc
@@ -113,9 +113,16 @@ 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_);
+ frame_stats = GetFrameStats(last_acked_frame_ + 1);
DCHECK(frame_stats);
+ if (frame_stats->sent_time.is_null()) {
+ // Can't ack a frame that hasn't been sent yet.
+ return;
+ }
+ last_acked_frame_++;
+ if (when < frame_stats->sent_time)
+ when = frame_stats->sent_time;
+
frame_stats->ack_time = when;
acked_bits_in_history_ += frame_stats->frame_size;
dead_time_in_history_ += DeadTime(*last_frame_stats, *frame_stats);
diff --git a/media/cast/sender/frame_sender.cc b/media/cast/sender/frame_sender.cc
index e5bc95cd50..b531a069fe 100644
--- a/media/cast/sender/frame_sender.cc
+++ b/media/cast/sender/frame_sender.cc
@@ -14,14 +14,19 @@ FrameSender::FrameSender(scoped_refptr<CastEnvironment> cast_environment,
CastTransportSender* const transport_sender,
base::TimeDelta rtcp_interval,
int frequency,
- uint32 ssrc)
+ uint32 ssrc,
+ double max_frame_rate,
+ base::TimeDelta playout_delay)
: cast_environment_(cast_environment),
transport_sender_(transport_sender),
+ ssrc_(ssrc),
rtp_timestamp_helper_(frequency),
rtt_available_(false),
rtcp_interval_(rtcp_interval),
- ssrc_(ssrc),
+ max_frame_rate_(max_frame_rate),
weak_factory_(this) {
+ SetTargetPlayoutDelay(playout_delay);
+ send_target_playout_delay_ = false;
}
FrameSender::~FrameSender() {
@@ -68,5 +73,16 @@ void FrameSender::OnReceivedRtt(base::TimeDelta rtt,
max_rtt_ = max_rtt;
}
+void FrameSender::SetTargetPlayoutDelay(
+ base::TimeDelta new_target_playout_delay) {
+ target_playout_delay_ = new_target_playout_delay;
+ max_unacked_frames_ =
+ std::min(kMaxUnackedFrames,
+ 1 + static_cast<int>(target_playout_delay_ *
+ max_frame_rate_ /
+ base::TimeDelta::FromSeconds(1)));
+ send_target_playout_delay_ = true;
+}
+
} // namespace cast
} // namespace media
diff --git a/media/cast/sender/frame_sender.h b/media/cast/sender/frame_sender.h
index 70eccba9ba..5da3927961 100644
--- a/media/cast/sender/frame_sender.h
+++ b/media/cast/sender/frame_sender.h
@@ -26,9 +26,19 @@ class FrameSender {
CastTransportSender* const transport_sender,
base::TimeDelta rtcp_interval,
int frequency,
- uint32 ssrc);
+ uint32 ssrc,
+ double max_frame_rate,
+ base::TimeDelta playout_delay);
virtual ~FrameSender();
+ // Calling this function is only valid if the receiver supports the
+ // "extra_playout_delay", rtp extension.
+ void SetTargetPlayoutDelay(base::TimeDelta new_target_playout_delay);
+
+ base::TimeDelta GetTargetPlayoutDelay() const {
+ return target_playout_delay_;
+ }
+
protected:
// Schedule and execute periodic sending of RTCP report.
void ScheduleNextRtcpReport();
@@ -50,6 +60,8 @@ class FrameSender {
// network layer.
CastTransportSender* const transport_sender_;
+ const uint32 ssrc_;
+
// Records lip-sync (i.e., mapping of RTP <--> NTP timestamps), and
// extrapolates this mapping to any other point in time.
RtpTimestampHelper rtp_timestamp_helper_;
@@ -61,10 +73,28 @@ class FrameSender {
base::TimeDelta min_rtt_;
base::TimeDelta max_rtt_;
- private:
+ protected:
const base::TimeDelta rtcp_interval_;
- const uint32 ssrc_;
+ // 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.).
+ base::TimeDelta target_playout_delay_;
+
+ // If true, we transmit the target playout delay to the receiver.
+ bool send_target_playout_delay_;
+
+ // Max encoded frames generated per second.
+ double max_frame_rate_;
+
+ // Maximum number of outstanding frames before the encoding and sending of
+ // new frames shall halt.
+ int max_unacked_frames_;
+
+ private:
// NOTE: Weak pointers must be invalidated before all other member variables.
base::WeakPtrFactory<FrameSender> weak_factory_;
diff --git a/media/cast/sender/video_sender.cc b/media/cast/sender/video_sender.cc
index b9d359bbde..f0e3a1440c 100644
--- a/media/cast/sender/video_sender.cc
+++ b/media/cast/sender/video_sender.cc
@@ -47,13 +47,9 @@ VideoSender::VideoSender(
transport_sender,
base::TimeDelta::FromMilliseconds(video_config.rtcp_interval),
kVideoFrequency,
- video_config.ssrc),
- target_playout_delay_(video_config.target_playout_delay),
- max_unacked_frames_(
- std::min(kMaxUnackedFrames,
- 1 + static_cast<int>(target_playout_delay_ *
- video_config.max_frame_rate /
- base::TimeDelta::FromSeconds(1)))),
+ video_config.ssrc,
+ video_config.max_frame_rate,
+ video_config.target_playout_delay),
fixed_bitrate_(GetFixedBitrate(video_config)),
num_aggressive_rtcp_reports_sent_(0),
frames_in_encoder_(0),
@@ -220,6 +216,10 @@ void VideoSender::SendEncodedVideoFrame(
congestion_control_.SendFrameToTransport(
frame_id, encoded_frame->data.size() * 8, last_send_time_);
+ if (send_target_playout_delay_) {
+ encoded_frame->new_playout_delay_ms =
+ target_playout_delay_.InMilliseconds();
+ }
transport_sender_->InsertCodedVideoFrame(*encoded_frame);
}
@@ -308,10 +308,6 @@ void VideoSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) {
// Only count duplicated ACKs if there is no NACK request in between.
// This is to avoid aggresive resend.
duplicate_ack_counter_ = 0;
-
- // A NACK is also used to cancel pending re-transmissions.
- transport_sender_->ResendPackets(
- false, cast_feedback.missing_frames_and_packets, true, rtt);
}
base::TimeTicks now = cast_environment_->Clock()->NowTicks();
@@ -332,14 +328,12 @@ void VideoSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) {
<< " 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;
+ std::vector<uint32> cancel_sending_frames;
while (latest_acked_frame_id_ != cast_feedback.ack_frame_id) {
latest_acked_frame_id_++;
- missing_frames_and_packets[latest_acked_frame_id_] = missing;
+ cancel_sending_frames.push_back(latest_acked_frame_id_);
}
- transport_sender_->ResendPackets(
- false, missing_frames_and_packets, true, rtt);
+ transport_sender_->CancelSendingFrames(ssrc_, cancel_sending_frames);
latest_acked_frame_id_ = cast_feedback.ack_frame_id;
}
}
@@ -363,20 +357,8 @@ void VideoSender::ResendForKickstart() {
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();
-
- // 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_);
+ transport_sender_->ResendFrameForKickstart(ssrc_, last_sent_frame_id_);
}
} // namespace cast
diff --git a/media/cast/sender/video_sender.h b/media/cast/sender/video_sender.h
index bf96e7bb92..ebada4d26a 100644
--- a/media/cast/sender/video_sender.h
+++ b/media/cast/sender/video_sender.h
@@ -79,19 +79,6 @@ class VideoSender : public FrameSender,
// Called by the |video_encoder_| with the next EncodeFrame to send.
void SendEncodedVideoFrame(int requested_bitrate_before_encode,
scoped_ptr<EncodedFrame> encoded_frame);
-
- // 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_;
-
- // Maximum number of outstanding frames before the encoding and sending of
- // new frames shall halt.
- const int max_unacked_frames_;
-
// If this value is non zero then a fixed value is used for bitrate.
// If external video encoder is used then bitrate will be fixed to
// (min_bitrate + max_bitrate) / 2.
diff --git a/media/cast/sender/video_sender_unittest.cc b/media/cast/sender/video_sender_unittest.cc
index 06e9dc0678..b9620fcdb2 100644
--- a/media/cast/sender/video_sender_unittest.cc
+++ b/media/cast/sender/video_sender_unittest.cc
@@ -15,7 +15,6 @@
#include "media/cast/net/cast_transport_config.h"
#include "media/cast/net/cast_transport_sender_impl.h"
#include "media/cast/net/pacing/paced_sender.h"
-#include "media/cast/net/rtcp/rtcp_receiver.h"
#include "media/cast/sender/video_sender.h"
#include "media/cast/test/fake_single_thread_task_runner.h"
#include "media/cast/test/fake_video_encode_accelerator.h"
@@ -67,7 +66,7 @@ class TestPacketSender : public PacketSender {
callback_ = cb;
return false;
}
- if (RtcpReceiver::IsRtcpPacket(&packet->data[0], packet->data.size())) {
+ 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
@@ -81,6 +80,10 @@ class TestPacketSender : public PacketSender {
return true;
}
+ virtual int64 GetBytesSent() OVERRIDE {
+ return 0;
+ }
+
int number_of_rtp_packets() const { return number_of_rtp_packets_; }
int number_of_rtcp_packets() const { return number_of_rtcp_packets_; }
@@ -503,32 +506,5 @@ TEST_F(VideoSenderTest, AcksCancelRetransmits) {
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
diff --git a/media/cast/test/cast_benchmarks.cc b/media/cast/test/cast_benchmarks.cc
index 57806b35da..07b4de5382 100644
--- a/media/cast/test/cast_benchmarks.cc
+++ b/media/cast/test/cast_benchmarks.cc
@@ -143,14 +143,15 @@ class CastTransportSenderWrapper : public CastTransportSender {
current_time_as_rtp_timestamp);
}
- // Retransmission request.
- virtual void ResendPackets(
- bool is_audio,
- const MissingFramesAndPacketsMap& missing_packets,
- bool cancel_rtx_if_not_in_list,
- base::TimeDelta dedupe_window) OVERRIDE {
- transport_->ResendPackets(
- is_audio, missing_packets, cancel_rtx_if_not_in_list, dedupe_window);
+ virtual void CancelSendingFrames(
+ uint32 ssrc,
+ const std::vector<uint32>& frame_ids) OVERRIDE {
+ transport_->CancelSendingFrames(ssrc, frame_ids);
+ }
+
+ virtual void ResendFrameForKickstart(uint32 ssrc,
+ uint32 frame_id) OVERRIDE {
+ transport_->ResendFrameForKickstart(ssrc, frame_id);
}
virtual PacketReceiverCallback PacketReceiverForTesting() OVERRIDE {
diff --git a/media/cast/test/end2end_unittest.cc b/media/cast/test/end2end_unittest.cc
index bfd9faba89..bb8b62d791 100644
--- a/media/cast/test/end2end_unittest.cc
+++ b/media/cast/test/end2end_unittest.cc
@@ -189,7 +189,8 @@ class LoopBackTransport : public PacketSender {
explicit LoopBackTransport(scoped_refptr<CastEnvironment> cast_environment)
: send_packets_(true),
drop_packets_belonging_to_odd_frames_(false),
- cast_environment_(cast_environment) {}
+ cast_environment_(cast_environment),
+ bytes_sent_(0) {}
void SetPacketReceiver(
const PacketReceiverCallback& packet_receiver,
@@ -211,6 +212,7 @@ class LoopBackTransport : public PacketSender {
if (!send_packets_)
return false;
+ bytes_sent_ += packet->data.size();
if (drop_packets_belonging_to_odd_frames_) {
uint32 frame_id = packet->data[13];
if (frame_id % 2 == 1)
@@ -222,6 +224,10 @@ class LoopBackTransport : public PacketSender {
return true;
}
+ virtual int64 GetBytesSent() OVERRIDE {
+ return bytes_sent_;
+ }
+
void SetSendPackets(bool send_packets) { send_packets_ = send_packets; }
void DropAllPacketsBelongingToOddFrames() {
@@ -239,6 +245,7 @@ class LoopBackTransport : public PacketSender {
bool drop_packets_belonging_to_odd_frames_;
scoped_refptr<CastEnvironment> cast_environment_;
scoped_ptr<test::PacketPipe> packet_pipe_;
+ int64 bytes_sent_;
};
// Class that verifies the audio frames coming out of the receiver.
@@ -1454,6 +1461,71 @@ TEST_F(End2EndTest, EvilNetwork) {
EXPECT_LT((video_ticks_.back().second - test_end).InMilliseconds(), 1000);
}
+TEST_F(End2EndTest, OldPacketNetwork) {
+ Configure(CODEC_VIDEO_FAKE, CODEC_AUDIO_PCM16, 32000, 1);
+ sender_to_receiver_.SetPacketPipe(test::NewRandomDrop(0.01));
+ scoped_ptr<test::PacketPipe> echo_chamber(
+ test::NewDuplicateAndDelay(1, 10 * kFrameTimerMs));
+ echo_chamber->AppendToPipe(
+ test::NewDuplicateAndDelay(1, 20 * kFrameTimerMs));
+ echo_chamber->AppendToPipe(
+ test::NewDuplicateAndDelay(1, 40 * kFrameTimerMs));
+ echo_chamber->AppendToPipe(
+ test::NewDuplicateAndDelay(1, 80 * kFrameTimerMs));
+ echo_chamber->AppendToPipe(
+ test::NewDuplicateAndDelay(1, 160 * kFrameTimerMs));
+
+ receiver_to_sender_.SetPacketPipe(echo_chamber.Pass());
+ Create();
+ StartBasicPlayer();
+
+ SetExpectedVideoPlayoutSmoothness(
+ base::TimeDelta::FromMilliseconds(kFrameTimerMs) * 90 / 100,
+ base::TimeDelta::FromMilliseconds(kFrameTimerMs) * 110 / 100,
+ base::TimeDelta::FromMilliseconds(kFrameTimerMs) / 10);
+
+ int frames_counter = 0;
+ for (; frames_counter < 10000; ++frames_counter) {
+ SendFakeVideoFrame(testing_clock_sender_->NowTicks());
+ RunTasks(kFrameTimerMs);
+ }
+ RunTasks(100 * kFrameTimerMs + 1); // Empty the pipeline.
+
+ EXPECT_EQ(10000ul, video_ticks_.size());
+}
+
+TEST_F(End2EndTest, TestSetPlayoutDelay) {
+ Configure(CODEC_VIDEO_FAKE, CODEC_AUDIO_PCM16, 32000, 1);
+ Create();
+ StartBasicPlayer();
+ const int kNewDelay = 600;
+
+ int frames_counter = 0;
+ for (; frames_counter < 200; ++frames_counter) {
+ SendFakeVideoFrame(testing_clock_sender_->NowTicks());
+ RunTasks(kFrameTimerMs);
+ }
+ cast_sender_->SetTargetPlayoutDelay(
+ base::TimeDelta::FromMilliseconds(kNewDelay));
+ for (; frames_counter < 400; ++frames_counter) {
+ SendFakeVideoFrame(testing_clock_sender_->NowTicks());
+ RunTasks(kFrameTimerMs);
+ }
+ RunTasks(100 * kFrameTimerMs + 1); // Empty the pipeline.
+ size_t jump = 0;
+ for (size_t i = 1; i < video_ticks_.size(); i++) {
+ int64 delta = (video_ticks_[i].second -
+ video_ticks_[i-1].second).InMilliseconds();
+ if (delta > 100) {
+ EXPECT_EQ(delta, kNewDelay - kTargetPlayoutDelayMs + kFrameTimerMs);
+ EXPECT_EQ(0u, jump);
+ jump = i;
+ }
+ }
+ EXPECT_GT(jump, 199u);
+ EXPECT_LT(jump, 220u);
+}
+
// TODO(pwestin): Add repeatable packet loss test.
// TODO(pwestin): Add test for misaligned send get calls.
// TODO(pwestin): Add more tests that does not resample.
diff --git a/media/cast/test/loopback_transport.cc b/media/cast/test/loopback_transport.cc
index 3b72a7ef5a..bc19f016c7 100644
--- a/media/cast/test/loopback_transport.cc
+++ b/media/cast/test/loopback_transport.cc
@@ -37,7 +37,8 @@ class LoopBackPacketPipe : public test::PacketPipe {
LoopBackTransport::LoopBackTransport(
scoped_refptr<CastEnvironment> cast_environment)
- : cast_environment_(cast_environment) {
+ : cast_environment_(cast_environment),
+ bytes_sent_(0) {
}
LoopBackTransport::~LoopBackTransport() {
@@ -48,9 +49,14 @@ bool LoopBackTransport::SendPacket(PacketRef packet,
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
scoped_ptr<Packet> packet_copy(new Packet(packet->data));
packet_pipe_->Send(packet_copy.Pass());
+ bytes_sent_ += packet->data.size();
return true;
}
+int64 LoopBackTransport::GetBytesSent() {
+ return bytes_sent_;
+}
+
void LoopBackTransport::Initialize(
scoped_ptr<test::PacketPipe> pipe,
const PacketReceiverCallback& packet_receiver,
diff --git a/media/cast/test/loopback_transport.h b/media/cast/test/loopback_transport.h
index 8942caa4b9..cf29fbb789 100644
--- a/media/cast/test/loopback_transport.h
+++ b/media/cast/test/loopback_transport.h
@@ -32,6 +32,8 @@ class LoopBackTransport : public PacketSender {
virtual bool SendPacket(PacketRef packet,
const base::Closure& cb) OVERRIDE;
+ virtual int64 GetBytesSent() OVERRIDE;
+
// Initiailize this loopback transport.
// Establish a flow of packets from |pipe| to |packet_receiver|.
// The data flow looks like:
@@ -45,6 +47,7 @@ class LoopBackTransport : public PacketSender {
private:
const scoped_refptr<CastEnvironment> cast_environment_;
scoped_ptr<test::PacketPipe> packet_pipe_;
+ int64 bytes_sent_;
DISALLOW_COPY_AND_ASSIGN(LoopBackTransport);
};
diff --git a/media/cast/test/simulator.cc b/media/cast/test/simulator.cc
index b3360c8722..39ff725a86 100644
--- a/media/cast/test/simulator.cc
+++ b/media/cast/test/simulator.cc
@@ -65,7 +65,7 @@ using media::cast::proto::NetworkSimulationModelType;
namespace media {
namespace cast {
namespace {
-const int kTargetDelay = 300;
+const int kTargetDelay = 400;
const char kSourcePath[] = "source";
const char kModelPath[] = "model";
const char kOutputPath[] = "output";
@@ -230,9 +230,9 @@ void RunSimulation(const base::FilePath& source_path,
// Video sender config.
VideoSenderConfig video_sender_config = GetDefaultVideoSenderConfig();
- video_sender_config.max_bitrate = 4000000;
+ video_sender_config.max_bitrate = 2500000;
video_sender_config.min_bitrate = 2000000;
- video_sender_config.start_bitrate = 4000000;
+ video_sender_config.start_bitrate = 2000000;
video_sender_config.target_playout_delay =
base::TimeDelta::FromMilliseconds(kTargetDelay);
diff --git a/media/cast/test/utility/udp_proxy.cc b/media/cast/test/utility/udp_proxy.cc
index e71678c482..4dbac83fa5 100644
--- a/media/cast/test/utility/udp_proxy.cc
+++ b/media/cast/test/utility/udp_proxy.cc
@@ -186,6 +186,29 @@ scoped_ptr<PacketPipe> NewRandomUnsortedDelay(double random_delay) {
return scoped_ptr<PacketPipe>(new RandomUnsortedDelay(random_delay)).Pass();
}
+class DuplicateAndDelay : public RandomUnsortedDelay {
+ public:
+ DuplicateAndDelay(double delay_min,
+ double random_delay) :
+ RandomUnsortedDelay(random_delay),
+ delay_min_(delay_min) {
+ }
+ virtual void Send(scoped_ptr<Packet> packet) OVERRIDE {
+ pipe_->Send(scoped_ptr<Packet>(new Packet(*packet.get())));
+ RandomUnsortedDelay::Send(packet.Pass());
+ }
+ virtual double GetDelay() OVERRIDE {
+ return RandomUnsortedDelay::GetDelay() + delay_min_;
+ }
+ private:
+ double delay_min_;
+};
+
+scoped_ptr<PacketPipe> NewDuplicateAndDelay(double delay_min,
+ double random_delay) {
+ return scoped_ptr<PacketPipe>(
+ new DuplicateAndDelay(delay_min, random_delay)).Pass();
+}
class RandomSortedDelay : public PacketPipe {
public:
diff --git a/media/cast/test/utility/udp_proxy.h b/media/cast/test/utility/udp_proxy.h
index 6c72fb6576..967288f78a 100644
--- a/media/cast/test/utility/udp_proxy.h
+++ b/media/cast/test/utility/udp_proxy.h
@@ -140,6 +140,13 @@ scoped_ptr<PacketPipe> NewConstantDelay(double delay_seconds);
// This PacketPipe can reorder packets.
scoped_ptr<PacketPipe> NewRandomUnsortedDelay(double delay);
+// Duplicates every packet, one is transmitted immediately,
+// one is transmitted after a random delay between |delay_min|
+// and |delay_min + random_delay|.
+// This PacketPipe will reorder packets.
+scoped_ptr<PacketPipe> NewDuplicateAndDelay(double delay_min,
+ double random_delay);
+
// This PacketPipe inserts a random delay between each packet.
// This PacketPipe cannot re-order packets. The delay between each
// packet is asically |min_delay| + random( |random_delay| )
diff --git a/media/cdm/aes_decryptor.cc b/media/cdm/aes_decryptor.cc
index daafb1e88d..b83c9b185d 100644
--- a/media/cdm/aes_decryptor.cc
+++ b/media/cdm/aes_decryptor.cc
@@ -245,11 +245,11 @@ void AesDecryptor::CreateSession(const std::string& init_data_type,
// For now, the AesDecryptor does not care about |init_data_type| or
// |session_type|; just resolve the promise and then fire a message event
- // with the |init_data| as the request.
+ // using the |init_data| as the key ID in the license request.
// TODO(jrummell): Validate |init_data_type| and |session_type|.
std::vector<uint8> message;
if (init_data && init_data_length)
- message.assign(init_data, init_data + init_data_length);
+ CreateLicenseRequest(init_data, init_data_length, session_type, &message);
promise->resolve(web_session_id);
@@ -280,7 +280,8 @@ void AesDecryptor::UpdateSession(const std::string& web_session_id,
response_length);
KeyIdAndKeyPairs keys;
- if (!ExtractKeysFromJWKSet(key_string, &keys)) {
+ SessionType session_type = MediaKeys::TEMPORARY_SESSION;
+ if (!ExtractKeysFromJWKSet(key_string, &keys, &session_type)) {
promise->reject(
INVALID_ACCESS_ERROR, 0, "response is not a valid JSON Web Key Set.");
return;
diff --git a/media/cdm/aes_decryptor_unittest.cc b/media/cdm/aes_decryptor_unittest.cc
index 91cd6ac365..fe84c07521 100644
--- a/media/cdm/aes_decryptor_unittest.cc
+++ b/media/cdm/aes_decryptor_unittest.cc
@@ -7,6 +7,8 @@
#include "base/basictypes.h"
#include "base/bind.h"
+#include "base/json/json_reader.h"
+#include "base/values.h"
#include "media/base/cdm_promise.h"
#include "media/base/decoder_buffer.h"
#include "media/base/decrypt_config.h"
@@ -24,6 +26,11 @@ using ::testing::StrNe;
MATCHER(IsEmpty, "") { return arg.empty(); }
MATCHER(IsNotEmpty, "") { return !arg.empty(); }
+MATCHER(IsJSONDictionary, "") {
+ std::string result(arg.begin(), arg.end());
+ scoped_ptr<base::Value> root(base::JSONReader().ReadToValue(result));
+ return (root.get() && root->GetType() == base::Value::TYPE_DICTIONARY);
+}
class GURL;
@@ -51,7 +58,8 @@ const char kKeyAsJWK[] =
" \"kid\": \"AAECAw\","
" \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
" }"
- " ]"
+ " ],"
+ " \"type\": \"temporary\""
"}";
// Same kid as kKeyAsJWK, key to decrypt kEncryptedData2
@@ -287,7 +295,8 @@ class AesDecryptorTest : public testing::Test {
std::string CreateSession(const std::vector<uint8>& key_id) {
DCHECK(!key_id.empty());
EXPECT_CALL(*this,
- OnSessionMessage(IsNotEmpty(), key_id, GURL::EmptyGURL()));
+ OnSessionMessage(
+ IsNotEmpty(), IsJSONDictionary(), GURL::EmptyGURL()));
decryptor_.CreateSession(std::string(),
&key_id[0],
key_id.size(),
diff --git a/media/cdm/json_web_key.cc b/media/cdm/json_web_key.cc
index 4b9d8221f7..03a8a1a705 100644
--- a/media/cdm/json_web_key.cc
+++ b/media/cdm/json_web_key.cc
@@ -7,6 +7,7 @@
#include "base/base64.h"
#include "base/json/json_reader.h"
#include "base/json/json_string_value_serializer.h"
+#include "base/json/string_escape.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_util.h"
@@ -19,7 +20,11 @@ const char kKeyTypeTag[] = "kty";
const char kSymmetricKeyValue[] = "oct";
const char kKeyTag[] = "k";
const char kKeyIdTag[] = "kid";
+const char kKeyIdsTag[] = "kids";
const char kBase64Padding = '=';
+const char kTypeTag[] = "type";
+const char kPersistentType[] = "persistent";
+const char kTemporaryType[] = "temporary";
// Encodes |input| into a base64 string without padding.
static std::string EncodeBase64(const uint8* input, int input_length) {
@@ -120,7 +125,9 @@ static bool ConvertJwkToKeyPair(const base::DictionaryValue& jwk,
return true;
}
-bool ExtractKeysFromJWKSet(const std::string& jwk_set, KeyIdAndKeyPairs* keys) {
+bool ExtractKeysFromJWKSet(const std::string& jwk_set,
+ KeyIdAndKeyPairs* keys,
+ MediaKeys::SessionType* session_type) {
if (!base::IsStringASCII(jwk_set))
return false;
@@ -156,9 +163,101 @@ bool ExtractKeysFromJWKSet(const std::string& jwk_set, KeyIdAndKeyPairs* keys) {
local_keys.push_back(key_pair);
}
- // Successfully processed all JWKs in the set.
+ // Successfully processed all JWKs in the set. Now check if "type" is
+ // specified.
+ base::Value* value = NULL;
+ std::string type_id;
+ if (!dictionary->Get(kTypeTag, &value)) {
+ // Not specified, so use the default type.
+ *session_type = MediaKeys::TEMPORARY_SESSION;
+ } else if (!value->GetAsString(&type_id)) {
+ DVLOG(1) << "Invalid '" << kTypeTag << "' value";
+ return false;
+ } else if (type_id == kPersistentType) {
+ *session_type = MediaKeys::PERSISTENT_SESSION;
+ } else if (type_id == kTemporaryType) {
+ *session_type = MediaKeys::TEMPORARY_SESSION;
+ } else {
+ DVLOG(1) << "Invalid '" << kTypeTag << "' value: " << type_id;
+ return false;
+ }
+
+ // All done.
keys->swap(local_keys);
return true;
}
+void CreateLicenseRequest(const uint8* key_id,
+ int key_id_length,
+ MediaKeys::SessionType session_type,
+ std::vector<uint8>* license) {
+ // Create the license request.
+ scoped_ptr<base::DictionaryValue> request(new base::DictionaryValue());
+ scoped_ptr<base::ListValue> list(new base::ListValue());
+ list->AppendString(EncodeBase64(key_id, key_id_length));
+ request->Set(kKeyIdsTag, list.release());
+
+ switch (session_type) {
+ case MediaKeys::TEMPORARY_SESSION:
+ request->SetString(kTypeTag, kTemporaryType);
+ break;
+ case MediaKeys::PERSISTENT_SESSION:
+ request->SetString(kTypeTag, kPersistentType);
+ break;
+ }
+
+ // Serialize the license request as a string.
+ std::string json;
+ JSONStringValueSerializer serializer(&json);
+ serializer.Serialize(*request);
+
+ // Convert the serialized license request into std::vector and return it.
+ std::vector<uint8> result(json.begin(), json.end());
+ license->swap(result);
+}
+
+bool ExtractFirstKeyIdFromLicenseRequest(const std::vector<uint8>& license,
+ std::vector<uint8>* first_key) {
+ const std::string license_as_str(
+ reinterpret_cast<const char*>(!license.empty() ? &license[0] : NULL),
+ license.size());
+ if (!base::IsStringASCII(license_as_str))
+ return false;
+
+ scoped_ptr<base::Value> root(base::JSONReader().ReadToValue(license_as_str));
+ if (!root.get() || root->GetType() != base::Value::TYPE_DICTIONARY)
+ return false;
+
+ // Locate the set from the dictionary.
+ base::DictionaryValue* dictionary =
+ static_cast<base::DictionaryValue*>(root.get());
+ base::ListValue* list_val = NULL;
+ if (!dictionary->GetList(kKeyIdsTag, &list_val)) {
+ DVLOG(1) << "Missing '" << kKeyIdsTag << "' parameter or not a list";
+ return false;
+ }
+
+ // Get the first key.
+ if (list_val->GetSize() < 1) {
+ DVLOG(1) << "Empty '" << kKeyIdsTag << "' list";
+ return false;
+ }
+
+ std::string encoded_key;
+ if (!list_val->GetString(0, &encoded_key)) {
+ DVLOG(1) << "First entry in '" << kKeyIdsTag << "' not a string";
+ return false;
+ }
+
+ std::string decoded_string = DecodeBase64(encoded_key);
+ if (decoded_string.empty()) {
+ DVLOG(1) << "Invalid '" << kKeyIdsTag << "' value: " << encoded_key;
+ return false;
+ }
+
+ std::vector<uint8> result(decoded_string.begin(), decoded_string.end());
+ first_key->swap(result);
+ return true;
+}
+
} // namespace media
diff --git a/media/cdm/json_web_key.h b/media/cdm/json_web_key.h
index cb483aeb8b..af028f2fe2 100644
--- a/media/cdm/json_web_key.h
+++ b/media/cdm/json_web_key.h
@@ -11,21 +11,42 @@
#include "base/basictypes.h"
#include "media/base/media_export.h"
+#include "media/base/media_keys.h"
namespace media {
+// The ClearKey license request format (ref:
+// https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html#clear-key)
+// is a JSON object containing the following members:
+// "kids" : An array of key IDs. Each element of the array is the base64url
+// encoding of the octet sequence containing the key ID value.
+// "type" : The requested SessionType.
+// An example:
+// { "kids":["67ef0gd8pvfd0","77ef0gd8pvfd0"], "type":"temporary" }
+
+// The ClearKey license format is a JSON Web Key (JWK) Set containing
+// representation of the symmetric key to be used for decryption.
+// For each JWK in the set, the parameter values are as follows:
+// "kty" (key type) : "oct" (octet sequence)
+// "alg" (algorithm) : "A128KW" (AES key wrap using a 128-bit key)
+// "k" (key value) : The base64url encoding of the octet sequence
+// containing the symmetric key value.
+// "kid" (key ID) : The base64url encoding of the octet sequence
+// containing the key ID value.
+// The JSON object may have an optional "type" member value, which may be
+// any of the SessionType values. If not specified, the default value of
+// "temporary" is used.
// A JSON Web Key Set looks like the following in JSON:
-// { "keys": [ JWK1, JWK2, ... ] }
+// { "keys": [ JWK1, JWK2, ... ], "type":"temporary" }
// A symmetric keys JWK looks like the following in JSON:
// { "kty":"oct",
+// "alg":"A128KW",
// "kid":"AQIDBAUGBwgJCgsMDQ4PEA",
// "k":"FBUWFxgZGhscHR4fICEiIw" }
+
// There may be other properties specified, but they are ignored.
// Ref: http://tools.ietf.org/html/draft-ietf-jose-json-web-key and:
// http://tools.ietf.org/html/draft-jones-jose-json-private-and-symmetric-key
-//
-// For EME WD, both 'kid' and 'k' are base64 encoded strings, without trailing
-// padding.
// Vector of [key_id, key_value] pairs. Values are raw binary data, stored in
// strings for convenience.
@@ -37,10 +58,27 @@ MEDIA_EXPORT std::string GenerateJWKSet(const uint8* key, int key_length,
const uint8* key_id, int key_id_length);
// Extracts the JSON Web Keys from a JSON Web Key Set. If |input| looks like
-// a valid JWK Set, then true is returned and |keys| is updated to contain
-// the list of keys found. Otherwise return false.
+// a valid JWK Set, then true is returned and |keys| and |session_type| are
+// updated to contain the values found. Otherwise return false.
MEDIA_EXPORT bool ExtractKeysFromJWKSet(const std::string& jwk_set,
- KeyIdAndKeyPairs* keys);
+ KeyIdAndKeyPairs* keys,
+ MediaKeys::SessionType* session_type);
+
+// Create a license request message for the |key_id| and |session_type|
+// specified. Currently ClearKey generates a message for each key individually,
+// so no need to take a list of |key_id|'s. |license| is updated to contain the
+// resulting JSON string.
+MEDIA_EXPORT void CreateLicenseRequest(const uint8* key_id,
+ int key_id_length,
+ MediaKeys::SessionType session_type,
+ std::vector<uint8>* license);
+
+// Extract the first key from the license request message. Returns true if
+// |license| is a valid license request and contains at least one key,
+// otherwise false and |first_key| is not touched.
+MEDIA_EXPORT bool ExtractFirstKeyIdFromLicenseRequest(
+ const std::vector<uint8>& license,
+ std::vector<uint8>* first_key);
} // namespace media
diff --git a/media/cdm/json_web_key_unittest.cc b/media/cdm/json_web_key_unittest.cc
index 1018d173c7..49ea32bd45 100644
--- a/media/cdm/json_web_key_unittest.cc
+++ b/media/cdm/json_web_key_unittest.cc
@@ -19,9 +19,50 @@ class JSONWebKeyTest : public testing::Test {
size_t expected_number_of_keys) {
DCHECK(!jwk.empty());
KeyIdAndKeyPairs keys;
- EXPECT_EQ(expected_result, ExtractKeysFromJWKSet(jwk, &keys));
+ MediaKeys::SessionType session_type;
+ EXPECT_EQ(expected_result,
+ ExtractKeysFromJWKSet(jwk, &keys, &session_type));
EXPECT_EQ(expected_number_of_keys, keys.size());
}
+
+ void ExtractSessionTypeAndExpect(const std::string& jwk,
+ bool expected_result,
+ MediaKeys::SessionType expected_type) {
+ DCHECK(!jwk.empty());
+ KeyIdAndKeyPairs keys;
+ MediaKeys::SessionType session_type;
+ EXPECT_EQ(expected_result,
+ ExtractKeysFromJWKSet(jwk, &keys, &session_type));
+ if (expected_result) {
+ // Only check if successful.
+ EXPECT_EQ(expected_type, session_type);
+ }
+ }
+
+ void CreateLicenseAndExpect(const uint8* key_id,
+ int key_id_length,
+ MediaKeys::SessionType session_type,
+ const std::string& expected_result) {
+ std::vector<uint8> result;
+ CreateLicenseRequest(key_id, key_id_length, session_type, &result);
+ std::string s(result.begin(), result.end());
+ EXPECT_EQ(expected_result, s);
+ }
+
+ void ExtractKeyFromLicenseAndExpect(const std::string& license,
+ bool expected_result,
+ const uint8* expected_key,
+ int expected_key_length) {
+ std::vector<uint8> license_vector(license.begin(), license.end());
+ std::vector<uint8> key;
+ EXPECT_EQ(expected_result,
+ ExtractFirstKeyIdFromLicenseRequest(license_vector, &key));
+ if (expected_result) {
+ std::vector<uint8> key_result(expected_key,
+ expected_key + expected_key_length);
+ EXPECT_EQ(key_result, key);
+ }
+ }
};
TEST_F(JSONWebKeyTest, GenerateJWKSet) {
@@ -182,5 +223,99 @@ TEST_F(JSONWebKeyTest, ExtractJWKKeys) {
ExtractJWKKeysAndExpect(kJwksDuplicateKids, true, 2);
}
+TEST_F(JSONWebKeyTest, SessionType) {
+ ExtractSessionTypeAndExpect(
+ "{\"keys\":[{\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}]}",
+ true,
+ MediaKeys::TEMPORARY_SESSION);
+ ExtractSessionTypeAndExpect(
+ "{\"keys\":[{\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}],\"type\":"
+ "\"temporary\"}",
+ true,
+ MediaKeys::TEMPORARY_SESSION);
+ ExtractSessionTypeAndExpect(
+ "{\"keys\":[{\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}],\"type\":"
+ "\"persistent\"}",
+ true,
+ MediaKeys::PERSISTENT_SESSION);
+ ExtractSessionTypeAndExpect(
+ "{\"keys\":[{\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}],\"type\":"
+ "\"unknown\"}",
+ false,
+ MediaKeys::TEMPORARY_SESSION);
+ ExtractSessionTypeAndExpect(
+ "{\"keys\":[{\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}],\"type\":3}",
+ false,
+ MediaKeys::TEMPORARY_SESSION);
+}
+
+TEST_F(JSONWebKeyTest, CreateLicense) {
+ const uint8 data1[] = { 0x01, 0x02 };
+ const uint8 data2[] = { 0x01, 0x02, 0x03, 0x04 };
+ const uint8 data3[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 };
+
+ CreateLicenseAndExpect(data1,
+ arraysize(data1),
+ MediaKeys::TEMPORARY_SESSION,
+ "{\"kids\":[\"AQI\"],\"type\":\"temporary\"}");
+ CreateLicenseAndExpect(data1,
+ arraysize(data1),
+ MediaKeys::PERSISTENT_SESSION,
+ "{\"kids\":[\"AQI\"],\"type\":\"persistent\"}");
+ CreateLicenseAndExpect(data2,
+ arraysize(data2),
+ MediaKeys::TEMPORARY_SESSION,
+ "{\"kids\":[\"AQIDBA\"],\"type\":\"temporary\"}");
+ CreateLicenseAndExpect(
+ data3,
+ arraysize(data3),
+ MediaKeys::PERSISTENT_SESSION,
+ "{\"kids\":[\"AQIDBAUGBwgJCgsMDQ4PEA\"],\"type\":\"persistent\"}");
+}
+
+TEST_F(JSONWebKeyTest, ExtractLicense) {
+ const uint8 data1[] = { 0x01, 0x02 };
+ const uint8 data2[] = { 0x01, 0x02, 0x03, 0x04 };
+ const uint8 data3[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 };
+
+ ExtractKeyFromLicenseAndExpect(
+ "{\"kids\":[\"AQI\"],\"type\":\"temporary\"}",
+ true,
+ data1,
+ arraysize(data1));
+ ExtractKeyFromLicenseAndExpect(
+ "{\"kids\":[\"AQIDBA\"],\"type\":\"temporary\"}",
+ true,
+ data2,
+ arraysize(data2));
+ ExtractKeyFromLicenseAndExpect(
+ "{\"kids\":[\"AQIDBAUGBwgJCgsMDQ4PEA\"],\"type\":\"persistent\"}",
+ true,
+ data3,
+ arraysize(data3));
+
+ // Try some incorrect JSON.
+ ExtractKeyFromLicenseAndExpect("", false, NULL, 0);
+ ExtractKeyFromLicenseAndExpect("!@#$%^&*()", false, NULL, 0);
+
+ // Valid JSON, but not a dictionary.
+ ExtractKeyFromLicenseAndExpect("6", false, NULL, 0);
+ ExtractKeyFromLicenseAndExpect("[\"AQI\"]", false, NULL, 0);
+
+ // Dictionary, but missing expected tag.
+ ExtractKeyFromLicenseAndExpect("{\"kid\":[\"AQI\"]}", false, NULL, 0);
+
+ // Correct tag, but empty list.
+ ExtractKeyFromLicenseAndExpect("{\"kids\":[]}", false, NULL, 0);
+
+ // Correct tag, but list doesn't contain a string.
+ ExtractKeyFromLicenseAndExpect("{\"kids\":[[\"AQI\"]]}", false, NULL, 0);
+
+ // Correct tag, but invalid base64 encoding.
+ ExtractKeyFromLicenseAndExpect("{\"kids\":[\"!@#$%^&*()\"]}", false, NULL, 0);
+}
+
} // namespace media
diff --git a/media/cdm/ppapi/cdm_wrapper.h b/media/cdm/ppapi/cdm_wrapper.h
index 708cb5713d..72afa90a02 100644
--- a/media/cdm/ppapi/cdm_wrapper.h
+++ b/media/cdm/ppapi/cdm_wrapper.h
@@ -130,8 +130,8 @@ class CdmWrapper {
// Prior to CDM_6, |init_data_type| was a content type. This helper convererts
// an |init_data_type| to a content type.
// TODO(sandersd): Remove once Host_4 and Host_5 interfaces are removed.
- virtual const char* ConvertInitDataTypeToContentType(
- const char* init_data_type) const = 0;
+ virtual std::string ConvertInitDataTypeToContentType(
+ const std::string& init_data_type) const = 0;
protected:
CdmWrapper() {}
@@ -368,11 +368,11 @@ class CdmWrapperImpl : public CdmWrapper {
v1->timestamp = v2.timestamp;
}
- virtual const char* ConvertInitDataTypeToContentType(
- const char* init_data_type) const {
- if (!strcmp(init_data_type, "cenc"))
+ virtual std::string ConvertInitDataTypeToContentType(
+ const std::string& init_data_type) const {
+ if (init_data_type == "cenc")
return "video/mp4";
- if (!strcmp(init_data_type, "webm"))
+ if (init_data_type == "webm")
return "video/webm";
return init_data_type;
}
@@ -413,9 +413,11 @@ void CdmWrapperImpl<cdm::ContentDecryptionModule_4>::CreateSession(
cdm::SessionType session_type) {
uint32_t session_id = CreateSessionId();
RegisterPromise(session_id, promise_id);
+ std::string converted_init_data_type = ConvertInitDataTypeToContentType(
+ std::string(init_data_type, init_data_type_size));
cdm_->CreateSession(session_id,
- ConvertInitDataTypeToContentType(init_data_type),
- init_data_type_size,
+ converted_init_data_type.data(),
+ converted_init_data_type.length(),
init_data,
init_data_size);
}
@@ -518,14 +520,16 @@ void CdmWrapperImpl<cdm::ContentDecryptionModule_5>::CreateSession(
const uint8_t* init_data,
uint32_t init_data_size,
cdm::SessionType session_type) {
+ std::string converted_init_data_type = ConvertInitDataTypeToContentType(
+ std::string(init_data_type, init_data_type_size));
// TODO(jrummell): Remove this code once |session_type| is passed through
// Pepper. When removing, add the header back in for CDM4.
PP_DCHECK(session_type == cdm::kTemporary);
const char kPersistentSessionHeader[] = "PERSISTENT|";
if (HasHeader(init_data, init_data_size, kPersistentSessionHeader)) {
cdm_->CreateSession(promise_id,
- ConvertInitDataTypeToContentType(init_data_type),
- init_data_type_size,
+ converted_init_data_type.data(),
+ converted_init_data_type.length(),
init_data + strlen(kPersistentSessionHeader),
init_data_size - strlen(kPersistentSessionHeader),
cdm::kPersistent);
@@ -533,8 +537,8 @@ void CdmWrapperImpl<cdm::ContentDecryptionModule_5>::CreateSession(
}
cdm_->CreateSession(promise_id,
- ConvertInitDataTypeToContentType(init_data_type),
- init_data_type_size,
+ converted_init_data_type.data(),
+ converted_init_data_type.length(),
init_data,
init_data_size,
session_type);
diff --git a/media/filters/decoder_stream.cc b/media/filters/decoder_stream.cc
index 4517290141..0e11c4ecbc 100644
--- a/media/filters/decoder_stream.cc
+++ b/media/filters/decoder_stream.cc
@@ -227,16 +227,17 @@ void DecoderStream<StreamType>::OnDecoderSelected(
if (!selected_decoder) {
state_ = STATE_UNINITIALIZED;
- StreamTraits::FinishInitialization(
- base::ResetAndReturn(&init_cb_), selected_decoder.get(), stream_);
+ base::ResetAndReturn(&init_cb_).Run(false);
return;
}
state_ = STATE_NORMAL;
decoder_ = selected_decoder.Pass();
decrypting_demuxer_stream_ = decrypting_demuxer_stream.Pass();
- StreamTraits::FinishInitialization(
- base::ResetAndReturn(&init_cb_), decoder_.get(), stream_);
+
+ if (StreamTraits::NeedsBitstreamConversion(decoder_.get()))
+ stream_->EnableBitstreamConverter();
+ base::ResetAndReturn(&init_cb_).Run(true);
}
template <DemuxerStream::Type StreamType>
diff --git a/media/filters/decoder_stream.h b/media/filters/decoder_stream.h
index c077a1830e..d6ee126634 100644
--- a/media/filters/decoder_stream.h
+++ b/media/filters/decoder_stream.h
@@ -192,10 +192,6 @@ class MEDIA_EXPORT DecoderStream {
// NOTE: Weak pointers must be invalidated before all other member variables.
base::WeakPtrFactory<DecoderStream<StreamType> > weak_factory_;
-
- // This is required so the VideoFrameStream can access private members in
- // FinishInitialization() and ReportStatistics().
- DISALLOW_IMPLICIT_CONSTRUCTORS(DecoderStream);
};
template <>
diff --git a/media/filters/decoder_stream_traits.cc b/media/filters/decoder_stream_traits.cc
index c86862f7fa..824b912825 100644
--- a/media/filters/decoder_stream_traits.cc
+++ b/media/filters/decoder_stream_traits.cc
@@ -27,19 +27,6 @@ void DecoderStreamTraits<DemuxerStream::AUDIO>::Initialize(
decoder->Initialize(config, status_cb, output_cb);
}
-bool DecoderStreamTraits<DemuxerStream::AUDIO>::FinishInitialization(
- const StreamInitCB& init_cb,
- DecoderType* decoder,
- DemuxerStream* stream) {
- DCHECK(stream);
- if (!decoder) {
- init_cb.Run(false);
- return false;
- }
- init_cb.Run(true);
- return true;
-}
-
void DecoderStreamTraits<DemuxerStream::AUDIO>::ReportStatistics(
const StatisticsCB& statistics_cb,
int bytes_decoded) {
@@ -72,19 +59,9 @@ void DecoderStreamTraits<DemuxerStream::VIDEO>::Initialize(
decoder->Initialize(config, low_delay, status_cb, output_cb);
}
-bool DecoderStreamTraits<DemuxerStream::VIDEO>::FinishInitialization(
- const StreamInitCB& init_cb,
- DecoderType* decoder,
- DemuxerStream* stream) {
- DCHECK(stream);
- if (!decoder) {
- init_cb.Run(false);
- return false;
- }
- if (decoder->NeedsBitstreamConversion())
- stream->EnableBitstreamConverter();
- init_cb.Run(true);
- return true;
+bool DecoderStreamTraits<DemuxerStream::VIDEO>::NeedsBitstreamConversion(
+ DecoderType* decoder) {
+ return decoder->NeedsBitstreamConversion();
}
void DecoderStreamTraits<DemuxerStream::VIDEO>::ReportStatistics(
diff --git a/media/filters/decoder_stream_traits.h b/media/filters/decoder_stream_traits.h
index 59e534d2e6..c995962b44 100644
--- a/media/filters/decoder_stream_traits.h
+++ b/media/filters/decoder_stream_traits.h
@@ -36,9 +36,7 @@ struct DecoderStreamTraits<DemuxerStream::AUDIO> {
bool low_delay,
const PipelineStatusCB& status_cb,
const OutputCB& output_cb);
- static bool FinishInitialization(const StreamInitCB& init_cb,
- DecoderType* decoder,
- DemuxerStream* stream);
+ static bool NeedsBitstreamConversion(DecoderType* decoder) { return false; }
static void ReportStatistics(const StatisticsCB& statistics_cb,
int bytes_decoded);
static DecoderConfigType GetDecoderConfig(DemuxerStream& stream);
@@ -60,9 +58,7 @@ struct DecoderStreamTraits<DemuxerStream::VIDEO> {
bool low_delay,
const PipelineStatusCB& status_cb,
const OutputCB& output_cb);
- static bool FinishInitialization(const StreamInitCB& init_cb,
- DecoderType* decoder,
- DemuxerStream* stream);
+ static bool NeedsBitstreamConversion(DecoderType* decoder);
static void ReportStatistics(const StatisticsCB& statistics_cb,
int bytes_decoded);
static DecoderConfigType GetDecoderConfig(DemuxerStream& stream);
diff --git a/media/filters/gpu_video_decoder.cc b/media/filters/gpu_video_decoder.cc
index 16a33eae54..3954e09406 100644
--- a/media/filters/gpu_video_decoder.cc
+++ b/media/filters/gpu_video_decoder.cc
@@ -422,8 +422,22 @@ void GpuVideoDecoder::PictureReady(const media::Picture& picture) {
}
const PictureBuffer& pb = it->second;
+ // Validate picture rectangle from GPU. This is for sanity/security check
+ // even the rectangle is not used in this class.
+ if (picture.visible_rect().IsEmpty() ||
+ !gfx::Rect(pb.size()).Contains(picture.visible_rect())) {
+ NOTREACHED() << "Invalid picture size from VDA: "
+ << picture.visible_rect().ToString() << " should fit in "
+ << pb.size().ToString();
+ NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE);
+ return;
+ }
+
// Update frame's timestamp.
base::TimeDelta timestamp;
+ // Some of the VDAs don't support and thus don't provide us with visible
+ // size in picture.size, passing coded size instead, so always drop it and
+ // use config information instead.
gfx::Rect visible_rect;
gfx::Size natural_size;
GetBufferData(picture.bitstream_buffer_id(), &timestamp, &visible_rect,
diff --git a/media/filters/skcanvas_video_renderer.cc b/media/filters/skcanvas_video_renderer.cc
index fe1bfde044..003c1951a9 100644
--- a/media/filters/skcanvas_video_renderer.cc
+++ b/media/filters/skcanvas_video_renderer.cc
@@ -190,6 +190,7 @@ void SkCanvasVideoRenderer::Paint(media::VideoFrame* video_frame,
SkCanvas* canvas,
const gfx::RectF& dest_rect,
uint8 alpha,
+ SkXfermode::Mode mode,
VideoRotation video_rotation) {
if (alpha == 0) {
return;
@@ -233,13 +234,21 @@ void SkCanvasVideoRenderer::Paint(media::VideoFrame* video_frame,
last_frame_timestamp_ = video_frame->timestamp();
}
- // Use SRC mode so we completely overwrite the buffer (in case we have alpha)
- // this means we don't need the extra cost of clearing the buffer first.
- paint.setXfermode(SkXfermode::Create(SkXfermode::kSrc_Mode));
+ paint.setXfermodeMode(mode);
// Paint using |last_frame_|.
paint.setFilterLevel(SkPaint::kLow_FilterLevel);
canvas->drawBitmapRect(last_frame_, NULL, dest, &paint);
}
+void SkCanvasVideoRenderer::Copy(media::VideoFrame* video_frame,
+ SkCanvas* canvas) {
+ Paint(video_frame,
+ canvas,
+ video_frame->visible_rect(),
+ 0xff,
+ SkXfermode::kSrc_Mode,
+ media::VIDEO_ROTATION_0);
+}
+
} // namespace media
diff --git a/media/filters/skcanvas_video_renderer.h b/media/filters/skcanvas_video_renderer.h
index 0e5c520339..3d1f4da56f 100644
--- a/media/filters/skcanvas_video_renderer.h
+++ b/media/filters/skcanvas_video_renderer.h
@@ -9,6 +9,7 @@
#include "media/base/media_export.h"
#include "media/base/video_rotation.h"
#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkXfermode.h"
#include "ui/gfx/rect.h"
class SkCanvas;
@@ -32,8 +33,12 @@ class MEDIA_EXPORT SkCanvasVideoRenderer {
SkCanvas* canvas,
const gfx::RectF& dest_rect,
uint8 alpha,
+ SkXfermode::Mode mode,
VideoRotation video_rotation);
+ // Copy |video_frame| on |canvas|.
+ void Copy(media::VideoFrame* video_frame, SkCanvas* canvas);
+
private:
// An RGB bitmap and corresponding timestamp of the previously converted
// video frame data.
diff --git a/media/filters/skcanvas_video_renderer_unittest.cc b/media/filters/skcanvas_video_renderer_unittest.cc
index 2a8b174837..358ce0ab1e 100644
--- a/media/filters/skcanvas_video_renderer_unittest.cc
+++ b/media/filters/skcanvas_video_renderer_unittest.cc
@@ -56,8 +56,11 @@ class SkCanvasVideoRendererTest : public testing::Test {
void PaintRotated(VideoFrame* video_frame,
SkCanvas* canvas,
Color color,
+ SkXfermode::Mode mode,
VideoRotation video_rotation);
+ void Copy(VideoFrame* video_frame, SkCanvas* canvas);
+
// Getters for various frame sizes.
VideoFrame* natural_frame() { return natural_frame_.get(); }
VideoFrame* larger_frame() { return larger_frame_.get(); }
@@ -189,18 +192,25 @@ SkCanvasVideoRendererTest::SkCanvasVideoRendererTest()
SkCanvasVideoRendererTest::~SkCanvasVideoRendererTest() {}
void SkCanvasVideoRendererTest::PaintWithoutFrame(SkCanvas* canvas) {
- renderer_.Paint(NULL, canvas, kNaturalRect, 0xFF, VIDEO_ROTATION_0);
+ renderer_.Paint(NULL,
+ canvas,
+ kNaturalRect,
+ 0xFF,
+ SkXfermode::kSrcOver_Mode,
+ VIDEO_ROTATION_0);
}
void SkCanvasVideoRendererTest::Paint(VideoFrame* video_frame,
SkCanvas* canvas,
Color color) {
- PaintRotated(video_frame, canvas, color, VIDEO_ROTATION_0);
+ PaintRotated(
+ video_frame, canvas, color, SkXfermode::kSrcOver_Mode, VIDEO_ROTATION_0);
}
void SkCanvasVideoRendererTest::PaintRotated(VideoFrame* video_frame,
SkCanvas* canvas,
Color color,
+ SkXfermode::Mode mode,
VideoRotation video_rotation) {
switch (color) {
case kNone:
@@ -215,7 +225,13 @@ void SkCanvasVideoRendererTest::PaintRotated(VideoFrame* video_frame,
media::FillYUV(video_frame, 29, 255, 107);
break;
}
- renderer_.Paint(video_frame, canvas, kNaturalRect, 0xFF, video_rotation);
+ renderer_.Paint(
+ video_frame, canvas, kNaturalRect, 0xFF, mode, video_rotation);
+}
+
+void SkCanvasVideoRendererTest::Copy(VideoFrame* video_frame,
+ SkCanvas* canvas) {
+ renderer_.Copy(video_frame, canvas);
}
TEST_F(SkCanvasVideoRendererTest, NoFrame) {
@@ -226,11 +242,31 @@ TEST_F(SkCanvasVideoRendererTest, NoFrame) {
}
TEST_F(SkCanvasVideoRendererTest, TransparentFrame) {
- // Test that we don't blend with existing canvas contents.
FillCanvas(target_canvas(), SK_ColorRED);
- Paint(VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)),
- target_canvas(),
- kNone);
+ PaintRotated(VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)),
+ target_canvas(),
+ kNone,
+ SkXfermode::kSrcOver_Mode,
+ VIDEO_ROTATION_0);
+ EXPECT_EQ(static_cast<SkColor>(SK_ColorRED), GetColor(target_canvas()));
+}
+
+TEST_F(SkCanvasVideoRendererTest, TransparentFrameSrcMode) {
+ FillCanvas(target_canvas(), SK_ColorRED);
+ // SRC mode completely overwrites the buffer.
+ PaintRotated(VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)),
+ target_canvas(),
+ kNone,
+ SkXfermode::kSrc_Mode,
+ VIDEO_ROTATION_0);
+ EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
+ GetColor(target_canvas()));
+}
+
+TEST_F(SkCanvasVideoRendererTest, CopyTransparentFrame) {
+ FillCanvas(target_canvas(), SK_ColorRED);
+ Copy(VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)),
+ target_canvas());
EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
GetColor(target_canvas()));
}
@@ -325,7 +361,11 @@ TEST_F(SkCanvasVideoRendererTest, CroppedFrame_NoScaling) {
TEST_F(SkCanvasVideoRendererTest, Video_Rotation_90) {
SkCanvas canvas(AllocBitmap(kWidth, kHeight));
const gfx::Rect crop_rect = cropped_frame()->visible_rect();
- PaintRotated(cropped_frame(), &canvas, kNone, VIDEO_ROTATION_90);
+ PaintRotated(cropped_frame(),
+ &canvas,
+ kNone,
+ SkXfermode::kSrcOver_Mode,
+ VIDEO_ROTATION_90);
// Check the corners.
EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, 0, 0));
EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, kWidth - 1, 0));
@@ -336,7 +376,11 @@ TEST_F(SkCanvasVideoRendererTest, Video_Rotation_90) {
TEST_F(SkCanvasVideoRendererTest, Video_Rotation_180) {
SkCanvas canvas(AllocBitmap(kWidth, kHeight));
const gfx::Rect crop_rect = cropped_frame()->visible_rect();
- PaintRotated(cropped_frame(), &canvas, kNone, VIDEO_ROTATION_180);
+ PaintRotated(cropped_frame(),
+ &canvas,
+ kNone,
+ SkXfermode::kSrcOver_Mode,
+ VIDEO_ROTATION_180);
// Check the corners.
EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, 0, 0));
EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, kWidth - 1, 0));
@@ -347,7 +391,11 @@ TEST_F(SkCanvasVideoRendererTest, Video_Rotation_180) {
TEST_F(SkCanvasVideoRendererTest, Video_Rotation_270) {
SkCanvas canvas(AllocBitmap(kWidth, kHeight));
const gfx::Rect crop_rect = cropped_frame()->visible_rect();
- PaintRotated(cropped_frame(), &canvas, kNone, VIDEO_ROTATION_270);
+ PaintRotated(cropped_frame(),
+ &canvas,
+ kNone,
+ SkXfermode::kSrcOver_Mode,
+ VIDEO_ROTATION_270);
// Check the corners.
EXPECT_EQ(SK_ColorRED, GetColorAt(&canvas, 0, 0));
EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, kWidth - 1, 0));
diff --git a/media/formats/mp2t/es_parser.h b/media/formats/mp2t/es_parser.h
index 96785eafb7..a1959c20a3 100644
--- a/media/formats/mp2t/es_parser.h
+++ b/media/formats/mp2t/es_parser.h
@@ -9,6 +9,7 @@
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/time/time.h"
+#include "media/base/media_export.h"
#include "media/base/stream_parser_buffer.h"
namespace media {
@@ -17,7 +18,7 @@ class StreamParserBuffer;
namespace mp2t {
-class EsParser {
+class MEDIA_EXPORT EsParser {
public:
typedef base::Callback<void(scoped_refptr<StreamParserBuffer>)> EmitBufferCB;
diff --git a/media/formats/mp2t/es_parser_h264.h b/media/formats/mp2t/es_parser_h264.h
index 82499f5ec5..df09c88689 100644
--- a/media/formats/mp2t/es_parser_h264.h
+++ b/media/formats/mp2t/es_parser_h264.h
@@ -32,7 +32,7 @@ namespace mp2t {
// Mpeg2 TS spec: "2.14 Carriage of Rec. ITU-T H.264 | ISO/IEC 14496-10 video"
// "Each AVC access unit shall contain an access unit delimiter NAL Unit;"
//
-class MEDIA_EXPORT EsParserH264 : NON_EXPORTED_BASE(public EsParser) {
+class MEDIA_EXPORT EsParserH264 : public EsParser {
public:
typedef base::Callback<void(const VideoDecoderConfig&)> NewVideoConfigCB;
diff --git a/media/test/data/eme_player_js/clearkey_player.js b/media/test/data/eme_player_js/clearkey_player.js
index 6de8408ce5..92580f3049 100644
--- a/media/test/data/eme_player_js/clearkey_player.js
+++ b/media/test/data/eme_player_js/clearkey_player.js
@@ -20,7 +20,7 @@ ClearKeyPlayer.prototype.registerEventListeners = function() {
ClearKeyPlayer.prototype.onMessage = function(message) {
Utils.timeLog('MediaKeySession onMessage', message);
var initData =
- Utils.getInitDataFromMessage(message, this.testConfig.mediaType);
+ Utils.getInitDataFromMessage(message, this.testConfig.mediaType, true);
var key = Utils.getDefaultKey(this.testConfig.forceInvalidResponse);
var jwkSet = Utils.createJWKData(initData, key);
if (PROMISES_SUPPORTED) {
diff --git a/media/test/data/eme_player_js/prefixed_clearkey_player.js b/media/test/data/eme_player_js/prefixed_clearkey_player.js
index cbca7d3305..910a126bf7 100644
--- a/media/test/data/eme_player_js/prefixed_clearkey_player.js
+++ b/media/test/data/eme_player_js/prefixed_clearkey_player.js
@@ -19,7 +19,7 @@ PrefixedClearKeyPlayer.prototype.registerEventListeners = function() {
PrefixedClearKeyPlayer.prototype.onWebkitKeyMessage = function(message) {
var initData =
- Utils.getInitDataFromMessage(message, this.testConfig.mediaType);
+ Utils.getInitDataFromMessage(message, this.testConfig.mediaType, false);
var key = Utils.getDefaultKey(this.testConfig.forceInvalidResponse);
Utils.timeLog('Adding key to sessionID: ' + message.sessionId);
message.target.webkitAddKey(this.testConfig.keySystem, key, initData,
diff --git a/media/test/data/eme_player_js/utils.js b/media/test/data/eme_player_js/utils.js
index 491bac7892..ca62cd1f7b 100644
--- a/media/test/data/eme_player_js/utils.js
+++ b/media/test/data/eme_player_js/utils.js
@@ -79,6 +79,28 @@ Utils.createJWKData = function(keyId, key) {
return Utils.convertToUint8Array(createJWKSet(createJWK(keyId, key)));
};
+Utils.extractFirstLicenseKey = function(message) {
+ // Decodes data (Uint8Array) from base64 string.
+ // TODO(jrummell): Update once the EME spec is updated to say base64url
+ // encoding.
+ function base64Decode(data) {
+ return atob(data);
+ }
+
+ function convertToString(data) {
+ return String.fromCharCode.apply(null, Utils.convertToUint8Array(data));
+ }
+
+ try {
+ var json = JSON.parse(convertToString(message));
+ // Decode the first element of 'kids', return it as an Uint8Array.
+ return Utils.convertToUint8Array(base64Decode(json.kids[0]));
+ } catch (error) {
+ // Not valid JSON, so return message untouched as Uint8Array.
+ return Utils.convertToUint8Array(message);
+ }
+}
+
Utils.documentLog = function(log, success, time) {
if (!docLogs)
return;
@@ -154,13 +176,17 @@ Utils.getHexString = function(uintArray) {
return hex_str;
};
-Utils.getInitDataFromMessage = function(message, mediaType) {
- var initData = Utils.convertToUint8Array(message.message);
+Utils.getInitDataFromMessage = function(message, mediaType, decodeJSONMessage) {
+ var initData;
if (mediaType.indexOf('mp4') != -1) {
// Temporary hack for Clear Key in v0.1.
// If content uses mp4, then message.message is PSSH data. Instead of
// parsing that data we hard code the initData.
initData = Utils.convertToUint8Array(KEY_ID);
+ } else if (decodeJSONMessage) {
+ initData = Utils.extractFirstLicenseKey(message.message);
+ } else {
+ initData = Utils.convertToUint8Array(message.message);
}
return initData;
};
diff --git a/media/video/capture/mac/platform_video_capturing_mac.h b/media/video/capture/mac/platform_video_capturing_mac.h
index 29bc49715c..33ad7b6e54 100644
--- a/media/video/capture/mac/platform_video_capturing_mac.h
+++ b/media/video/capture/mac/platform_video_capturing_mac.h
@@ -21,7 +21,7 @@ class VideoCaptureDeviceMac;
// implementation.
- (id)initWithFrameReceiver:(media::VideoCaptureDeviceMac*)frameReceiver;
-// Set the frame receiver. This method executes the registration in mutual
+// Sets the frame receiver. This method executes the registration in mutual
// exclusion.
// TODO(mcasas): This method and stopCapture() are always called in sequence and
// this one is only used to clear the frameReceiver, investigate if both can be
@@ -31,8 +31,9 @@ class VideoCaptureDeviceMac;
// Sets which capture device to use by name passed as deviceId argument. The
// device names are usually obtained via VideoCaptureDevice::GetDeviceNames()
// method. This method will also configure all device properties except those in
-// setCaptureHeight:widht:frameRate. If |deviceId| is nil, all potential
-// configuration is torn down. Returns YES on sucess, NO otherwise.
+// setCaptureHeight:width:frameRate. If |deviceId| is nil, capture is stopped
+// and all potential configuration is torn down. Returns YES on sucess, NO
+// otherwise.
- (BOOL)setCaptureDevice:(NSString*)deviceId;
// Configures the capture properties.
@@ -40,7 +41,7 @@ class VideoCaptureDeviceMac;
width:(int)width
frameRate:(float)frameRate;
-// Start video capturing, register observers. Returns YES on sucess, NO
+// Starts video capturing, registers observers. Returns YES on sucess, NO
// otherwise.
- (BOOL)startCapture;
diff --git a/media/video/capture/mac/video_capture_device_mac.h b/media/video/capture/mac/video_capture_device_mac.h
index 60da1396d5..7bda9e31e0 100644
--- a/media/video/capture/mac/video_capture_device_mac.h
+++ b/media/video/capture/mac/video_capture_device_mac.h
@@ -73,11 +73,14 @@ class VideoCaptureDeviceMac : public VideoCaptureDevice {
int aspect_numerator,
int aspect_denominator);
+ // Forwarder to VideoCaptureDevice::Client::OnError().
void ReceiveError(const std::string& reason);
+ // Forwarder to VideoCaptureDevice::Client::OnLog().
+ void LogMessage(const std::string& message);
+
private:
void SetErrorState(const std::string& reason);
- void LogMessage(const std::string& message);
bool UpdateCaptureResolution();
// Flag indicating the internal state.
diff --git a/media/video/capture/mac/video_capture_device_mac.mm b/media/video/capture/mac/video_capture_device_mac.mm
index 2c1943b0b9..66019b7f64 100644
--- a/media/video/capture/mac/video_capture_device_mac.mm
+++ b/media/video/capture/mac/video_capture_device_mac.mm
@@ -440,7 +440,6 @@ void VideoCaptureDeviceMac::AllocateAndStart(
void VideoCaptureDeviceMac::StopAndDeAllocate() {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(state_ == kCapturing || state_ == kError) << state_;
- [capture_device_ stopCapture];
[capture_device_ setCaptureDevice:nil];
[capture_device_ setFrameReceiver:nil];
diff --git a/media/video/capture/mac/video_capture_device_qtkit_mac.mm b/media/video/capture/mac/video_capture_device_qtkit_mac.mm
index 7f4a853c63..dec7415874 100644
--- a/media/video/capture/mac/video_capture_device_qtkit_mac.mm
+++ b/media/video/capture/mac/video_capture_device_qtkit_mac.mm
@@ -140,32 +140,14 @@
} else {
// Remove the previously set capture device.
if (!captureDeviceInput_) {
- [self sendErrorString:[NSString
+ // Being here means stopping a device that never started OK in the first
+ // place, log it.
+ [self sendLogString:[NSString
stringWithUTF8String:"No video capture device set, on removal."]];
return YES;
}
- if ([[captureSession_ inputs] count] > 0) {
- // The device is still running.
- [self stopCapture];
- }
- if ([[captureSession_ outputs] count] > 0) {
- // Only one output is set for |captureSession_|.
- DCHECK_EQ([[captureSession_ outputs] count], 1u);
- id output = [[captureSession_ outputs] objectAtIndex:0];
- [output setDelegate:nil];
-
- // TODO(shess): QTKit achieves thread safety by posting messages to the
- // main thread. As part of -addOutput:, it posts a message to the main
- // thread which in turn posts a notification which will run in a future
- // spin after the original method returns. -removeOutput: can post a
- // main-thread message in between while holding a lock which the
- // notification handler will need. Posting either -addOutput: or
- // -removeOutput: to the main thread should fix it, remove is likely
- // safer. http://crbug.com/152757
- [captureSession_ performSelectorOnMainThread:@selector(removeOutput:)
- withObject:output
- waitUntilDone:YES];
- }
+ // Tear down input and output, stop the capture and deregister observers.
+ [self stopCapture];
[captureSession_ release];
captureSession_ = nil;
[captureDeviceInput_ release];
@@ -240,12 +222,30 @@
}
- (void)stopCapture {
- if ([[captureSession_ inputs] count] == 1) {
+ // QTKit achieves thread safety and asynchronous execution by posting messages
+ // to the main thread, e.g. -addOutput:. Both -removeOutput: and -removeInput:
+ // post a message to the main thread while holding a lock that the
+ // notification handler might need. To avoid a deadlock, we perform those
+ // tasks in the main thread. See bugs http://crbug.com/152757 and
+ // http://crbug.com/399792.
+ [self performSelectorOnMainThread:@selector(stopCaptureOnUIThread:)
+ withObject:nil
+ waitUntilDone:YES];
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+- (void)stopCaptureOnUIThread:(id)dummy {
+ if ([[captureSession_ inputs] count] > 0) {
[captureSession_ removeInput:captureDeviceInput_];
+ DCHECK_EQ([[captureSession_ inputs] count], 1u);
[captureSession_ stopRunning];
}
-
- [[NSNotificationCenter defaultCenter] removeObserver:self];
+ if ([[captureSession_ outputs] count] > 0) {
+ DCHECK_EQ([[captureSession_ outputs] count], 1u);
+ id output = [[captureSession_ outputs] objectAtIndex:0];
+ [output setDelegate:nil];
+ [captureSession_ removeOutput:output];
+ }
}
// |captureOutput| is called by the capture device to deliver a new frame.
@@ -345,4 +345,12 @@
[lock_ unlock];
}
+- (void)sendLogString:(NSString*)message {
+ DVLOG(1) << [message UTF8String];
+ [lock_ lock];
+ if (frameReceiver_)
+ frameReceiver_->LogMessage([message UTF8String]);
+ [lock_ unlock];
+}
+
@end
diff --git a/media/video/capture/win/video_capture_device_win.cc b/media/video/capture/win/video_capture_device_win.cc
index 29ccf5afcd..a7d1f104cd 100644
--- a/media/video/capture/win/video_capture_device_win.cc
+++ b/media/video/capture/win/video_capture_device_win.cc
@@ -312,9 +312,14 @@ void VideoCaptureDeviceWin::AllocateAndStart(
ScopedMediaType media_type;
// Get the windows capability from the capture device.
+ // GetStreamCaps can return S_FALSE which we consider an error. Therefore the
+ // FAILED macro can't be used.
hr = stream_config->GetStreamCaps(
found_capability.stream_index, media_type.Receive(), caps.get());
- if (SUCCEEDED(hr)) {
+ if (hr != S_OK) {
+ SetErrorState("Failed to get capture device capabilities");
+ return;
+ } else {
if (media_type->formattype == FORMAT_VideoInfo) {
VIDEOINFOHEADER* h =
reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat);
@@ -325,11 +330,13 @@ void VideoCaptureDeviceWin::AllocateAndStart(
sink_filter_->SetRequestedMediaFormat(format);
// Order the capture device to use this format.
hr = stream_config->SetFormat(media_type.get());
+ if (FAILED(hr)) {
+ // TODO(grunell): Log the error. http://crbug.com/405016.
+ SetErrorState("Failed to set capture device output format");
+ return;
+ }
}
- if (FAILED(hr))
- SetErrorState("Failed to set capture device output format");
-
if (format.pixel_format == PIXEL_FORMAT_MJPEG && !mjpg_filter_.get()) {
// Create MJPG filter if we need it.
hr = mjpg_filter_.CreateInstance(CLSID_MjpegDec, NULL, CLSCTX_INPROC);
diff --git a/media/video/picture.cc b/media/video/picture.cc
index 7b32563824..f0510131ea 100644
--- a/media/video/picture.cc
+++ b/media/video/picture.cc
@@ -22,9 +22,12 @@ PictureBuffer::PictureBuffer(int32 id,
texture_mailbox_(texture_mailbox) {
}
-Picture::Picture(int32 picture_buffer_id, int32 bitstream_buffer_id)
+Picture::Picture(int32 picture_buffer_id,
+ int32 bitstream_buffer_id,
+ const gfx::Rect& visible_rect)
: picture_buffer_id_(picture_buffer_id),
- bitstream_buffer_id_(bitstream_buffer_id) {
+ bitstream_buffer_id_(bitstream_buffer_id),
+ visible_rect_(visible_rect) {
}
} // namespace media
diff --git a/media/video/picture.h b/media/video/picture.h
index d5be2276f2..844e629ef3 100644
--- a/media/video/picture.h
+++ b/media/video/picture.h
@@ -8,6 +8,7 @@
#include "base/basictypes.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "media/base/media_export.h"
+#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/size.h"
namespace media {
@@ -54,7 +55,9 @@ class MEDIA_EXPORT PictureBuffer {
// This is the media-namespace equivalent of PP_Picture_Dev.
class MEDIA_EXPORT Picture {
public:
- Picture(int32 picture_buffer_id, int32 bitstream_buffer_id);
+ Picture(int32 picture_buffer_id,
+ int32 bitstream_buffer_id,
+ const gfx::Rect& visible_rect);
// Returns the id of the picture buffer where this picture is contained.
int32 picture_buffer_id() const {
@@ -70,9 +73,15 @@ class MEDIA_EXPORT Picture {
bitstream_buffer_id_ = bitstream_buffer_id;
}
+ // Returns the visible rectangle of the picture. Its size may be smaller
+ // than the size of the PictureBuffer, as it is the only visible part of the
+ // Picture contained in the PictureBuffer.
+ gfx::Rect visible_rect() const { return visible_rect_; }
+
private:
int32 picture_buffer_id_;
int32 bitstream_buffer_id_;
+ gfx::Rect visible_rect_;
};
} // namespace media