1 // Copyright 2014 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.
5 #include "media/cast/sender/audio_sender.h"
8 #include "base/logging.h"
9 #include "base/message_loop/message_loop.h"
10 #include "media/cast/cast_defines.h"
11 #include "media/cast/net/cast_transport_config.h"
12 #include "media/cast/sender/audio_encoder.h"
18 const int kNumAggressiveReportsSentAtStart
= 100;
20 // TODO(miu): This should be specified in AudioSenderConfig, but currently it is
21 // fixed to 100 FPS (i.e., 10 ms per frame), and AudioEncoder assumes this as
23 const int kAudioFrameRate
= 100;
27 AudioSender::AudioSender(scoped_refptr
<CastEnvironment
> cast_environment
,
28 const AudioSenderConfig
& audio_config
,
29 CastTransportSender
* const transport_sender
)
33 base::TimeDelta::FromMilliseconds(audio_config
.rtcp_interval
),
34 audio_config
.frequency
,
36 kAudioFrameRate
* 2.0, // We lie to increase max outstanding frames.
37 audio_config
.target_playout_delay
),
38 configured_encoder_bitrate_(audio_config
.bitrate
),
40 cast_initialization_status_
= STATUS_AUDIO_UNINITIALIZED
;
41 VLOG(1) << "max_unacked_frames " << max_unacked_frames_
;
42 DCHECK_GT(max_unacked_frames_
, 0);
44 if (!audio_config
.use_external_encoder
) {
46 new AudioEncoder(cast_environment
,
47 audio_config
.channels
,
48 audio_config
.frequency
,
51 base::Bind(&AudioSender::SendEncodedAudioFrame
,
52 weak_factory_
.GetWeakPtr())));
53 cast_initialization_status_
= audio_encoder_
->InitializationResult();
55 NOTREACHED(); // No support for external audio encoding.
56 cast_initialization_status_
= STATUS_AUDIO_UNINITIALIZED
;
59 media::cast::CastTransportRtpConfig transport_config
;
60 transport_config
.ssrc
= audio_config
.ssrc
;
61 transport_config
.feedback_ssrc
= audio_config
.incoming_feedback_ssrc
;
62 transport_config
.rtp_payload_type
= audio_config
.rtp_payload_type
;
63 // TODO(miu): AudioSender needs to be like VideoSender in providing an upper
64 // limit on the number of in-flight frames.
65 transport_config
.stored_frames
= max_unacked_frames_
;
66 transport_config
.aes_key
= audio_config
.aes_key
;
67 transport_config
.aes_iv_mask
= audio_config
.aes_iv_mask
;
69 transport_sender
->InitializeAudio(
71 base::Bind(&AudioSender::OnReceivedCastFeedback
,
72 weak_factory_
.GetWeakPtr()),
73 base::Bind(&AudioSender::OnReceivedRtt
, weak_factory_
.GetWeakPtr()));
76 AudioSender::~AudioSender() {}
78 void AudioSender::InsertAudio(scoped_ptr
<AudioBus
> audio_bus
,
79 const base::TimeTicks
& recorded_time
) {
80 DCHECK(cast_environment_
->CurrentlyOn(CastEnvironment::MAIN
));
81 if (cast_initialization_status_
!= STATUS_AUDIO_INITIALIZED
) {
85 DCHECK(audio_encoder_
.get()) << "Invalid internal state";
87 if (ShouldDropNextFrame(recorded_time
)) {
88 VLOG(1) << "Dropping frame due to too many frames currently in-flight.";
92 audio_encoder_
->InsertAudio(audio_bus
.Pass(), recorded_time
);
95 void AudioSender::SendEncodedAudioFrame(
96 scoped_ptr
<EncodedFrame
> encoded_frame
) {
97 DCHECK(cast_environment_
->CurrentlyOn(CastEnvironment::MAIN
));
99 const uint32 frame_id
= encoded_frame
->frame_id
;
101 const bool is_first_frame_to_be_sent
= last_send_time_
.is_null();
102 last_send_time_
= cast_environment_
->Clock()->NowTicks();
103 last_sent_frame_id_
= frame_id
;
104 // If this is the first frame about to be sent, fake the value of
105 // |latest_acked_frame_id_| to indicate the receiver starts out all caught up.
106 // Also, schedule the periodic frame re-send checks.
107 if (is_first_frame_to_be_sent
) {
108 latest_acked_frame_id_
= frame_id
- 1;
109 ScheduleNextResendCheck();
112 cast_environment_
->Logging()->InsertEncodedFrameEvent(
113 last_send_time_
, FRAME_ENCODED
, AUDIO_EVENT
, encoded_frame
->rtp_timestamp
,
114 frame_id
, static_cast<int>(encoded_frame
->data
.size()),
115 encoded_frame
->dependency
== EncodedFrame::KEY
,
116 configured_encoder_bitrate_
);
118 RecordLatestFrameTimestamps(frame_id
,
119 encoded_frame
->reference_time
,
120 encoded_frame
->rtp_timestamp
);
122 // At the start of the session, it's important to send reports before each
123 // frame so that the receiver can properly compute playout times. The reason
124 // more than one report is sent is because transmission is not guaranteed,
125 // only best effort, so we send enough that one should almost certainly get
127 if (num_aggressive_rtcp_reports_sent_
< kNumAggressiveReportsSentAtStart
) {
128 // SendRtcpReport() will schedule future reports to be made if this is the
129 // last "aggressive report."
130 ++num_aggressive_rtcp_reports_sent_
;
131 const bool is_last_aggressive_report
=
132 (num_aggressive_rtcp_reports_sent_
== kNumAggressiveReportsSentAtStart
);
133 VLOG_IF(1, is_last_aggressive_report
) << "Sending last aggressive report.";
134 SendRtcpReport(is_last_aggressive_report
);
137 if (send_target_playout_delay_
) {
138 encoded_frame
->new_playout_delay_ms
=
139 target_playout_delay_
.InMilliseconds();
141 transport_sender_
->InsertCodedAudioFrame(*encoded_frame
);
144 void AudioSender::OnReceivedCastFeedback(const RtcpCastMessage
& cast_feedback
) {
145 DCHECK(cast_environment_
->CurrentlyOn(CastEnvironment::MAIN
));
147 if (is_rtt_available()) {
148 // Having the RTT values implies the receiver sent back a receiver report
149 // based on it having received a report from here. Therefore, ensure this
150 // sender stops aggressively sending reports.
151 if (num_aggressive_rtcp_reports_sent_
< kNumAggressiveReportsSentAtStart
) {
152 VLOG(1) << "No longer a need to send reports aggressively (sent "
153 << num_aggressive_rtcp_reports_sent_
<< ").";
154 num_aggressive_rtcp_reports_sent_
= kNumAggressiveReportsSentAtStart
;
155 ScheduleNextRtcpReport();
159 if (last_send_time_
.is_null())
160 return; // Cannot get an ACK without having first sent a frame.
162 if (cast_feedback
.missing_frames_and_packets
.empty()) {
163 // We only count duplicate ACKs when we have sent newer frames.
164 if (latest_acked_frame_id_
== cast_feedback
.ack_frame_id
&&
165 latest_acked_frame_id_
!= last_sent_frame_id_
) {
166 duplicate_ack_counter_
++;
168 duplicate_ack_counter_
= 0;
170 // TODO(miu): The values "2" and "3" should be derived from configuration.
171 if (duplicate_ack_counter_
>= 2 && duplicate_ack_counter_
% 3 == 2) {
172 VLOG(1) << "Received duplicate ACK for frame " << latest_acked_frame_id_
;
173 ResendForKickstart();
176 // Only count duplicated ACKs if there is no NACK request in between.
177 // This is to avoid aggresive resend.
178 duplicate_ack_counter_
= 0;
181 cast_environment_
->Logging()->InsertFrameEvent(
182 cast_environment_
->Clock()->NowTicks(),
185 GetRecordedRtpTimestamp(cast_feedback
.ack_frame_id
),
186 cast_feedback
.ack_frame_id
);
188 const bool is_acked_out_of_order
=
189 static_cast<int32
>(cast_feedback
.ack_frame_id
-
190 latest_acked_frame_id_
) < 0;
191 VLOG(2) << "Received ACK" << (is_acked_out_of_order
? " out-of-order" : "")
192 << " for frame " << cast_feedback
.ack_frame_id
;
193 if (!is_acked_out_of_order
) {
194 // Cancel resends of acked frames.
195 std::vector
<uint32
> cancel_sending_frames
;
196 while (latest_acked_frame_id_
!= cast_feedback
.ack_frame_id
) {
197 latest_acked_frame_id_
++;
198 cancel_sending_frames
.push_back(latest_acked_frame_id_
);
200 transport_sender_
->CancelSendingFrames(ssrc_
, cancel_sending_frames
);
201 latest_acked_frame_id_
= cast_feedback
.ack_frame_id
;
205 bool AudioSender::ShouldDropNextFrame(base::TimeTicks capture_time
) const {
206 DCHECK(cast_environment_
->CurrentlyOn(CastEnvironment::MAIN
));
207 int frames_in_flight
= 0;
208 base::TimeDelta duration_in_flight
;
209 if (!last_send_time_
.is_null()) {
211 static_cast<int32
>(last_sent_frame_id_
- latest_acked_frame_id_
);
212 if (frames_in_flight
> 0) {
213 const uint32 oldest_unacked_frame_id
= latest_acked_frame_id_
+ 1;
215 capture_time
- GetRecordedReferenceTime(oldest_unacked_frame_id
);
218 VLOG(2) << frames_in_flight
219 << " frames in flight; last sent: " << last_sent_frame_id_
220 << "; latest acked: " << latest_acked_frame_id_
221 << "; duration in flight: "
222 << duration_in_flight
.InMicroseconds() << " usec ("
223 << (target_playout_delay_
> base::TimeDelta() ?
224 100 * duration_in_flight
/ target_playout_delay_
:
226 return frames_in_flight
>= max_unacked_frames_
||
227 duration_in_flight
>= target_playout_delay_
;