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

Side by Side Diff: media/cast/sender/audio_encoder.cc

Issue 605803004: [cast] Allow audio encoder implementations to specify the frame length. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 2 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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"
11 #include "base/location.h" 11 #include "base/location.h"
12 #include "base/stl_util.h" 12 #include "base/stl_util.h"
13 #include "base/sys_byteorder.h" 13 #include "base/sys_byteorder.h"
14 #include "base/time/time.h" 14 #include "base/time/time.h"
15 #include "media/base/audio_bus.h" 15 #include "media/base/audio_bus.h"
16 #include "media/cast/cast_defines.h" 16 #include "media/cast/cast_defines.h"
17 #include "media/cast/cast_environment.h" 17 #include "media/cast/cast_environment.h"
18 #include "third_party/opus/src/include/opus.h" 18 #include "third_party/opus/src/include/opus.h"
19 19
20 namespace media { 20 namespace media {
21 namespace cast { 21 namespace cast {
22 22
23 namespace {
24
25 // The fixed number of audio frames per second and, inversely, the duration of
26 // one frame's worth of samples.
27 const int kFramesPerSecond = 100;
28 const int kFrameDurationMillis = 1000 / kFramesPerSecond; // No remainder!
29
30 // Threshold used to decide whether audio being delivered to the encoder is
31 // coming in too slow with respect to the capture timestamps.
32 const int kUnderrunThresholdMillis = 3 * kFrameDurationMillis;
33
34 } // namespace
35
36 23
37 // Base class that handles the common problem of feeding one or more AudioBus' 24 // Base class that handles the common problem of feeding one or more AudioBus'
38 // data into a buffer and then, once the buffer is full, encoding the signal and 25 // data into a buffer and then, once the buffer is full, encoding the signal and
39 // emitting an EncodedFrame via the FrameEncodedCallback. 26 // emitting an EncodedFrame via the FrameEncodedCallback.
40 // 27 //
41 // Subclasses complete the implementation by handling the actual encoding 28 // Subclasses complete the implementation by handling the actual encoding
42 // details. 29 // details.
43 class AudioEncoder::ImplBase 30 class AudioEncoder::ImplBase
44 : public base::RefCountedThreadSafe<AudioEncoder::ImplBase> { 31 : public base::RefCountedThreadSafe<AudioEncoder::ImplBase> {
45 public: 32 public:
46 ImplBase(const scoped_refptr<CastEnvironment>& cast_environment, 33 ImplBase(const scoped_refptr<CastEnvironment>& cast_environment,
47 Codec codec, 34 Codec codec,
48 int num_channels, 35 int num_channels,
49 int sampling_rate, 36 int sampling_rate,
37 int samples_per_frame,
50 const FrameEncodedCallback& callback) 38 const FrameEncodedCallback& callback)
51 : cast_environment_(cast_environment), 39 : cast_environment_(cast_environment),
52 codec_(codec), 40 codec_(codec),
53 num_channels_(num_channels), 41 num_channels_(num_channels),
54 samples_per_frame_(sampling_rate / kFramesPerSecond), 42 samples_per_frame_(samples_per_frame),
55 callback_(callback), 43 callback_(callback),
56 cast_initialization_status_(STATUS_AUDIO_UNINITIALIZED), 44 cast_initialization_status_(STATUS_AUDIO_UNINITIALIZED),
45 frame_duration_milliseconds_(
46 double(samples_per_frame_) / sampling_rate * 1000),
miu 2014/09/29 19:13:25 This member should be of type base::TimeDelta, and
jfroy 2014/10/17 21:22:42 Thanks, that is much better.
47 underrun_threshold_milliseconds_(frame_duration_milliseconds_ * 3),
miu 2014/09/29 19:13:25 3 needs to be defined as a constant, but first con
57 buffer_fill_end_(0), 48 buffer_fill_end_(0),
58 frame_id_(0), 49 frame_id_(0),
59 frame_rtp_timestamp_(0), 50 frame_rtp_timestamp_(0),
60 samples_dropped_from_buffer_(0) { 51 samples_dropped_from_buffer_(0) {
61 // Support for max sampling rate of 48KHz, 2 channels, 100 ms duration. 52 // Support for max sampling rate of 48KHz, 2 channels, 100 ms duration.
62 const int kMaxSamplesTimesChannelsPerFrame = 48 * 2 * 100; 53 const int kMaxSamplesTimesChannelsPerFrame = 48 * 2 * 100;
63 if (num_channels_ <= 0 || samples_per_frame_ <= 0 || 54 if (num_channels_ <= 0 || samples_per_frame_ <= 0 ||
64 sampling_rate % kFramesPerSecond != 0 || 55 frame_duration_milliseconds_ < 2 ||
miu 2014/09/29 19:13:25 This check needs to be added to OpusImpl's ctor, s
miu 2014/09/29 19:13:25 "frame_duration_ > base::TimeDelta()"
jfroy 2014/10/17 21:22:42 I didn't comment on that, but the previous conditi
65 samples_per_frame_ * num_channels_ > kMaxSamplesTimesChannelsPerFrame) { 56 samples_per_frame_ * num_channels_ > kMaxSamplesTimesChannelsPerFrame) {
66 cast_initialization_status_ = STATUS_INVALID_AUDIO_CONFIGURATION; 57 cast_initialization_status_ = STATUS_INVALID_AUDIO_CONFIGURATION;
67 } 58 }
68 } 59 }
69 60
70 CastInitializationStatus InitializationResult() const { 61 CastInitializationStatus InitializationResult() const {
71 return cast_initialization_status_; 62 return cast_initialization_status_;
72 } 63 }
73 64
74 int samples_per_frame() const { 65 int samples_per_frame() const {
75 return samples_per_frame_; 66 return samples_per_frame_;
76 } 67 }
77 68
78 void EncodeAudio(scoped_ptr<AudioBus> audio_bus, 69 void EncodeAudio(scoped_ptr<AudioBus> audio_bus,
79 const base::TimeTicks& recorded_time) { 70 const base::TimeTicks& recorded_time) {
80 DCHECK_EQ(cast_initialization_status_, STATUS_AUDIO_INITIALIZED); 71 DCHECK_EQ(cast_initialization_status_, STATUS_AUDIO_INITIALIZED);
81 DCHECK(!recorded_time.is_null()); 72 DCHECK(!recorded_time.is_null());
82 73
83 // Determine whether |recorded_time| is consistent with the amount of audio 74 // Determine whether |recorded_time| is consistent with the amount of audio
84 // data having been processed in the past. Resolve the underrun problem by 75 // data having been processed in the past. Resolve the underrun problem by
85 // dropping data from the internal buffer and skipping ahead the next 76 // dropping data from the internal buffer and skipping ahead the next
86 // frame's RTP timestamp by the estimated number of frames missed. On the 77 // frame's RTP timestamp by the estimated number of frames missed. On the
87 // other hand, don't attempt to resolve overruns: A receiver should 78 // other hand, don't attempt to resolve overruns: A receiver should
88 // gracefully deal with an excess of audio data. 79 // gracefully deal with an excess of audio data.
89 const base::TimeDelta frame_duration = 80 const base::TimeDelta frame_duration =
90 base::TimeDelta::FromMilliseconds(kFrameDurationMillis); 81 base::TimeDelta::FromMilliseconds(frame_duration_milliseconds_);
91 base::TimeDelta buffer_fill_duration = 82 base::TimeDelta buffer_fill_duration =
92 buffer_fill_end_ * frame_duration / samples_per_frame_; 83 buffer_fill_end_ * frame_duration / samples_per_frame_;
93 if (!frame_capture_time_.is_null()) { 84 if (!frame_capture_time_.is_null()) {
94 const base::TimeDelta amount_ahead_by = 85 const base::TimeDelta amount_ahead_by =
95 recorded_time - (frame_capture_time_ + buffer_fill_duration); 86 recorded_time - (frame_capture_time_ + buffer_fill_duration);
96 if (amount_ahead_by > 87 if (amount_ahead_by >
97 base::TimeDelta::FromMilliseconds(kUnderrunThresholdMillis)) { 88 base::TimeDelta::FromMilliseconds(underrun_threshold_milliseconds_)) {
98 samples_dropped_from_buffer_ += buffer_fill_end_; 89 samples_dropped_from_buffer_ += buffer_fill_end_;
99 buffer_fill_end_ = 0; 90 buffer_fill_end_ = 0;
100 buffer_fill_duration = base::TimeDelta(); 91 buffer_fill_duration = base::TimeDelta();
101 const int64 num_frames_missed = amount_ahead_by / 92 const int64 num_frames_missed =
miu 2014/09/29 19:13:25 This should be: const int64 num_frames_missed =
jfroy 2014/10/17 21:22:42 Makes sense.
102 base::TimeDelta::FromMilliseconds(kFrameDurationMillis); 93 amount_ahead_by /
94 base::TimeDelta::FromMilliseconds(frame_duration_milliseconds_);
103 frame_rtp_timestamp_ += 95 frame_rtp_timestamp_ +=
104 static_cast<uint32>(num_frames_missed * samples_per_frame_); 96 static_cast<uint32>(num_frames_missed * samples_per_frame_);
105 DVLOG(1) << "Skipping RTP timestamp ahead to account for " 97 DVLOG(1) << "Skipping RTP timestamp ahead to account for "
106 << num_frames_missed * samples_per_frame_ 98 << num_frames_missed * samples_per_frame_
107 << " samples' worth of underrun."; 99 << " samples' worth of underrun.";
108 } 100 }
109 } 101 }
110 frame_capture_time_ = recorded_time - buffer_fill_duration; 102 frame_capture_time_ = recorded_time - buffer_fill_duration;
111 103
112 // Encode all audio in |audio_bus| into zero or more frames. 104 // Encode all audio in |audio_bus| into zero or more frames.
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
162 const scoped_refptr<CastEnvironment> cast_environment_; 154 const scoped_refptr<CastEnvironment> cast_environment_;
163 const Codec codec_; 155 const Codec codec_;
164 const int num_channels_; 156 const int num_channels_;
165 const int samples_per_frame_; 157 const int samples_per_frame_;
166 const FrameEncodedCallback callback_; 158 const FrameEncodedCallback callback_;
167 159
168 // Subclass' ctor is expected to set this to STATUS_AUDIO_INITIALIZED. 160 // Subclass' ctor is expected to set this to STATUS_AUDIO_INITIALIZED.
169 CastInitializationStatus cast_initialization_status_; 161 CastInitializationStatus cast_initialization_status_;
170 162
171 private: 163 private:
164 // The duration of one frame (or packet) of encoded audio samples. Derived
165 // from samples_per_frame_ and sampling_rate.
166 const int frame_duration_milliseconds_;
167
168 // Threshold used to decide whether audio being delivered to the encoder is
169 // coming in too slow with respect to the capture timestamps. Derived from
170 // frame_duration_milliseconds_.
171 const int underrun_threshold_milliseconds_;
172
172 // In the case where a call to EncodeAudio() cannot completely fill the 173 // In the case where a call to EncodeAudio() cannot completely fill the
173 // buffer, this points to the position at which to populate data in a later 174 // buffer, this points to the position at which to populate data in a later
174 // call. 175 // call.
175 int buffer_fill_end_; 176 int buffer_fill_end_;
176 177
177 // A counter used to label EncodedFrames. 178 // A counter used to label EncodedFrames.
178 uint32 frame_id_; 179 uint32 frame_id_;
179 180
180 // The RTP timestamp for the next frame of encoded audio. This is defined as 181 // The RTP timestamp for the next frame of encoded audio. This is defined as
181 // the number of audio samples encoded so far, plus the estimated number of 182 // the number of audio samples encoded so far, plus the estimated number of
(...skipping 20 matching lines...) Expand all
202 public: 203 public:
203 OpusImpl(const scoped_refptr<CastEnvironment>& cast_environment, 204 OpusImpl(const scoped_refptr<CastEnvironment>& cast_environment,
204 int num_channels, 205 int num_channels,
205 int sampling_rate, 206 int sampling_rate,
206 int bitrate, 207 int bitrate,
207 const FrameEncodedCallback& callback) 208 const FrameEncodedCallback& callback)
208 : ImplBase(cast_environment, 209 : ImplBase(cast_environment,
209 CODEC_AUDIO_OPUS, 210 CODEC_AUDIO_OPUS,
210 num_channels, 211 num_channels,
211 sampling_rate, 212 sampling_rate,
213 sampling_rate / 100, /* 10 ms frames */
miu 2014/09/29 19:13:25 style: 100 needs to be represented by an integer c
jfroy 2014/10/17 21:22:42 OK, and apply that constant to the PCM impl as wel
212 callback), 214 callback),
213 encoder_memory_(new uint8[opus_encoder_get_size(num_channels)]), 215 encoder_memory_(new uint8[opus_encoder_get_size(num_channels)]),
214 opus_encoder_(reinterpret_cast<OpusEncoder*>(encoder_memory_.get())), 216 opus_encoder_(reinterpret_cast<OpusEncoder*>(encoder_memory_.get())),
215 buffer_(new float[num_channels * samples_per_frame_]) { 217 buffer_(new float[num_channels * samples_per_frame_]) {
216 if (ImplBase::cast_initialization_status_ != STATUS_AUDIO_UNINITIALIZED) 218 if (ImplBase::cast_initialization_status_ != STATUS_AUDIO_UNINITIALIZED)
217 return; 219 return;
218 if (opus_encoder_init(opus_encoder_, 220 if (opus_encoder_init(opus_encoder_,
219 sampling_rate, 221 sampling_rate,
220 num_channels, 222 num_channels,
221 OPUS_APPLICATION_AUDIO) != OPUS_OK) { 223 OPUS_APPLICATION_AUDIO) != OPUS_OK) {
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
292 class AudioEncoder::Pcm16Impl : public AudioEncoder::ImplBase { 294 class AudioEncoder::Pcm16Impl : public AudioEncoder::ImplBase {
293 public: 295 public:
294 Pcm16Impl(const scoped_refptr<CastEnvironment>& cast_environment, 296 Pcm16Impl(const scoped_refptr<CastEnvironment>& cast_environment,
295 int num_channels, 297 int num_channels,
296 int sampling_rate, 298 int sampling_rate,
297 const FrameEncodedCallback& callback) 299 const FrameEncodedCallback& callback)
298 : ImplBase(cast_environment, 300 : ImplBase(cast_environment,
299 CODEC_AUDIO_PCM16, 301 CODEC_AUDIO_PCM16,
300 num_channels, 302 num_channels,
301 sampling_rate, 303 sampling_rate,
304 sampling_rate / 100, /* 10 ms frames */
302 callback), 305 callback),
303 buffer_(new int16[num_channels * samples_per_frame_]) { 306 buffer_(new int16[num_channels * samples_per_frame_]) {
304 if (ImplBase::cast_initialization_status_ != STATUS_AUDIO_UNINITIALIZED) 307 if (ImplBase::cast_initialization_status_ != STATUS_AUDIO_UNINITIALIZED)
305 return; 308 return;
306 cast_initialization_status_ = STATUS_AUDIO_INITIALIZED; 309 cast_initialization_status_ = STATUS_AUDIO_INITIALIZED;
307 } 310 }
308 311
309 private: 312 private:
310 virtual ~Pcm16Impl() {} 313 virtual ~Pcm16Impl() {}
311 314
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
398 cast_environment_->PostTask(CastEnvironment::AUDIO, 401 cast_environment_->PostTask(CastEnvironment::AUDIO,
399 FROM_HERE, 402 FROM_HERE,
400 base::Bind(&AudioEncoder::ImplBase::EncodeAudio, 403 base::Bind(&AudioEncoder::ImplBase::EncodeAudio,
401 impl_, 404 impl_,
402 base::Passed(&audio_bus), 405 base::Passed(&audio_bus),
403 recorded_time)); 406 recorded_time));
404 } 407 }
405 408
406 } // namespace cast 409 } // namespace cast
407 } // namespace media 410 } // namespace media
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698