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

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: removed build/util/LASTCHANGE Created 4 years, 11 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.
25 int Score(
26 const LpcmStreamType& in_type,
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) {
79 const StreamTypeSetPtr* best = nullptr;
80 int best_score = 0;
81 // Array has no const iterator, hence the for(;;).
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
118 // See if we have a matching COMPRESSED_AUDIO type.
119 // Array has no const iterator, hence the for(;;).
120 for (size_t i = 0; i < out_type_sets->size(); i++) {
121 const StreamTypeSetPtr& out_type_set = (*out_type_sets)[i];
122 switch (out_type_set->scheme()) {
123 case StreamType::Scheme::kAnyElementary:
124 case StreamType::Scheme::kAnyAudio:
125 case StreamType::Scheme::kAny:
126 // Wildcard scheme allows any type without conversion.
127 *out_type = in_type.Clone();
128 return AddResult::kFinished;
129 case StreamType::Scheme::kCompressedAudio: {
130 if (out_type_set->compressed_audio()->contains(in_type)) {
131 // No transform needed.
132 *out_type = in_type.Clone();
133 return AddResult::kFinished;
134 }
135 break;
136 }
137 default:
138 break;
139 }
140 // TODO(dalesat): Support a different compressed output type by transcoding.
141 }
142
143 // Find the best LPCM output type.
144 const StreamTypeSetPtr* best = FindBestLpcm(in_type, out_type_sets);
145 if (best == nullptr) {
146 // No candidates found.
147 *out_type = nullptr;
148 return AddResult::kFailed;
149 }
150
151 DCHECK_EQ((*best)->scheme(), StreamType::Scheme::kLpcm);
152
153 // Need to decode. Create a decoder and go from there.
154 DecoderPtr decoder;
155 Result result = Decoder::Create(in_type_ptr, &decoder);
156 if (result != Result::kOk) {
157 // No decoder found.
158 *out_type = nullptr;
159 return AddResult::kFailed;
160 }
161
162 *output = engine.Connect(*output, engine.Add(decoder)).output();
163 *out_type = decoder->output_stream_type();
164
165 return AddResult::kProgressed;
166 }
167
168 // Attempts to add transforms to the pipeline given an input LPCM stream type
169 // (in_type) and the output lpcm stream type set for the type we need to convert
170 // to (out_type_set). If the call succeeds, *out_type is set to the new output
171 // type. Otherwise, *out_type is set to nullptr.
172 AddResult AddTransformsForLpcm(
173 const LpcmStreamType& in_type,
174 const LpcmStreamTypeSet& out_type_set,
175 Engine& engine,
176 Engine::Output* output,
177 StreamTypePtr* out_type) {
178 DCHECK(out_type);
179
180 // TODO(dalesat): Room for more intelligence here wrt transform ordering and
181 // transforms that handle more than one conversion.
182 if (in_type.sample_format() != out_type_set.sample_format() &&
183 out_type_set.sample_format() != LpcmStreamType::SampleFormat::kAny) {
184 // The reformatter will fix interleave conversion.
185 *output = engine.Connect(
186 *output,
187 engine.Add(LpcmReformatter::New(in_type, out_type_set))).output();
188 }
189
190 if (!out_type_set.channels().contains(in_type.channels())) {
191 // TODO(dalesat): Insert mixdown/up transform.
192 NOTREACHED() << "conversion requires mixdown/up - not supported";
johngro 2016/01/20 00:25:32 why not just LOG(ERROR) or MOJO_LOG(ERROR) instead
dalesat 2016/01/25 17:47:29 The NOTREACHED will be removed as part of addressi
johngro 2016/01/27 22:35:22 Understood, but not really what I was asking. I w
dalesat 2016/01/28 18:49:15 Acknowledged.
193 *out_type = nullptr;
194 return AddResult::kFailed;
195 }
196
197 if (!out_type_set.frames_per_second().contains(in_type.frames_per_second())) {
198 // TODO(dalesat): Insert resampler.
199 NOTREACHED() << "conversion requires resampling - not supported";
200 *out_type = nullptr;
201 return AddResult::kFailed;
202 }
203
204 // Build the resulting media type.
205 *out_type = LpcmStreamType::New(
206 out_type_set.sample_format() == LpcmStreamType::SampleFormat::kAny ?
207 in_type.sample_format() :
208 out_type_set.sample_format(),
209 in_type.channels(),
210 in_type.frames_per_second());
211
212 return AddResult::kFinished;
213 }
214
215 // Attempts to add transforms to the pipeline given an input media type with
216 // scheme LPCM (in_type) and the set of output types we need to convert to
217 // (out_type_sets). If the call succeeds, *out_type is set to the new output
218 // type. Otherwise, *out_type is set to nullptr.
219 AddResult AddTransformsForLpcm(
220 const LpcmStreamType& in_type,
221 const StreamTypeSetsPtr& out_type_sets,
222 Engine& engine,
223 Engine::Output* output,
224 StreamTypePtr* out_type) {
225 DCHECK(out_type);
226
227 const StreamTypeSetPtr* best = FindBestLpcm(in_type, out_type_sets);
228 if (best == nullptr) {
229 // TODO(dalesat): Support a compressed output type by encoding.
230 NOTREACHED() << "conversion using encoder not supported";
231 *out_type = nullptr;
232 return AddResult::kFailed;
233 }
234
235 switch ((*best)->scheme()) {
236 case StreamType::Scheme::kAnyElementary:
237 case StreamType::Scheme::kAnyAudio:
238 case StreamType::Scheme::kAny:
239 // Wildcard scheme allows any type without conversion.
240 *out_type = in_type.Clone();
241 return AddResult::kFinished;
242 case StreamType::Scheme::kLpcm:
243 return AddTransformsForLpcm(
244 in_type,
245 *(*best)->lpcm(),
246 engine,
247 output,
248 out_type);
249 default:
250 NOTREACHED() << "FindBestLpcm produced unexpected type set scheme"
251 << (*best)->scheme();
252 return AddResult::kFailed;
253 }
254 }
255
256 // Attempts to add transforms to the pipeline given an input media type of any
257 // scheme (in_type) and the set of output types we need to convert to
258 // (out_type_sets). If the call succeeds, *out_type is set to the new output
259 // type. Otherwise, *out_type is set to nullptr.
260 AddResult AddTransforms(
261 const StreamTypePtr& in_type,
262 const StreamTypeSetsPtr& out_type_sets,
263 Engine& engine,
264 Engine::Output* output,
265 StreamTypePtr* out_type) {
266 DCHECK(in_type);
267 DCHECK(out_type);
268
269 switch (in_type->scheme()) {
270 case StreamType::Scheme::kLpcm:
271 return AddTransformsForLpcm(
272 *in_type->lpcm(),
273 out_type_sets,
274 engine,
275 output,
276 out_type);
277 case StreamType::Scheme::kCompressedAudio:
278 return AddTransformsForCompressedAudio(
279 *in_type->compressed_audio(),
280 in_type,
281 out_type_sets,
282 engine,
283 output,
284 out_type);
285 default:
286 NOTREACHED() << "conversion not supported for scheme"
287 << in_type->scheme();
288 *out_type = nullptr;
289 return AddResult::kFailed;
290 }
291 }
292
293 } // namespace
294
295 bool BuildConversionPipeline(
296 const StreamTypePtr& in_type,
297 const StreamTypeSetsPtr& out_type_sets,
298 Engine& engine,
299 Engine::Output* output,
300 StreamTypePtr* out_type) {
301 DCHECK(in_type);
302 DCHECK(out_type_sets);
303 DCHECK(output);
304 DCHECK(out_type);
305
306 Engine::Output out = *output;
307
308 const StreamTypePtr* type_to_convert = &in_type;
309 StreamTypePtr next_in_type;
310 while (true) {
311 StreamTypePtr converted_type;
312 switch (AddTransforms(
313 *type_to_convert,
314 out_type_sets,
315 engine,
316 &out,
317 &converted_type)) {
318 case AddResult::kFailed:
319 // Failed to find a suitable conversion. Return the pipeline to its
320 // original state.
321 engine.RemoveAll(*output);
322 *out_type = nullptr;
323 return false;
324 case AddResult::kProgressed:
325 // Made progress. Continue.
326 break;
327 case AddResult::kFinished:
328 // No further conversion required.
329 *output = out;
330 *out_type = std::move(converted_type);
331 return true;
332 }
333
334 next_in_type = std::move(converted_type);
335 type_to_convert = &next_in_type;
336 }
337 }
338
339 } // namespace media
340 } // namespace mojo
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698