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

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: sync 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/formatting.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 for (const StreamTypeSetPtr& out_type_set : *out_type_sets) {
82 switch (out_type_set->scheme()) {
83 case StreamType::Scheme::kAnyElementary:
84 case StreamType::Scheme::kAnyAudio:
85 case StreamType::Scheme::kAny:
86 // Wildcard scheme allows any type without conversion.
87 return &out_type_set;
88 case StreamType::Scheme::kLpcm: {
89 int score = Score(in_type, *out_type_set->lpcm());
90 if (best_score < score) {
91 best_score = score;
92 best = &out_type_set;
93 }
94 break;
95 }
96 default:
97 break;
98 }
99 }
100 return best;
101 }
102
103 // Attempts to add transforms to the pipeline given an input compressed audio
104 // stream type with (in_type) and the set of output types we need to convert to
105 // (out_type_sets). If the call succeeds, *out_type is set to the new output
106 // type. Otherwise, *out_type is set to nullptr.
107 AddResult AddTransformsForCompressedAudio(
108 const CompressedAudioStreamType& in_type,
109 const StreamTypePtr& in_type_ptr,
110 const StreamTypeSetsPtr& out_type_sets,
111 Engine* engine,
112 Engine::Output* output,
113 StreamTypePtr* out_type) {
114 DCHECK(out_type);
115 DCHECK(engine);
116
117 // See if we have a matching COMPRESSED_AUDIO type.
118 for (const StreamTypeSetPtr& out_type_set : *out_type_sets) {
119 switch (out_type_set->scheme()) {
120 case StreamType::Scheme::kAnyElementary:
121 case StreamType::Scheme::kAnyAudio:
122 case StreamType::Scheme::kAny:
123 // Wildcard scheme allows any type without conversion.
124 *out_type = in_type.Clone();
125 return AddResult::kFinished;
126 case StreamType::Scheme::kCompressedAudio: {
127 if (out_type_set->compressed_audio()->contains(in_type)) {
128 // No transform needed.
129 *out_type = in_type.Clone();
130 return AddResult::kFinished;
131 }
132 break;
133 }
134 default:
135 break;
136 }
137 // TODO(dalesat): Support a different compressed output type by transcoding.
138 }
139
140 // Find the best LPCM output type.
141 const StreamTypeSetPtr* best = FindBestLpcm(in_type, out_type_sets);
142 if (best == nullptr) {
143 // No candidates found.
144 *out_type = nullptr;
145 return AddResult::kFailed;
146 }
147
148 DCHECK_EQ((*best)->scheme(), StreamType::Scheme::kLpcm);
149
150 // Need to decode. Create a decoder and go from there.
151 DecoderPtr decoder;
152 Result result = Decoder::Create(in_type_ptr, &decoder);
153 if (result != Result::kOk) {
154 // No decoder found.
155 *out_type = nullptr;
156 return AddResult::kFailed;
157 }
158
159 *output = engine->ConnectOutputToPart(*output, engine->Add(decoder)).output();
160 *out_type = decoder->output_stream_type();
161
162 return AddResult::kProgressed;
163 }
164
165 // Attempts to add transforms to the pipeline given an input LPCM stream type
166 // (in_type) and the output lpcm stream type set for the type we need to convert
167 // to (out_type_set). If the call succeeds, *out_type is set to the new output
168 // type. Otherwise, *out_type is set to nullptr.
169 AddResult AddTransformsForLpcm(
170 const LpcmStreamType& in_type,
171 const LpcmStreamTypeSet& out_type_set,
172 Engine* engine,
173 Engine::Output* output,
174 StreamTypePtr* out_type) {
175 DCHECK(engine);
176 DCHECK(out_type);
177
178 // TODO(dalesat): Room for more intelligence here wrt transform ordering and
179 // transforms that handle more than one conversion.
180 if (in_type.sample_format() != out_type_set.sample_format() &&
181 out_type_set.sample_format() != LpcmStreamType::SampleFormat::kAny) {
182 // The reformatter will fix interleave conversion.
183 *output = engine->ConnectOutputToPart(
184 *output,
185 engine->Add(LpcmReformatter::Create(in_type, out_type_set))).output();
186 }
187
188 if (!out_type_set.channels().contains(in_type.channels())) {
189 // TODO(dalesat): Insert mixdown/up transform.
190 NOTREACHED() << "conversion requires mixdown/up - not supported";
191 *out_type = nullptr;
192 return AddResult::kFailed;
193 }
194
195 if (!out_type_set.frames_per_second().contains(in_type.frames_per_second())) {
196 // TODO(dalesat): Insert resampler.
197 NOTREACHED() << "conversion requires resampling - not supported";
198 *out_type = nullptr;
199 return AddResult::kFailed;
200 }
201
202 // Build the resulting media type.
203 *out_type = LpcmStreamType::Create(
204 out_type_set.sample_format() == LpcmStreamType::SampleFormat::kAny ?
205 in_type.sample_format() :
206 out_type_set.sample_format(),
207 in_type.channels(),
208 in_type.frames_per_second());
209
210 return AddResult::kFinished;
211 }
212
213 // Attempts to add transforms to the pipeline given an input media type with
214 // scheme LPCM (in_type) and the set of output types we need to convert to
215 // (out_type_sets). If the call succeeds, *out_type is set to the new output
216 // type. Otherwise, *out_type is set to nullptr.
217 AddResult AddTransformsForLpcm(
218 const LpcmStreamType& in_type,
219 const StreamTypeSetsPtr& out_type_sets,
220 Engine* engine,
221 Engine::Output* output,
222 StreamTypePtr* out_type) {
223 DCHECK(engine);
224 DCHECK(out_type);
225
226 const StreamTypeSetPtr* best = FindBestLpcm(in_type, out_type_sets);
227 if (best == nullptr) {
228 // TODO(dalesat): Support a compressed output type by encoding.
229 NOTREACHED() << "conversion using encoder not supported";
230 *out_type = nullptr;
231 return AddResult::kFailed;
232 }
233
234 switch ((*best)->scheme()) {
235 case StreamType::Scheme::kAnyElementary:
236 case StreamType::Scheme::kAnyAudio:
237 case StreamType::Scheme::kAny:
238 // Wildcard scheme allows any type without conversion.
239 *out_type = in_type.Clone();
240 return AddResult::kFinished;
241 case StreamType::Scheme::kLpcm:
242 return AddTransformsForLpcm(
243 in_type,
244 *(*best)->lpcm(),
245 engine,
246 output,
247 out_type);
248 default:
249 NOTREACHED() << "FindBestLpcm produced unexpected type set scheme"
250 << (*best)->scheme();
251 return AddResult::kFailed;
252 }
253 }
254
255 // Attempts to add transforms to the pipeline given an input media type of any
256 // scheme (in_type) and the set of output types we need to convert to
257 // (out_type_sets). If the call succeeds, *out_type is set to the new output
258 // type. Otherwise, *out_type is set to nullptr.
259 AddResult AddTransforms(
260 const StreamTypePtr& in_type,
261 const StreamTypeSetsPtr& out_type_sets,
262 Engine* engine,
263 Engine::Output* output,
264 StreamTypePtr* out_type) {
265 DCHECK(in_type);
266 DCHECK(engine);
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(engine);
304 DCHECK(output);
305 DCHECK(out_type);
306
307 Engine::Output out = *output;
308
309 const StreamTypePtr* type_to_convert = &in_type;
310 StreamTypePtr next_in_type;
311 while (true) {
312 StreamTypePtr converted_type;
313 switch (AddTransforms(
314 *type_to_convert,
315 out_type_sets,
316 engine,
317 &out,
318 &converted_type)) {
319 case AddResult::kFailed:
320 // Failed to find a suitable conversion. Return the pipeline to its
321 // original state.
322 engine->RemovePartsConnectedToOutput(*output);
323 *out_type = nullptr;
324 return false;
325 case AddResult::kProgressed:
326 // Made progress. Continue.
327 break;
328 case AddResult::kFinished:
329 // No further conversion required.
330 *output = out;
331 *out_type = std::move(converted_type);
332 return true;
333 }
334
335 next_in_type = std::move(converted_type);
336 type_to_convert = &next_in_type;
337 }
338 }
339
340 } // namespace media
341 } // namespace mojo
OLDNEW
« no previous file with comments | « services/media/framework/conversion_pipeline_builder.h ('k') | services/media/framework/engine.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698