OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/cast/sender/audio_encoder.h" | 5 #include "media/cast/sender/audio_encoder.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/bind_helpers.h" | 10 #include "base/bind_helpers.h" |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
49 int sampling_rate, | 49 int sampling_rate, |
50 const FrameEncodedCallback& callback) | 50 const FrameEncodedCallback& callback) |
51 : cast_environment_(cast_environment), | 51 : cast_environment_(cast_environment), |
52 codec_(codec), | 52 codec_(codec), |
53 num_channels_(num_channels), | 53 num_channels_(num_channels), |
54 samples_per_frame_(sampling_rate / kFramesPerSecond), | 54 samples_per_frame_(sampling_rate / kFramesPerSecond), |
55 callback_(callback), | 55 callback_(callback), |
56 cast_initialization_status_(STATUS_AUDIO_UNINITIALIZED), | 56 cast_initialization_status_(STATUS_AUDIO_UNINITIALIZED), |
57 buffer_fill_end_(0), | 57 buffer_fill_end_(0), |
58 frame_id_(0), | 58 frame_id_(0), |
59 frame_rtp_timestamp_(0) { | 59 frame_rtp_timestamp_(0), |
| 60 samples_dropped_from_buffer_(0) { |
60 // Support for max sampling rate of 48KHz, 2 channels, 100 ms duration. | 61 // Support for max sampling rate of 48KHz, 2 channels, 100 ms duration. |
61 const int kMaxSamplesTimesChannelsPerFrame = 48 * 2 * 100; | 62 const int kMaxSamplesTimesChannelsPerFrame = 48 * 2 * 100; |
62 if (num_channels_ <= 0 || samples_per_frame_ <= 0 || | 63 if (num_channels_ <= 0 || samples_per_frame_ <= 0 || |
63 sampling_rate % kFramesPerSecond != 0 || | 64 sampling_rate % kFramesPerSecond != 0 || |
64 samples_per_frame_ * num_channels_ > kMaxSamplesTimesChannelsPerFrame) { | 65 samples_per_frame_ * num_channels_ > kMaxSamplesTimesChannelsPerFrame) { |
65 cast_initialization_status_ = STATUS_INVALID_AUDIO_CONFIGURATION; | 66 cast_initialization_status_ = STATUS_INVALID_AUDIO_CONFIGURATION; |
66 } | 67 } |
67 } | 68 } |
68 | 69 |
69 CastInitializationStatus InitializationResult() const { | 70 CastInitializationStatus InitializationResult() const { |
70 return cast_initialization_status_; | 71 return cast_initialization_status_; |
71 } | 72 } |
72 | 73 |
| 74 int samples_per_frame() const { |
| 75 return samples_per_frame_; |
| 76 } |
| 77 |
73 void EncodeAudio(scoped_ptr<AudioBus> audio_bus, | 78 void EncodeAudio(scoped_ptr<AudioBus> audio_bus, |
74 const base::TimeTicks& recorded_time) { | 79 const base::TimeTicks& recorded_time) { |
75 DCHECK_EQ(cast_initialization_status_, STATUS_AUDIO_INITIALIZED); | 80 DCHECK_EQ(cast_initialization_status_, STATUS_AUDIO_INITIALIZED); |
76 DCHECK(!recorded_time.is_null()); | 81 DCHECK(!recorded_time.is_null()); |
77 | 82 |
78 // Determine whether |recorded_time| is consistent with the amount of audio | 83 // Determine whether |recorded_time| is consistent with the amount of audio |
79 // data having been processed in the past. Resolve the underrun problem by | 84 // data having been processed in the past. Resolve the underrun problem by |
80 // dropping data from the internal buffer and skipping ahead the next | 85 // dropping data from the internal buffer and skipping ahead the next |
81 // frame's RTP timestamp by the estimated number of frames missed. On the | 86 // frame's RTP timestamp by the estimated number of frames missed. On the |
82 // other hand, don't attempt to resolve overruns: A receiver should | 87 // other hand, don't attempt to resolve overruns: A receiver should |
83 // gracefully deal with an excess of audio data. | 88 // gracefully deal with an excess of audio data. |
84 const base::TimeDelta frame_duration = | 89 const base::TimeDelta frame_duration = |
85 base::TimeDelta::FromMilliseconds(kFrameDurationMillis); | 90 base::TimeDelta::FromMilliseconds(kFrameDurationMillis); |
86 base::TimeDelta buffer_fill_duration = | 91 base::TimeDelta buffer_fill_duration = |
87 buffer_fill_end_ * frame_duration / samples_per_frame_; | 92 buffer_fill_end_ * frame_duration / samples_per_frame_; |
88 if (!frame_capture_time_.is_null()) { | 93 if (!frame_capture_time_.is_null()) { |
89 const base::TimeDelta amount_ahead_by = | 94 const base::TimeDelta amount_ahead_by = |
90 recorded_time - (frame_capture_time_ + buffer_fill_duration); | 95 recorded_time - (frame_capture_time_ + buffer_fill_duration); |
91 if (amount_ahead_by > | 96 if (amount_ahead_by > |
92 base::TimeDelta::FromMilliseconds(kUnderrunThresholdMillis)) { | 97 base::TimeDelta::FromMilliseconds(kUnderrunThresholdMillis)) { |
| 98 samples_dropped_from_buffer_ += buffer_fill_end_; |
93 buffer_fill_end_ = 0; | 99 buffer_fill_end_ = 0; |
94 buffer_fill_duration = base::TimeDelta(); | 100 buffer_fill_duration = base::TimeDelta(); |
95 const int64 num_frames_missed = amount_ahead_by / | 101 const int64 num_frames_missed = amount_ahead_by / |
96 base::TimeDelta::FromMilliseconds(kFrameDurationMillis); | 102 base::TimeDelta::FromMilliseconds(kFrameDurationMillis); |
97 frame_rtp_timestamp_ += | 103 frame_rtp_timestamp_ += |
98 static_cast<uint32>(num_frames_missed * samples_per_frame_); | 104 static_cast<uint32>(num_frames_missed * samples_per_frame_); |
99 DVLOG(1) << "Skipping RTP timestamp ahead to account for " | 105 DVLOG(1) << "Skipping RTP timestamp ahead to account for " |
100 << num_frames_missed * samples_per_frame_ | 106 << num_frames_missed * samples_per_frame_ |
101 << " samples' worth of underrun."; | 107 << " samples' worth of underrun."; |
102 } | 108 } |
(...skipping 19 matching lines...) Expand all Loading... |
122 audio_frame->dependency = EncodedFrame::KEY; | 128 audio_frame->dependency = EncodedFrame::KEY; |
123 audio_frame->frame_id = frame_id_; | 129 audio_frame->frame_id = frame_id_; |
124 audio_frame->referenced_frame_id = frame_id_; | 130 audio_frame->referenced_frame_id = frame_id_; |
125 audio_frame->rtp_timestamp = frame_rtp_timestamp_; | 131 audio_frame->rtp_timestamp = frame_rtp_timestamp_; |
126 audio_frame->reference_time = frame_capture_time_; | 132 audio_frame->reference_time = frame_capture_time_; |
127 | 133 |
128 if (EncodeFromFilledBuffer(&audio_frame->data)) { | 134 if (EncodeFromFilledBuffer(&audio_frame->data)) { |
129 cast_environment_->PostTask( | 135 cast_environment_->PostTask( |
130 CastEnvironment::MAIN, | 136 CastEnvironment::MAIN, |
131 FROM_HERE, | 137 FROM_HERE, |
132 base::Bind(callback_, base::Passed(&audio_frame))); | 138 base::Bind(callback_, |
| 139 base::Passed(&audio_frame), |
| 140 samples_dropped_from_buffer_)); |
| 141 samples_dropped_from_buffer_ = 0; |
133 } | 142 } |
134 | 143 |
135 // Reset the internal buffer, frame ID, and timestamps for the next frame. | 144 // Reset the internal buffer, frame ID, and timestamps for the next frame. |
136 buffer_fill_end_ = 0; | 145 buffer_fill_end_ = 0; |
137 ++frame_id_; | 146 ++frame_id_; |
138 frame_rtp_timestamp_ += samples_per_frame_; | 147 frame_rtp_timestamp_ += samples_per_frame_; |
139 frame_capture_time_ += frame_duration; | 148 frame_capture_time_ += frame_duration; |
140 } | 149 } |
141 } | 150 } |
142 | 151 |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
175 // timestamp values are allowed to overflow and roll around past zero. | 184 // timestamp values are allowed to overflow and roll around past zero. |
176 uint32 frame_rtp_timestamp_; | 185 uint32 frame_rtp_timestamp_; |
177 | 186 |
178 // The local system time associated with the start of the next frame of | 187 // The local system time associated with the start of the next frame of |
179 // encoded audio. This value is passed on to a receiver as a reference clock | 188 // encoded audio. This value is passed on to a receiver as a reference clock |
180 // timestamp for the purposes of synchronizing audio and video. Its | 189 // timestamp for the purposes of synchronizing audio and video. Its |
181 // progression is expected to drift relative to the elapsed time implied by | 190 // progression is expected to drift relative to the elapsed time implied by |
182 // the RTP timestamps. | 191 // the RTP timestamps. |
183 base::TimeTicks frame_capture_time_; | 192 base::TimeTicks frame_capture_time_; |
184 | 193 |
| 194 // Set to non-zero to indicate the next output frame skipped over audio |
| 195 // samples in order to recover from an input underrun. |
| 196 int samples_dropped_from_buffer_; |
| 197 |
185 DISALLOW_COPY_AND_ASSIGN(ImplBase); | 198 DISALLOW_COPY_AND_ASSIGN(ImplBase); |
186 }; | 199 }; |
187 | 200 |
188 class AudioEncoder::OpusImpl : public AudioEncoder::ImplBase { | 201 class AudioEncoder::OpusImpl : public AudioEncoder::ImplBase { |
189 public: | 202 public: |
190 OpusImpl(const scoped_refptr<CastEnvironment>& cast_environment, | 203 OpusImpl(const scoped_refptr<CastEnvironment>& cast_environment, |
191 int num_channels, | 204 int num_channels, |
192 int sampling_rate, | 205 int sampling_rate, |
193 int bitrate, | 206 int bitrate, |
194 const FrameEncodedCallback& callback) | 207 const FrameEncodedCallback& callback) |
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
358 AudioEncoder::~AudioEncoder() {} | 371 AudioEncoder::~AudioEncoder() {} |
359 | 372 |
360 CastInitializationStatus AudioEncoder::InitializationResult() const { | 373 CastInitializationStatus AudioEncoder::InitializationResult() const { |
361 DCHECK(insert_thread_checker_.CalledOnValidThread()); | 374 DCHECK(insert_thread_checker_.CalledOnValidThread()); |
362 if (impl_.get()) { | 375 if (impl_.get()) { |
363 return impl_->InitializationResult(); | 376 return impl_->InitializationResult(); |
364 } | 377 } |
365 return STATUS_UNSUPPORTED_AUDIO_CODEC; | 378 return STATUS_UNSUPPORTED_AUDIO_CODEC; |
366 } | 379 } |
367 | 380 |
| 381 int AudioEncoder::GetSamplesPerFrame() const { |
| 382 DCHECK(insert_thread_checker_.CalledOnValidThread()); |
| 383 if (InitializationResult() != STATUS_AUDIO_INITIALIZED) { |
| 384 NOTREACHED(); |
| 385 return std::numeric_limits<int>::max(); |
| 386 } |
| 387 return impl_->samples_per_frame(); |
| 388 } |
| 389 |
368 void AudioEncoder::InsertAudio(scoped_ptr<AudioBus> audio_bus, | 390 void AudioEncoder::InsertAudio(scoped_ptr<AudioBus> audio_bus, |
369 const base::TimeTicks& recorded_time) { | 391 const base::TimeTicks& recorded_time) { |
370 DCHECK(insert_thread_checker_.CalledOnValidThread()); | 392 DCHECK(insert_thread_checker_.CalledOnValidThread()); |
371 DCHECK(audio_bus.get()); | 393 DCHECK(audio_bus.get()); |
372 if (!impl_.get()) { | 394 if (InitializationResult() != STATUS_AUDIO_INITIALIZED) { |
373 NOTREACHED(); | 395 NOTREACHED(); |
374 return; | 396 return; |
375 } | 397 } |
376 cast_environment_->PostTask(CastEnvironment::AUDIO, | 398 cast_environment_->PostTask(CastEnvironment::AUDIO, |
377 FROM_HERE, | 399 FROM_HERE, |
378 base::Bind(&AudioEncoder::ImplBase::EncodeAudio, | 400 base::Bind(&AudioEncoder::ImplBase::EncodeAudio, |
379 impl_, | 401 impl_, |
380 base::Passed(&audio_bus), | 402 base::Passed(&audio_bus), |
381 recorded_time)); | 403 recorded_time)); |
382 } | 404 } |
383 | 405 |
384 } // namespace cast | 406 } // namespace cast |
385 } // namespace media | 407 } // namespace media |
OLD | NEW |