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

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: fix issues discovered with initial preflight 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->supported_media_types =
81 Array<MediaTypeSetPtr>::New(arraysize(kSupportedLpcmTypeSets));
82
83 for (size_t i = 0; i < desc->supported_media_types.size(); ++i) {
84 const MediaTypeSetPtr& mts =
85 (desc->supported_media_types[i] = MediaTypeSet::New());
86
87 mts->scheme = MediaTypeScheme::LPCM;
88 mts->details = MediaTypeSetDetails::New();
89
90 const auto& s = kSupportedLpcmTypeSets[i];
91 LpcmMediaTypeSetDetailsPtr lpcm_detail = LpcmMediaTypeSetDetails::New();
92
93 lpcm_detail->sample_format = s.sample_format;
94 lpcm_detail->min_samples_per_frame = s.min_samples_per_frame;
95 lpcm_detail->max_samples_per_frame = s.max_samples_per_frame;
96 lpcm_detail->min_frames_per_second = s.min_frames_per_second;
97 lpcm_detail->max_frames_per_second = s.max_frames_per_second;
98 mts->details->set_lpcm(lpcm_detail.Pass());
99 }
100
101 cbk.Run(desc.Pass());
102 }
103
104 void AudioTrackImpl::Configure(AudioTrackConfigurationPtr configuration,
105 InterfaceRequest<MediaPipe> req,
106 const ConfigureCallback& cbk) {
107 // Are we already configured?
108 if (pipe_.IsInitialized()) {
109 cbk.Run(MediaResult::BAD_STATE);
110 return;
111 }
112
113 // Check the requested configuration.
114 if ((configuration->media_type->scheme != MediaTypeScheme::LPCM) ||
115 (!configuration->media_type->details->is_lpcm())) {
116 cbk.Run(MediaResult::UNSUPPORTED_CONFIG);
117 return;
118 }
119
120 // Search our supported configuration sets to find one compatible with this
121 // request.
122 auto& cfg = configuration->media_type->details->get_lpcm();
123 size_t i;
124 for (i = 0; i < arraysize(kSupportedLpcmTypeSets); ++i) {
125 const auto& cfg_set = kSupportedLpcmTypeSets[i];
126
127 if ((cfg->sample_format == cfg_set.sample_format) &&
128 (cfg->samples_per_frame >= cfg_set.min_samples_per_frame) &&
129 (cfg->samples_per_frame <= cfg_set.max_samples_per_frame) &&
130 (cfg->frames_per_second >= cfg_set.min_frames_per_second) &&
131 (cfg->frames_per_second <= cfg_set.max_frames_per_second)) {
132 break;
133 }
134 }
135
136 if (i >= arraysize(kSupportedLpcmTypeSets)) {
137 cbk.Run(MediaResult::UNSUPPORTED_CONFIG);
138 return;
139 }
140
141 // Sanity check the ratio which relates audio frames to media time.
142 int32_t numerator = static_cast<int32_t>(configuration->audio_frame_ratio);
143 uint32_t denominator = static_cast<int32_t>(configuration->media_time_ratio);
144 if ((numerator < 1) || (denominator < 1)) {
145 cbk.Run(MediaResult::INVALID_ARGUMENT);
146 return;
147 }
148
149
150 // Figure out the rate we need to scale by in order to produce our fixed
151 // point timestamps.
152 LinearTransform::Ratio frac_scale(1 << PTS_FRACTIONAL_BITS, 1);
153 LinearTransform::Ratio frame_scale(LinearTransform::Ratio(numerator,
154 denominator));
155 bool no_loss = LinearTransform::Ratio::Compose(frac_scale,
156 frame_scale,
157 &frame_to_media_ratio_);
158 if (!no_loss) {
159 cbk.Run(MediaResult::INVALID_ARGUMENT);
160 return;
161 }
162
163 // Figure out how many bytes we need to hold the requested number of nSec of
164 // audio.
165 switch (cfg->sample_format) {
166 case LpcmSampleFormat::UNSIGNED_8:
167 bytes_per_frame_ = 1;
168 break;
169
170 case LpcmSampleFormat::SIGNED_16:
171 bytes_per_frame_ = 2;
172 break;
173
174 case LpcmSampleFormat::SIGNED_24_IN_32:
175 bytes_per_frame_ = 4;
176 break;
177
178 default:
179 DCHECK(false);
180 bytes_per_frame_ = 2;
181 break;
182 }
183 bytes_per_frame_ *= cfg->samples_per_frame;
184
185 // Overflow trying to convert from frames to bytes?
186 uint64_t requested_frames = configuration->max_frames;
187 if (requested_frames >
188 (std::numeric_limits<size_t>::max() / bytes_per_frame_)) {
189 cbk.Run(MediaResult::INSUFFICIENT_RESOURCES);
190 return;
191 }
192
193 size_t requested_bytes = (requested_frames * bytes_per_frame_);
194
195 // Attempt to initialize our shared buffer and bind it to our interface
196 // request.
197 if (pipe_.Init(req.Pass(), requested_bytes) != MOJO_RESULT_OK) {
198 cbk.Run(MediaResult::INSUFFICIENT_RESOURCES);
199 return;
200 }
201
202 // Stash our configuration.
203 format_ = cfg.Pass();
204
205 // Have the audio output manager initialize our set of outputs. Note; there
206 // is currently no need for a lock here. Methods called from our user-facing
207 // interfaces are seriailzed by nature of the mojo framework, and none of the
208 // output manager's threads should ever need to manipulate the set. Cleanup
209 // of outputs which have gone away is currently handled in a lazy fashion when
210 // the track fails to promote its weak reference during an operation involving
211 // its outputs.
212 //
213 // TODO(johngro): someday, we will need to deal with recalculating properties
214 // which depend on a track's current set of outputs (for example, the minimum
215 // latency). This will probably be done using a dirty flag in the track
216 // implementations, and scheduling a job to recalculate the properties for the
217 // dirty tracks and notify the users as appropriate.
218
219 // If we cannot promote our own weak pointer, something is seriously wrong.
220 AudioTrackImplPtr strong_this(weak_this_.lock());
221 DCHECK(strong_this);
222 DCHECK(owner_);
223 owner_->GetOutputManager().SelectOutputsForTrack(strong_this);
224
225 // Done
226 cbk.Run(MediaResult::OK);
227 }
228
229 void AudioTrackImpl::GetRateControl(InterfaceRequest<RateControl> req,
230 const GetRateControlCallback& cbk) {
231 cbk.Run(rate_control_.Bind(req.Pass()));
232 }
233
234 void AudioTrackImpl::AddOutput(AudioTrackToOutputLinkPtr link) {
235 // TODO(johngro): assert that we are on the main message loop thread.
236 DCHECK(link);
237 auto res = outputs_.emplace(link);
238 DCHECK(res.second);
239 }
240
241 void AudioTrackImpl::RemoveOutput(AudioTrackToOutputLinkPtr link) {
242 // TODO(johngro): assert that we are on the main message loop thread.
243 DCHECK(link);
244
245 auto iter = outputs_.find(link);
246 if (iter != outputs_.end()) {
247 outputs_.erase(iter);
248 } else {
249 // TODO(johngro): that's odd. I can't think of a reason why we we should
250 // not be able to find this link in our set of outputs... should we log
251 // something about this?
252 DCHECK(false);
253 }
254 }
255
256 void AudioTrackImpl::OnPacketReceived(AudioPipe::AudioPacketRefPtr packet) {
257 DCHECK(packet);
258 for (const auto& output : outputs_) {
259 DCHECK(output);
260 output->PushToPendingQueue(packet);
261 }
262 }
263
264 void AudioTrackImpl::OnFlushRequested(const MediaPipe::FlushCallback& cbk) {
265 for (const auto& output : outputs_) {
266 DCHECK(output);
267 output->FlushPendingQueue();
268 }
269 cbk.Run(MediaResult::OK);
270 }
271
272 } // namespace audio
273 } // namespace media
274 } // namespace mojo
OLDNEW
« no previous file with comments | « services/media/audio/audio_track_impl.h ('k') | services/media/audio/audio_track_to_output_link.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698