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

Side by Side Diff: services/media/framework/conversion_pipeline_builder.cc

Issue 1577953002: Motown in-proc streaming framework used to implement media services. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Fixes based on feedback. Created 4 years, 10 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
OLDNEW
(Empty)
1 // Copyright 2016 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 "services/media/framework/conversion_pipeline_builder.h"
6 #include "services/media/framework/ostream.h"
7 #include "services/media/framework/parts/decoder.h"
8 #include "services/media/framework/parts/lpcm_reformatter.h"
9
10 namespace mojo {
11 namespace media {
12
13 namespace {
14
15 enum class AddResult {
16 kFailed, // Can't convert.
17 kProgressed, // Added a conversion transform.
18 kFinished // Done adding conversion transforms.
19 };
20
21 // Produces a score for in_type with respect to out_type_set. The score
22 // is used to compare type sets to see which represents the best goal for
23 // conversion. Higher scores are preferred. A score of zero indicates that
24 // in_type is incompatible with out_type_set.
jeffbrown 2016/02/02 05:35:46 I'm generally skeptical of baking policies like th
dalesat 2016/02/02 21:46:38 From the framework perspective, there's no baking
25 int Score(
26 const LpcmStreamType& in_type,
jeffbrown 2016/02/02 05:35:46 Formatting looks wrong here. Use clang-format.
dalesat 2016/02/02 21:46:38 Acknowledged.
27 const LpcmStreamTypeSet& out_type_set) {
28 // TODO(dalesat): Plenty of room for more subtlety here. Maybe actually
29 // measure conversion costs (cpu, quality, etc) and reflect them here.
30
31 int score = 1; // We can convert anything, so 1 is the minimum score.
32
33 if (in_type.sample_format() == out_type_set.sample_format() ||
34 out_type_set.sample_format() == LpcmStreamType::SampleFormat::kAny) {
35 // Prefer not to convert sample format.
36 score += 10;
37 } else {
38 // Prefer higher-quality formats.
39 switch (out_type_set.sample_format()) {
40 case LpcmStreamType::SampleFormat::kUnsigned8:
41 break;
42 case LpcmStreamType::SampleFormat::kSigned16:
43 score += 1;
44 break;
45 case LpcmStreamType::SampleFormat::kSigned24In32:
46 score += 2;
47 break;
48 case LpcmStreamType::SampleFormat::kFloat:
49 score += 3;
50 break;
51 default:
52 NOTREACHED() << "unsupported sample format "
53 << out_type_set.sample_format();
54 }
55 }
56
57 if (out_type_set.channels().contains(in_type.channels())) {
58 // Prefer not to mixdown/up.
59 score += 10;
60 } else {
61 return 0; // TODO(dalesat): Remove when we have mixdown/up.
62 }
63
64 if (out_type_set.frames_per_second().
65 contains(in_type.frames_per_second())) {
66 // Very much prefer not to resample.
67 score += 50;
68 } else {
69 return 0; // TODO(dalesat): Remove when we have resamplers.
70 }
71
72 return score;
73 }
74
75 // Finds the media type set that best matches in_type.
76 const StreamTypeSetPtr* FindBestLpcm(
77 const LpcmStreamType& in_type,
78 const StreamTypeSetsPtr& out_type_sets) {
jeffbrown 2016/02/02 05:35:46 It's really hard to follow this code when you have
dalesat 2016/02/02 21:46:38 Acknowledged.
79 const StreamTypeSetPtr* best = nullptr;
80 int best_score = 0;
81 // Array has no const iterator, hence the for(;;).
jeffbrown 2016/02/02 05:35:46 array->storage().cbegin()...
dalesat 2016/02/02 21:46:38 Comment predates transition from MediaType to Stre
82 for (size_t i = 0; i < out_type_sets->size(); i++) {
83 const StreamTypeSetPtr& out_type_set = (*out_type_sets)[i];
84 switch (out_type_set->scheme()) {
85 case StreamType::Scheme::kAnyElementary:
86 case StreamType::Scheme::kAnyAudio:
87 case StreamType::Scheme::kAny:
88 // Wildcard scheme allows any type without conversion.
89 return &out_type_set;
90 case StreamType::Scheme::kLpcm: {
91 int score = Score(in_type, *out_type_set->lpcm());
92 if (best_score < score) {
93 best_score = score;
94 best = &out_type_set;
95 }
96 break;
97 }
98 default:
99 break;
100 }
101 }
102 return best;
103 }
104
105 // Attempts to add transforms to the pipeline given an input compressed audio
106 // stream type with (in_type) and the set of output types we need to convert to
107 // (out_type_sets). If the call succeeds, *out_type is set to the new output
108 // type. Otherwise, *out_type is set to nullptr.
109 AddResult AddTransformsForCompressedAudio(
110 const CompressedAudioStreamType& in_type,
111 const StreamTypePtr& in_type_ptr,
112 const StreamTypeSetsPtr& out_type_sets,
113 Engine* engine,
114 Engine::Output* output,
115 StreamTypePtr* out_type) {
116 DCHECK(out_type);
117 DCHECK(engine);
118
119 // See if we have a matching COMPRESSED_AUDIO type.
120 // Array has no const iterator, hence the for(;;).
121 for (size_t i = 0; i < out_type_sets->size(); i++) {
122 const StreamTypeSetPtr& out_type_set = (*out_type_sets)[i];
123 switch (out_type_set->scheme()) {
124 case StreamType::Scheme::kAnyElementary:
125 case StreamType::Scheme::kAnyAudio:
126 case StreamType::Scheme::kAny:
127 // Wildcard scheme allows any type without conversion.
128 *out_type = in_type.Clone();
129 return AddResult::kFinished;
130 case StreamType::Scheme::kCompressedAudio: {
131 if (out_type_set->compressed_audio()->contains(in_type)) {
132 // No transform needed.
133 *out_type = in_type.Clone();
134 return AddResult::kFinished;
135 }
136 break;
137 }
138 default:
139 break;
140 }
141 // TODO(dalesat): Support a different compressed output type by transcoding.
142 }
143
144 // Find the best LPCM output type.
145 const StreamTypeSetPtr* best = FindBestLpcm(in_type, out_type_sets);
146 if (best == nullptr) {
147 // No candidates found.
148 *out_type = nullptr;
149 return AddResult::kFailed;
150 }
151
152 DCHECK_EQ((*best)->scheme(), StreamType::Scheme::kLpcm);
153
154 // Need to decode. Create a decoder and go from there.
155 DecoderPtr decoder;
156 Result result = Decoder::Create(in_type_ptr, &decoder);
157 if (result != Result::kOk) {
158 // No decoder found.
159 *out_type = nullptr;
160 return AddResult::kFailed;
161 }
162
163 *output = engine->Connect(*output, engine->Add(decoder)).output();
164 *out_type = decoder->output_stream_type();
165
166 return AddResult::kProgressed;
167 }
168
169 // Attempts to add transforms to the pipeline given an input LPCM stream type
170 // (in_type) and the output lpcm stream type set for the type we need to convert
171 // to (out_type_set). If the call succeeds, *out_type is set to the new output
172 // type. Otherwise, *out_type is set to nullptr.
173 AddResult AddTransformsForLpcm(
174 const LpcmStreamType& in_type,
175 const LpcmStreamTypeSet& out_type_set,
176 Engine* engine,
177 Engine::Output* output,
178 StreamTypePtr* out_type) {
179 DCHECK(engine);
180 DCHECK(out_type);
181
182 // TODO(dalesat): Room for more intelligence here wrt transform ordering and
183 // transforms that handle more than one conversion.
184 if (in_type.sample_format() != out_type_set.sample_format() &&
185 out_type_set.sample_format() != LpcmStreamType::SampleFormat::kAny) {
186 // The reformatter will fix interleave conversion.
187 *output = engine->Connect(
188 *output,
189 engine->Add(LpcmReformatter::New(in_type, out_type_set))).output();
190 }
191
192 if (!out_type_set.channels().contains(in_type.channels())) {
193 // TODO(dalesat): Insert mixdown/up transform.
194 NOTREACHED() << "conversion requires mixdown/up - not supported";
195 *out_type = nullptr;
196 return AddResult::kFailed;
197 }
198
199 if (!out_type_set.frames_per_second().contains(in_type.frames_per_second())) {
200 // TODO(dalesat): Insert resampler.
201 NOTREACHED() << "conversion requires resampling - not supported";
202 *out_type = nullptr;
203 return AddResult::kFailed;
204 }
205
206 // Build the resulting media type.
207 *out_type = LpcmStreamType::New(
208 out_type_set.sample_format() == LpcmStreamType::SampleFormat::kAny ?
209 in_type.sample_format() :
210 out_type_set.sample_format(),
211 in_type.channels(),
212 in_type.frames_per_second());
213
214 return AddResult::kFinished;
215 }
216
217 // Attempts to add transforms to the pipeline given an input media type with
218 // scheme LPCM (in_type) and the set of output types we need to convert to
219 // (out_type_sets). If the call succeeds, *out_type is set to the new output
220 // type. Otherwise, *out_type is set to nullptr.
221 AddResult AddTransformsForLpcm(
222 const LpcmStreamType& in_type,
223 const StreamTypeSetsPtr& out_type_sets,
224 Engine* engine,
225 Engine::Output* output,
226 StreamTypePtr* out_type) {
227 DCHECK(engine);
228 DCHECK(out_type);
229
230 const StreamTypeSetPtr* best = FindBestLpcm(in_type, out_type_sets);
231 if (best == nullptr) {
232 // TODO(dalesat): Support a compressed output type by encoding.
233 NOTREACHED() << "conversion using encoder not supported";
234 *out_type = nullptr;
235 return AddResult::kFailed;
236 }
237
238 switch ((*best)->scheme()) {
239 case StreamType::Scheme::kAnyElementary:
240 case StreamType::Scheme::kAnyAudio:
241 case StreamType::Scheme::kAny:
242 // Wildcard scheme allows any type without conversion.
243 *out_type = in_type.Clone();
244 return AddResult::kFinished;
245 case StreamType::Scheme::kLpcm:
246 return AddTransformsForLpcm(
247 in_type,
248 *(*best)->lpcm(),
249 engine,
250 output,
251 out_type);
252 default:
253 NOTREACHED() << "FindBestLpcm produced unexpected type set scheme"
254 << (*best)->scheme();
255 return AddResult::kFailed;
256 }
257 }
258
259 // Attempts to add transforms to the pipeline given an input media type of any
260 // scheme (in_type) and the set of output types we need to convert to
261 // (out_type_sets). If the call succeeds, *out_type is set to the new output
262 // type. Otherwise, *out_type is set to nullptr.
263 AddResult AddTransforms(
264 const StreamTypePtr& in_type,
265 const StreamTypeSetsPtr& out_type_sets,
266 Engine* engine,
267 Engine::Output* output,
268 StreamTypePtr* out_type) {
269 DCHECK(in_type);
270 DCHECK(engine);
271 DCHECK(out_type);
272
273 switch (in_type->scheme()) {
274 case StreamType::Scheme::kLpcm:
275 return AddTransformsForLpcm(
276 *in_type->lpcm(),
277 out_type_sets,
278 engine,
279 output,
280 out_type);
281 case StreamType::Scheme::kCompressedAudio:
282 return AddTransformsForCompressedAudio(
283 *in_type->compressed_audio(),
284 in_type,
285 out_type_sets,
286 engine,
287 output,
288 out_type);
289 default:
290 NOTREACHED() << "conversion not supported for scheme"
291 << in_type->scheme();
292 *out_type = nullptr;
293 return AddResult::kFailed;
294 }
295 }
296
297 } // namespace
298
299 bool BuildConversionPipeline(
300 const StreamTypePtr& in_type,
301 const StreamTypeSetsPtr& out_type_sets,
302 Engine* engine,
303 Engine::Output* output,
304 StreamTypePtr* out_type) {
305 DCHECK(in_type);
306 DCHECK(out_type_sets);
307 DCHECK(engine);
308 DCHECK(output);
309 DCHECK(out_type);
310
311 Engine::Output out = *output;
312
313 const StreamTypePtr* type_to_convert = &in_type;
314 StreamTypePtr next_in_type;
315 while (true) {
316 StreamTypePtr converted_type;
317 switch (AddTransforms(
318 *type_to_convert,
319 out_type_sets,
320 engine,
321 &out,
322 &converted_type)) {
323 case AddResult::kFailed:
324 // Failed to find a suitable conversion. Return the pipeline to its
325 // original state.
326 engine->RemoveAll(*output);
327 *out_type = nullptr;
328 return false;
329 case AddResult::kProgressed:
330 // Made progress. Continue.
331 break;
332 case AddResult::kFinished:
333 // No further conversion required.
334 *output = out;
335 *out_type = std::move(converted_type);
336 return true;
337 }
338
339 next_in_type = std::move(converted_type);
340 type_to_convert = &next_in_type;
341 }
342 }
343
344 } // namespace media
345 } // namespace mojo
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698