Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(852)

Side by Side Diff: media/cast/audio_sender/audio_sender.cc

Issue 388663003: Cast: Reshuffle files under media/cast (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: missing includes Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "media/cast/audio_sender/audio_sender.h"
6
7 #include "base/bind.h"
8 #include "base/logging.h"
9 #include "base/message_loop/message_loop.h"
10 #include "media/cast/audio_sender/audio_encoder.h"
11 #include "media/cast/cast_defines.h"
12 #include "media/cast/rtcp/rtcp_defines.h"
13 #include "media/cast/transport/cast_transport_config.h"
14
15 namespace media {
16 namespace cast {
17 namespace {
18
19 const int kNumAggressiveReportsSentAtStart = 100;
20 const int kMinSchedulingDelayMs = 1;
21
22 // TODO(miu): This should be specified in AudioSenderConfig, but currently it is
23 // fixed to 100 FPS (i.e., 10 ms per frame), and AudioEncoder assumes this as
24 // well.
25 const int kAudioFrameRate = 100;
26
27 // Helper function to compute the maximum unacked audio frames that is sent.
28 int GetMaxUnackedFrames(base::TimeDelta target_delay) {
29 // As long as it doesn't go over |kMaxUnackedFrames|, it is okay to send more
30 // audio data than the target delay would suggest. Audio packets are tiny and
31 // receiver has the ability to drop any one of the packets.
32 // We send up to three times of the target delay of audio frames.
33 int frames =
34 1 + 3 * target_delay * kAudioFrameRate / base::TimeDelta::FromSeconds(1);
35 return std::min(kMaxUnackedFrames, frames);
36 }
37 } // namespace
38
39 AudioSender::AudioSender(scoped_refptr<CastEnvironment> cast_environment,
40 const AudioSenderConfig& audio_config,
41 transport::CastTransportSender* const transport_sender)
42 : cast_environment_(cast_environment),
43 target_playout_delay_(audio_config.target_playout_delay),
44 transport_sender_(transport_sender),
45 max_unacked_frames_(GetMaxUnackedFrames(target_playout_delay_)),
46 configured_encoder_bitrate_(audio_config.bitrate),
47 rtcp_(cast_environment,
48 this,
49 transport_sender_,
50 NULL, // paced sender.
51 NULL,
52 audio_config.rtcp_mode,
53 base::TimeDelta::FromMilliseconds(audio_config.rtcp_interval),
54 audio_config.ssrc,
55 audio_config.incoming_feedback_ssrc,
56 audio_config.rtcp_c_name,
57 AUDIO_EVENT),
58 rtp_timestamp_helper_(audio_config.frequency),
59 num_aggressive_rtcp_reports_sent_(0),
60 last_sent_frame_id_(0),
61 latest_acked_frame_id_(0),
62 duplicate_ack_counter_(0),
63 cast_initialization_status_(STATUS_AUDIO_UNINITIALIZED),
64 weak_factory_(this) {
65 VLOG(1) << "max_unacked_frames " << max_unacked_frames_;
66 DCHECK_GT(max_unacked_frames_, 0);
67
68 if (!audio_config.use_external_encoder) {
69 audio_encoder_.reset(
70 new AudioEncoder(cast_environment,
71 audio_config.channels,
72 audio_config.frequency,
73 audio_config.bitrate,
74 audio_config.codec,
75 base::Bind(&AudioSender::SendEncodedAudioFrame,
76 weak_factory_.GetWeakPtr())));
77 cast_initialization_status_ = audio_encoder_->InitializationResult();
78 } else {
79 NOTREACHED(); // No support for external audio encoding.
80 cast_initialization_status_ = STATUS_AUDIO_UNINITIALIZED;
81 }
82
83 media::cast::transport::CastTransportRtpConfig transport_config;
84 transport_config.ssrc = audio_config.ssrc;
85 transport_config.rtp_payload_type = audio_config.rtp_payload_type;
86 // TODO(miu): AudioSender needs to be like VideoSender in providing an upper
87 // limit on the number of in-flight frames.
88 transport_config.stored_frames = max_unacked_frames_;
89 transport_config.aes_key = audio_config.aes_key;
90 transport_config.aes_iv_mask = audio_config.aes_iv_mask;
91 transport_sender_->InitializeAudio(transport_config);
92
93 rtcp_.SetCastReceiverEventHistorySize(kReceiverRtcpEventHistorySize);
94
95 memset(frame_id_to_rtp_timestamp_, 0, sizeof(frame_id_to_rtp_timestamp_));
96 }
97
98 AudioSender::~AudioSender() {}
99
100 void AudioSender::InsertAudio(scoped_ptr<AudioBus> audio_bus,
101 const base::TimeTicks& recorded_time) {
102 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
103 if (cast_initialization_status_ != STATUS_AUDIO_INITIALIZED) {
104 NOTREACHED();
105 return;
106 }
107 DCHECK(audio_encoder_.get()) << "Invalid internal state";
108
109 if (AreTooManyFramesInFlight()) {
110 VLOG(1) << "Dropping frame due to too many frames currently in-flight.";
111 return;
112 }
113
114 audio_encoder_->InsertAudio(audio_bus.Pass(), recorded_time);
115 }
116
117 void AudioSender::SendEncodedAudioFrame(
118 scoped_ptr<transport::EncodedFrame> encoded_frame) {
119 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
120
121 const uint32 frame_id = encoded_frame->frame_id;
122
123 const bool is_first_frame_to_be_sent = last_send_time_.is_null();
124 last_send_time_ = cast_environment_->Clock()->NowTicks();
125 last_sent_frame_id_ = frame_id;
126 // If this is the first frame about to be sent, fake the value of
127 // |latest_acked_frame_id_| to indicate the receiver starts out all caught up.
128 // Also, schedule the periodic frame re-send checks.
129 if (is_first_frame_to_be_sent) {
130 latest_acked_frame_id_ = frame_id - 1;
131 ScheduleNextResendCheck();
132 }
133
134 cast_environment_->Logging()->InsertEncodedFrameEvent(
135 last_send_time_, FRAME_ENCODED, AUDIO_EVENT, encoded_frame->rtp_timestamp,
136 frame_id, static_cast<int>(encoded_frame->data.size()),
137 encoded_frame->dependency == transport::EncodedFrame::KEY,
138 configured_encoder_bitrate_);
139 // Only use lowest 8 bits as key.
140 frame_id_to_rtp_timestamp_[frame_id & 0xff] = encoded_frame->rtp_timestamp;
141
142 DCHECK(!encoded_frame->reference_time.is_null());
143 rtp_timestamp_helper_.StoreLatestTime(encoded_frame->reference_time,
144 encoded_frame->rtp_timestamp);
145
146 // At the start of the session, it's important to send reports before each
147 // frame so that the receiver can properly compute playout times. The reason
148 // more than one report is sent is because transmission is not guaranteed,
149 // only best effort, so we send enough that one should almost certainly get
150 // through.
151 if (num_aggressive_rtcp_reports_sent_ < kNumAggressiveReportsSentAtStart) {
152 // SendRtcpReport() will schedule future reports to be made if this is the
153 // last "aggressive report."
154 ++num_aggressive_rtcp_reports_sent_;
155 const bool is_last_aggressive_report =
156 (num_aggressive_rtcp_reports_sent_ == kNumAggressiveReportsSentAtStart);
157 VLOG_IF(1, is_last_aggressive_report) << "Sending last aggressive report.";
158 SendRtcpReport(is_last_aggressive_report);
159 }
160
161 transport_sender_->InsertCodedAudioFrame(*encoded_frame);
162 }
163
164 void AudioSender::IncomingRtcpPacket(scoped_ptr<Packet> packet) {
165 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
166 rtcp_.IncomingRtcpPacket(&packet->front(), packet->size());
167 }
168
169 void AudioSender::ScheduleNextRtcpReport() {
170 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
171 base::TimeDelta time_to_next =
172 rtcp_.TimeToSendNextRtcpReport() - cast_environment_->Clock()->NowTicks();
173
174 time_to_next = std::max(
175 time_to_next, base::TimeDelta::FromMilliseconds(kMinSchedulingDelayMs));
176
177 cast_environment_->PostDelayedTask(
178 CastEnvironment::MAIN,
179 FROM_HERE,
180 base::Bind(&AudioSender::SendRtcpReport,
181 weak_factory_.GetWeakPtr(),
182 true),
183 time_to_next);
184 }
185
186 void AudioSender::SendRtcpReport(bool schedule_future_reports) {
187 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
188 const base::TimeTicks now = cast_environment_->Clock()->NowTicks();
189 uint32 now_as_rtp_timestamp = 0;
190 if (rtp_timestamp_helper_.GetCurrentTimeAsRtpTimestamp(
191 now, &now_as_rtp_timestamp)) {
192 rtcp_.SendRtcpFromRtpSender(now, now_as_rtp_timestamp);
193 } else {
194 // |rtp_timestamp_helper_| should have stored a mapping by this point.
195 NOTREACHED();
196 }
197 if (schedule_future_reports)
198 ScheduleNextRtcpReport();
199 }
200
201 void AudioSender::ScheduleNextResendCheck() {
202 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
203 DCHECK(!last_send_time_.is_null());
204 base::TimeDelta time_to_next =
205 last_send_time_ - cast_environment_->Clock()->NowTicks() +
206 target_playout_delay_;
207 time_to_next = std::max(
208 time_to_next, base::TimeDelta::FromMilliseconds(kMinSchedulingDelayMs));
209 cast_environment_->PostDelayedTask(
210 CastEnvironment::MAIN,
211 FROM_HERE,
212 base::Bind(&AudioSender::ResendCheck, weak_factory_.GetWeakPtr()),
213 time_to_next);
214 }
215
216 void AudioSender::ResendCheck() {
217 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
218 DCHECK(!last_send_time_.is_null());
219 const base::TimeDelta time_since_last_send =
220 cast_environment_->Clock()->NowTicks() - last_send_time_;
221 if (time_since_last_send > target_playout_delay_) {
222 if (latest_acked_frame_id_ == last_sent_frame_id_) {
223 // Last frame acked, no point in doing anything
224 } else {
225 VLOG(1) << "ACK timeout; last acked frame: " << latest_acked_frame_id_;
226 ResendForKickstart();
227 }
228 }
229 ScheduleNextResendCheck();
230 }
231
232 void AudioSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) {
233 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
234
235 if (rtcp_.is_rtt_available()) {
236 // Having the RTT values implies the receiver sent back a receiver report
237 // based on it having received a report from here. Therefore, ensure this
238 // sender stops aggressively sending reports.
239 if (num_aggressive_rtcp_reports_sent_ < kNumAggressiveReportsSentAtStart) {
240 VLOG(1) << "No longer a need to send reports aggressively (sent "
241 << num_aggressive_rtcp_reports_sent_ << ").";
242 num_aggressive_rtcp_reports_sent_ = kNumAggressiveReportsSentAtStart;
243 ScheduleNextRtcpReport();
244 }
245 }
246
247 if (last_send_time_.is_null())
248 return; // Cannot get an ACK without having first sent a frame.
249
250 if (cast_feedback.missing_frames_and_packets_.empty()) {
251 // We only count duplicate ACKs when we have sent newer frames.
252 if (latest_acked_frame_id_ == cast_feedback.ack_frame_id_ &&
253 latest_acked_frame_id_ != last_sent_frame_id_) {
254 duplicate_ack_counter_++;
255 } else {
256 duplicate_ack_counter_ = 0;
257 }
258 // TODO(miu): The values "2" and "3" should be derived from configuration.
259 if (duplicate_ack_counter_ >= 2 && duplicate_ack_counter_ % 3 == 2) {
260 VLOG(1) << "Received duplicate ACK for frame " << latest_acked_frame_id_;
261 ResendForKickstart();
262 }
263 } else {
264 // Only count duplicated ACKs if there is no NACK request in between.
265 // This is to avoid aggresive resend.
266 duplicate_ack_counter_ = 0;
267
268 base::TimeDelta rtt;
269 base::TimeDelta avg_rtt;
270 base::TimeDelta min_rtt;
271 base::TimeDelta max_rtt;
272 rtcp_.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt);
273
274 // A NACK is also used to cancel pending re-transmissions.
275 transport_sender_->ResendPackets(
276 true, cast_feedback.missing_frames_and_packets_, false, min_rtt);
277 }
278
279 const base::TimeTicks now = cast_environment_->Clock()->NowTicks();
280
281 const RtpTimestamp rtp_timestamp =
282 frame_id_to_rtp_timestamp_[cast_feedback.ack_frame_id_ & 0xff];
283 cast_environment_->Logging()->InsertFrameEvent(now,
284 FRAME_ACK_RECEIVED,
285 AUDIO_EVENT,
286 rtp_timestamp,
287 cast_feedback.ack_frame_id_);
288
289 const bool is_acked_out_of_order =
290 static_cast<int32>(cast_feedback.ack_frame_id_ -
291 latest_acked_frame_id_) < 0;
292 VLOG(2) << "Received ACK" << (is_acked_out_of_order ? " out-of-order" : "")
293 << " for frame " << cast_feedback.ack_frame_id_;
294 if (!is_acked_out_of_order) {
295 // Cancel resends of acked frames.
296 MissingFramesAndPacketsMap missing_frames_and_packets;
297 PacketIdSet missing;
298 while (latest_acked_frame_id_ != cast_feedback.ack_frame_id_) {
299 latest_acked_frame_id_++;
300 missing_frames_and_packets[latest_acked_frame_id_] = missing;
301 }
302 transport_sender_->ResendPackets(
303 true, missing_frames_and_packets, true, base::TimeDelta());
304 latest_acked_frame_id_ = cast_feedback.ack_frame_id_;
305 }
306 }
307
308 bool AudioSender::AreTooManyFramesInFlight() const {
309 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
310 int frames_in_flight = 0;
311 if (!last_send_time_.is_null()) {
312 frames_in_flight +=
313 static_cast<int32>(last_sent_frame_id_ - latest_acked_frame_id_);
314 }
315 VLOG(2) << frames_in_flight
316 << " frames in flight; last sent: " << last_sent_frame_id_
317 << " latest acked: " << latest_acked_frame_id_;
318 return frames_in_flight >= max_unacked_frames_;
319 }
320
321 void AudioSender::ResendForKickstart() {
322 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
323 DCHECK(!last_send_time_.is_null());
324 VLOG(1) << "Resending last packet of frame " << last_sent_frame_id_
325 << " to kick-start.";
326 // Send the first packet of the last encoded frame to kick start
327 // retransmission. This gives enough information to the receiver what
328 // packets and frames are missing.
329 MissingFramesAndPacketsMap missing_frames_and_packets;
330 PacketIdSet missing;
331 missing.insert(kRtcpCastLastPacket);
332 missing_frames_and_packets.insert(
333 std::make_pair(last_sent_frame_id_, missing));
334 last_send_time_ = cast_environment_->Clock()->NowTicks();
335
336 base::TimeDelta rtt;
337 base::TimeDelta avg_rtt;
338 base::TimeDelta min_rtt;
339 base::TimeDelta max_rtt;
340 rtcp_.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt);
341
342 // Sending this extra packet is to kick-start the session. There is
343 // no need to optimize re-transmission for this case.
344 transport_sender_->ResendPackets(
345 true, missing_frames_and_packets, false, min_rtt);
346 }
347
348 } // namespace cast
349 } // namespace media
OLDNEW
« no previous file with comments | « media/cast/audio_sender/audio_sender.h ('k') | media/cast/audio_sender/audio_sender_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698