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

Side by Side Diff: services/media/audio/audio_track_impl.cc

Issue 1424933002: Add an initial revision of an audio server. (Closed) Base URL: https://github.com/domokit/mojo.git@change4
Patch Set: refactor MixerKernel into a class to prepare for the addition of a linear interpolation sampler Created 5 years, 1 month 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
OLDNEW
(Empty)
1 // Copyright 2015 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 <algorithm>
6 #include <limits>
7
8 #include "base/logging.h"
9 #include "mojo/services/media/common/cpp/linear_transform.h"
10 #include "services/media/audio/audio_output_manager.h"
11 #include "services/media/audio/audio_server_impl.h"
12 #include "services/media/audio/audio_track_impl.h"
13 #include "services/media/audio/audio_track_to_output_link.h"
14
15 namespace mojo {
16 namespace media {
17 namespace audio {
18
19 constexpr size_t AudioTrackImpl::PTS_FRACTIONAL_BITS;
20
21 // TODO(johngro): If there is ever a better way to do this type of static-table
22 // initialization using mojom generated structs, we should switch to it.
23 static const struct {
24 LpcmSampleFormat sample_format;
25 uint8_t min_samples_per_frame;
26 uint8_t max_samples_per_frame;
27 uint32_t min_frames_per_second;
28 uint32_t max_frames_per_second;
29 } kSupportedLpcmTypeSets[] = {
30 {
31 .sample_format = LpcmSampleFormat::UNSIGNED_8,
32 .min_samples_per_frame = 1,
33 .max_samples_per_frame = 2,
34 .min_frames_per_second = 1000,
35 .max_frames_per_second = 48000,
36 },
37 {
38 .sample_format = LpcmSampleFormat::SIGNED_16,
39 .min_samples_per_frame = 1,
40 .max_samples_per_frame = 2,
41 .min_frames_per_second = 1000,
42 .max_frames_per_second = 48000,
43 },
44 };
45
46 AudioTrackImpl::AudioTrackImpl(InterfaceRequest<AudioTrack> iface,
47 AudioServerImpl* owner)
48 : owner_(owner),
49 binding_(this),
50 pipe_(this, owner) {
51 CHECK(nullptr != owner_);
52 binding_.Bind(iface.Pass());
53 }
54
55 AudioTrackImpl::~AudioTrackImpl() {
56 }
57
58 AudioTrackImplPtr AudioTrackImpl::Create(InterfaceRequest<AudioTrack> iface,
59 AudioServerImpl* owner) {
60 AudioTrackImplPtr ret(new AudioTrackImpl(iface.Pass(), owner));
61 ret->weak_this_ = ret;
62 return ret;
63 }
64
65 void AudioTrackImpl::Describe(const DescribeCallback& cbk) {
66 // Build a minimal descriptor
67 //
68 // TODO(johngro): one day, we need to make this description much more rich and
69 // fully describe our capabilities, based on things like what outputs are
70 // available, the class of hardware we are on, and what options we were
71 // compiled with.
72 //
73 // For now, it would be nice to just be able to have a static const tree of
74 // capabilities in this translational unit which we could use to construct our
75 // message, but the nature of the structures generated by the C++ bindings
76 // make this difficult. For now, we just create a trivial descriptor entierly
77 // by hand.
78 AudioTrackDescriptorPtr desc(AudioTrackDescriptor::New());
79
80 desc->supports_push_transport = true;
81 desc->supports_pull_transport = false;
82 desc->supported_media_types =
83 Array<MediaTypeSetPtr>::New(arraysize(kSupportedLpcmTypeSets));
84
85 for (size_t i = 0; i < desc->supported_media_types.size(); ++i) {
86 const MediaTypeSetPtr& mts =
87 (desc->supported_media_types[i] = MediaTypeSet::New());
88
89 mts->scheme = MediaTypeScheme::LPCM;
90 mts->details = MediaTypeSetDetails::New();
91
92 const auto& s = kSupportedLpcmTypeSets[i];
93 LpcmMediaTypeSetDetailsPtr lpcm_detail = LpcmMediaTypeSetDetails::New();
94
95 lpcm_detail->sample_format = s.sample_format;
96 lpcm_detail->min_samples_per_frame = s.min_samples_per_frame;
97 lpcm_detail->max_samples_per_frame = s.max_samples_per_frame;
98 lpcm_detail->min_frames_per_second = s.min_frames_per_second;
99 lpcm_detail->max_frames_per_second = s.max_frames_per_second;
100 mts->details->set_lpcm(lpcm_detail.Pass());
101 }
102
103 cbk.Run(desc.Pass());
104 }
105
106 void AudioTrackImpl::Configure(AudioTrackConfigurationPtr configuration,
107 InterfaceRequest<MediaPipe> req,
108 const ConfigureCallback& cbk) {
109 // Are we already configured?
jeffbrown 2015/11/04 23:43:33 Here's a place where it would be nice to just conf
johngro 2015/11/06 02:20:26 Acknowledged. Its on the TODO list.
110 if (pipe_.IsInitialized()) {
111 cbk.Run(MediaResult::BAD_STATE);
112 return;
113 }
114
115 // Check the requested configuration.
116 if ((configuration->media_type->scheme != MediaTypeScheme::LPCM) ||
117 (!configuration->media_type->details->is_lpcm())) {
118 cbk.Run(MediaResult::UNSUPPORTED_CONFIG);
jeffbrown 2015/11/04 23:43:33 Assuming clients should have known better before a
johngro 2015/11/06 02:20:25 Acknowledged. In the close-the-connection TODO buc
119 return;
120 }
121
122 // Search our supported configuration sets to find one compatible with this
123 // request.
124 const auto& cfg = configuration->media_type->details->get_lpcm();
125 size_t i;
126 for (i = 0; i < arraysize(kSupportedLpcmTypeSets); ++i) {
127 const auto& cfg_set = kSupportedLpcmTypeSets[i];
128
129 if ((cfg->sample_format == cfg_set.sample_format) &&
130 (cfg->samples_per_frame >= cfg_set.min_samples_per_frame) &&
131 (cfg->samples_per_frame <= cfg_set.max_samples_per_frame) &&
132 (cfg->frames_per_second >= cfg_set.min_frames_per_second) &&
133 (cfg->frames_per_second <= cfg_set.max_frames_per_second)) {
134 break;
135 }
136 }
137
138 if (i >= arraysize(kSupportedLpcmTypeSets)) {
139 cbk.Run(MediaResult::UNSUPPORTED_CONFIG);
140 return;
141 }
142
143 // Sanity check the ratio which relates audio frames to media time.
144 int32_t N = static_cast<int32_t>(configuration->audio_frame_ratio);
jeffbrown 2015/11/04 23:43:33 numerator / denominator, or num / denom Also, see
johngro 2015/11/06 02:20:26 re: future efforts... Precision is important here.
145 uint32_t D = static_cast<int32_t>(configuration->media_time_ratio);
146 if ((N < 1) || (D < 1)) {
147 cbk.Run(MediaResult::INVALID_ARGUMENT);
148 return;
149 }
150
151
152 // Figure out the rate we need to scale by in order to produce our fixed
153 // point timestamps.
154 LinearTransform::Ratio frac_scale(1 << PTS_FRACTIONAL_BITS, 1);
155 LinearTransform::Ratio frame_scale(LinearTransform::Ratio(N, D));
156 bool no_loss = LinearTransform::Ratio::Compose(frac_scale,
157 frame_scale,
158 &frame_to_media_ratio_);
159 if (!no_loss) {
160 cbk.Run(MediaResult::INVALID_ARGUMENT);
161 return;
162 }
163
164 // Figure out how many bytes we need to hold the requested number of nSec of
165 // audio.
166 switch (cfg->sample_format) {
167 case LpcmSampleFormat::UNSIGNED_8:
168 bytes_per_frame_ = 1;
169 break;
170
171 case LpcmSampleFormat::SIGNED_16:
172 bytes_per_frame_ = 2;
173 break;
174
175 case LpcmSampleFormat::SIGNED_24_IN_32:
176 bytes_per_frame_ = 4;
177 break;
178
179 default:
180 DCHECK(false);
181 bytes_per_frame_ = 2;
182 break;
183 }
184 bytes_per_frame_ *= cfg->samples_per_frame;
185
186 // Overflow trying to convert from frames to bytes?
187 uint64_t requested_frames = configuration->max_frames;
188 if (requested_frames >
189 (std::numeric_limits<size_t>::max() / bytes_per_frame_)) {
190 cbk.Run(MediaResult::INSUFFICIENT_RESOURCES);
191 return;
192 }
193
194 size_t requested_bytes = (requested_frames * bytes_per_frame_);
195
196 // Attempt to initialize our shared buffer and bind it to our interface
197 // request.
198 if (pipe_.Init(req.Pass(), requested_bytes) != MOJO_RESULT_OK) {
199 cbk.Run(MediaResult::INSUFFICIENT_RESOURCES);
200 return;
201 }
202
203 // Stash our configuration.
204 format_ = cfg->Clone();
jeffbrown 2015/11/04 23:43:33 Looks to me like the config is just going to be dr
johngro 2015/11/06 02:20:26 Done. The fact that this is even allowed is a bit
205
206 // Have the audio output manager initialize our set of outputs. Note; there
207 // is currently no need for a lock here. Methods called from our user-facing
208 // interfaces are seriailzed by nature of the mojo framework, and none of the
209 // output manager's threads should ever need to manipulate the set. Cleanup
210 // of outputs which have gone away is currently handled in a lazy fashion when
211 // the track fails to promote its weak reference during an operation involving
212 // its outputs.
213 //
214 // TODO(johngro): someday, we will need to deal with recalculating properties
215 // which depend on a track's current set of outputs (for example, the minimum
216 // latency). This will probably be done using a dirty flag in the track
217 // implementations, and scheduling a job to recalculate the properties for the
218 // dirty tracks and notify the users as appropriate.
219
220 // If we cannot promote our own weak pointer, something is seriously wrong.
221 AudioTrackImplPtr strong_this(weak_this_.lock());
222 DCHECK(strong_this);
223 DCHECK(owner_);
224 owner_->GetOutputManager().SelectOutputsForTrack(strong_this);
225
226 // Done
227 cbk.Run(MediaResult::OK);
228 }
229
230 void AudioTrackImpl::GetRateControl(InterfaceRequest<RateControl> req,
231 const GetRateControlCallback& cbk) {
232 cbk.Run(rate_control_.Bind(req.Pass()));
233 }
234
235 void AudioTrackImpl::AddOutput(AudioTrackToOutputLinkPtr link) {
236 // TODO(johngro): assert that we are on the main message loop thread.
237 DCHECK(link);
238 auto res = outputs_.emplace(link);
239 DCHECK(res.second);
240 }
241
242 void AudioTrackImpl::RemoveOutput(AudioTrackToOutputLinkPtr link) {
243 // TODO(johngro): assert that we are on the main message loop thread.
244 DCHECK(link);
245
246 auto iter = outputs_.find(link);
247 if (iter != outputs_.end()) {
248 outputs_.erase(iter);
249 } else {
250 // TODO(johngro): that's odd. I can't think of a reason why we we should
251 // not be able to find this link in our set of outputs... should we log
252 // something about this?
jeffbrown 2015/11/04 23:43:33 DCHECK! We failed an internal invariant.
johngro 2015/11/06 02:20:26 Done.
253 }
254 }
255
256 void AudioTrackImpl::OnPacketReceived(AudioPipe::AudioPacketRefPtr packet) {
jeffbrown 2015/11/04 23:43:33 Do we need to DCHECK the packet?
johngro 2015/11/06 02:20:26 no, but it does not hurt to do so. I have been ca
257 for (const auto& output : outputs_) {
258 DCHECK(output);
259 output->PushToPendingQueue(packet);
260 }
261 }
262
263 void AudioTrackImpl::OnFlushRequested(const MediaPipe::FlushCallback& cbk) {
264 for (const auto& output : outputs_) {
265 DCHECK(output);
jeffbrown 2015/11/04 23:43:33 I feel like this DCHECK is kind of redundant given
johngro 2015/11/06 02:20:26 Acknowledged. Can't have it both ways. Either we
266 output->FlushPendingQueue();
267 }
268 cbk.Run(MediaResult::OK);
269 }
270
271 } // namespace audio
272 } // namespace media
273 } // namespace mojo
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698