OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "services/media/framework/conversion_pipeline_builder.h" | 5 #include "services/media/framework/conversion_pipeline_builder.h" |
6 #include "services/media/framework/formatting.h" | 6 #include "services/media/framework/formatting.h" |
7 #include "services/media/framework/parts/decoder.h" | 7 #include "services/media/framework/parts/decoder.h" |
8 #include "services/media/framework/parts/lpcm_reformatter.h" | 8 #include "services/media/framework/parts/lpcm_reformatter.h" |
9 | 9 |
10 namespace mojo { | 10 namespace mojo { |
11 namespace media { | 11 namespace media { |
12 | 12 |
13 namespace { | 13 namespace { |
14 | 14 |
15 enum class AddResult { | 15 enum class AddResult { |
16 kFailed, // Can't convert. | 16 kFailed, // Can't convert. |
17 kProgressed, // Added a conversion transform. | 17 kProgressed, // Added a conversion transform. |
18 kFinished // Done adding conversion transforms. | 18 kFinished // Done adding conversion transforms. |
19 }; | 19 }; |
20 | 20 |
21 // Produces a score for in_type with respect to out_type_set. The score | 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 | 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 | 23 // conversion. Higher scores are preferred. A score of zero indicates that |
24 // in_type is incompatible with out_type_set. | 24 // in_type is incompatible with out_type_set. |
25 int Score( | 25 int Score(const LpcmStreamType& in_type, |
26 const LpcmStreamType& in_type, | 26 const LpcmStreamTypeSet& out_type_set) { |
27 const LpcmStreamTypeSet& out_type_set) { | |
28 // TODO(dalesat): Plenty of room for more subtlety here. Maybe actually | 27 // TODO(dalesat): Plenty of room for more subtlety here. Maybe actually |
29 // measure conversion costs (cpu, quality, etc) and reflect them here. | 28 // measure conversion costs (cpu, quality, etc) and reflect them here. |
30 | 29 |
31 int score = 1; // We can convert anything, so 1 is the minimum score. | 30 int score = 1; // We can convert anything, so 1 is the minimum score. |
32 | 31 |
33 if (in_type.sample_format() == out_type_set.sample_format() || | 32 if (in_type.sample_format() == out_type_set.sample_format() || |
34 out_type_set.sample_format() == LpcmStreamType::SampleFormat::kAny) { | 33 out_type_set.sample_format() == LpcmStreamType::SampleFormat::kAny) { |
35 // Prefer not to convert sample format. | 34 // Prefer not to convert sample format. |
36 score += 10; | 35 score += 10; |
37 } else { | 36 } else { |
38 // Prefer higher-quality formats. | 37 // Prefer higher-quality formats. |
39 switch (out_type_set.sample_format()) { | 38 switch (out_type_set.sample_format()) { |
40 case LpcmStreamType::SampleFormat::kUnsigned8: | 39 case LpcmStreamType::SampleFormat::kUnsigned8: |
41 break; | 40 break; |
42 case LpcmStreamType::SampleFormat::kSigned16: | 41 case LpcmStreamType::SampleFormat::kSigned16: |
43 score += 1; | 42 score += 1; |
44 break; | 43 break; |
45 case LpcmStreamType::SampleFormat::kSigned24In32: | 44 case LpcmStreamType::SampleFormat::kSigned24In32: |
46 score += 2; | 45 score += 2; |
47 break; | 46 break; |
48 case LpcmStreamType::SampleFormat::kFloat: | 47 case LpcmStreamType::SampleFormat::kFloat: |
49 score += 3; | 48 score += 3; |
50 break; | 49 break; |
51 default: | 50 default: |
52 NOTREACHED() << "unsupported sample format " | 51 NOTREACHED() << "unsupported sample format " |
53 << out_type_set.sample_format(); | 52 << out_type_set.sample_format(); |
54 } | 53 } |
55 } | 54 } |
56 | 55 |
57 if (out_type_set.channels().contains(in_type.channels())) { | 56 if (out_type_set.channels().contains(in_type.channels())) { |
58 // Prefer not to mixdown/up. | 57 // Prefer not to mixdown/up. |
59 score += 10; | 58 score += 10; |
60 } else { | 59 } else { |
61 return 0; // TODO(dalesat): Remove when we have mixdown/up. | 60 return 0; // TODO(dalesat): Remove when we have mixdown/up. |
62 } | 61 } |
63 | 62 |
64 if (out_type_set.frames_per_second(). | 63 if (out_type_set.frames_per_second().contains(in_type.frames_per_second())) { |
65 contains(in_type.frames_per_second())) { | |
66 // Very much prefer not to resample. | 64 // Very much prefer not to resample. |
67 score += 50; | 65 score += 50; |
68 } else { | 66 } else { |
69 return 0; // TODO(dalesat): Remove when we have resamplers. | 67 return 0; // TODO(dalesat): Remove when we have resamplers. |
70 } | 68 } |
71 | 69 |
72 return score; | 70 return score; |
73 } | 71 } |
74 | 72 |
75 // Finds the media type set that best matches in_type. | 73 // Finds the media type set that best matches in_type. |
76 const std::unique_ptr<StreamTypeSet>* FindBestLpcm( | 74 const std::unique_ptr<StreamTypeSet>* FindBestLpcm( |
77 const LpcmStreamType& in_type, | 75 const LpcmStreamType& in_type, |
78 const std::vector<std::unique_ptr<StreamTypeSet>>& out_type_sets) { | 76 const std::vector<std::unique_ptr<StreamTypeSet>>& out_type_sets) { |
79 const std::unique_ptr<StreamTypeSet>* best = nullptr; | 77 const std::unique_ptr<StreamTypeSet>* best = nullptr; |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
143 // No candidates found. | 141 // No candidates found. |
144 *out_type = nullptr; | 142 *out_type = nullptr; |
145 return AddResult::kFailed; | 143 return AddResult::kFailed; |
146 } | 144 } |
147 | 145 |
148 DCHECK_EQ((*best)->scheme(), StreamType::Scheme::kLpcm); | 146 DCHECK_EQ((*best)->scheme(), StreamType::Scheme::kLpcm); |
149 | 147 |
150 // Need to decode. Create a decoder and go from there. | 148 // Need to decode. Create a decoder and go from there. |
151 std::shared_ptr<Decoder> decoder; | 149 std::shared_ptr<Decoder> decoder; |
152 Result result = Decoder::Create(in_type, &decoder); | 150 Result result = Decoder::Create(in_type, &decoder); |
153 if (result != Result::kOk) { | 151 if (result != Result::kOk) { |
154 // No decoder found. | 152 // No decoder found. |
155 *out_type = nullptr; | 153 *out_type = nullptr; |
156 return AddResult::kFailed; | 154 return AddResult::kFailed; |
157 } | 155 } |
158 | 156 |
159 *output = graph->ConnectOutputToPart(*output, graph->Add(decoder)).output(); | 157 *output = graph->ConnectOutputToPart(*output, graph->Add(decoder)).output(); |
160 *out_type = decoder->output_stream_type(); | 158 *out_type = decoder->output_stream_type(); |
161 | 159 |
162 return AddResult::kProgressed; | 160 return AddResult::kProgressed; |
163 } | 161 } |
164 | 162 |
165 // Attempts to add transforms to the pipeline given an input LPCM stream type | 163 // 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 | 164 // (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 | 165 // 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. | 166 // type. Otherwise, *out_type is set to nullptr. |
169 AddResult AddTransformsForLpcm( | 167 AddResult AddTransformsForLpcm(const LpcmStreamType& in_type, |
170 const LpcmStreamType& in_type, | 168 const LpcmStreamTypeSet& out_type_set, |
171 const LpcmStreamTypeSet& out_type_set, | 169 Graph* graph, |
172 Graph* graph, | 170 OutputRef* output, |
173 OutputRef* output, | 171 std::unique_ptr<StreamType>* out_type) { |
174 std::unique_ptr<StreamType>* out_type) { | |
175 DCHECK(graph); | 172 DCHECK(graph); |
176 DCHECK(out_type); | 173 DCHECK(out_type); |
177 | 174 |
178 // TODO(dalesat): Room for more intelligence here wrt transform ordering and | 175 // TODO(dalesat): Room for more intelligence here wrt transform ordering and |
179 // transforms that handle more than one conversion. | 176 // transforms that handle more than one conversion. |
180 if (in_type.sample_format() != out_type_set.sample_format() && | 177 if (in_type.sample_format() != out_type_set.sample_format() && |
181 out_type_set.sample_format() != LpcmStreamType::SampleFormat::kAny) { | 178 out_type_set.sample_format() != LpcmStreamType::SampleFormat::kAny) { |
182 *output = graph->ConnectOutputToPart( | 179 *output = |
183 *output, | 180 graph |
184 graph->Add(LpcmReformatter::Create(in_type, out_type_set))).output(); | 181 ->ConnectOutputToPart(*output, graph->Add(LpcmReformatter::Create( |
| 182 in_type, out_type_set))) |
| 183 .output(); |
185 } | 184 } |
186 | 185 |
187 if (!out_type_set.channels().contains(in_type.channels())) { | 186 if (!out_type_set.channels().contains(in_type.channels())) { |
188 // TODO(dalesat): Insert mixdown/up transform. | 187 // TODO(dalesat): Insert mixdown/up transform. |
189 NOTREACHED() << "conversion requires mixdown/up - not supported"; | 188 NOTREACHED() << "conversion requires mixdown/up - not supported"; |
190 *out_type = nullptr; | 189 *out_type = nullptr; |
191 return AddResult::kFailed; | 190 return AddResult::kFailed; |
192 } | 191 } |
193 | 192 |
194 if (!out_type_set.frames_per_second().contains(in_type.frames_per_second())) { | 193 if (!out_type_set.frames_per_second().contains(in_type.frames_per_second())) { |
195 // TODO(dalesat): Insert resampler. | 194 // TODO(dalesat): Insert resampler. |
196 NOTREACHED() << "conversion requires resampling - not supported"; | 195 NOTREACHED() << "conversion requires resampling - not supported"; |
197 *out_type = nullptr; | 196 *out_type = nullptr; |
198 return AddResult::kFailed; | 197 return AddResult::kFailed; |
199 } | 198 } |
200 | 199 |
201 // Build the resulting media type. | 200 // Build the resulting media type. |
202 *out_type = LpcmStreamType::Create( | 201 *out_type = LpcmStreamType::Create( |
203 out_type_set.sample_format() == LpcmStreamType::SampleFormat::kAny ? | 202 out_type_set.sample_format() == LpcmStreamType::SampleFormat::kAny |
204 in_type.sample_format() : | 203 ? in_type.sample_format() |
205 out_type_set.sample_format(), | 204 : out_type_set.sample_format(), |
206 in_type.channels(), | 205 in_type.channels(), in_type.frames_per_second()); |
207 in_type.frames_per_second()); | |
208 | 206 |
209 return AddResult::kFinished; | 207 return AddResult::kFinished; |
210 } | 208 } |
211 | 209 |
212 // Attempts to add transforms to the pipeline given an input media type with | 210 // Attempts to add transforms to the pipeline given an input media type with |
213 // scheme LPCM (in_type) and the set of output types we need to convert to | 211 // scheme LPCM (in_type) and the set of output types we need to convert to |
214 // (out_type_sets). If the call succeeds, *out_type is set to the new output | 212 // (out_type_sets). If the call succeeds, *out_type is set to the new output |
215 // type. Otherwise, *out_type is set to nullptr. | 213 // type. Otherwise, *out_type is set to nullptr. |
216 AddResult AddTransformsForLpcm( | 214 AddResult AddTransformsForLpcm( |
217 const LpcmStreamType& in_type, | 215 const LpcmStreamType& in_type, |
(...skipping 14 matching lines...) Expand all Loading... |
232 } | 230 } |
233 | 231 |
234 switch ((*best)->scheme()) { | 232 switch ((*best)->scheme()) { |
235 case StreamType::Scheme::kAnyElementary: | 233 case StreamType::Scheme::kAnyElementary: |
236 case StreamType::Scheme::kAnyAudio: | 234 case StreamType::Scheme::kAnyAudio: |
237 case StreamType::Scheme::kAny: | 235 case StreamType::Scheme::kAny: |
238 // Wildcard scheme allows any type without conversion. | 236 // Wildcard scheme allows any type without conversion. |
239 *out_type = in_type.Clone(); | 237 *out_type = in_type.Clone(); |
240 return AddResult::kFinished; | 238 return AddResult::kFinished; |
241 case StreamType::Scheme::kLpcm: | 239 case StreamType::Scheme::kLpcm: |
242 return AddTransformsForLpcm( | 240 return AddTransformsForLpcm(in_type, *(*best)->lpcm(), graph, output, |
243 in_type, | 241 out_type); |
244 *(*best)->lpcm(), | |
245 graph, | |
246 output, | |
247 out_type); | |
248 default: | 242 default: |
249 NOTREACHED() << "FindBestLpcm produced unexpected type set scheme" | 243 NOTREACHED() << "FindBestLpcm produced unexpected type set scheme" |
250 << (*best)->scheme(); | 244 << (*best)->scheme(); |
251 return AddResult::kFailed; | 245 return AddResult::kFailed; |
252 } | 246 } |
253 } | 247 } |
254 | 248 |
255 // Attempts to add transforms to the pipeline given an input media type of any | 249 // 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 | 250 // 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 | 251 // (out_type_sets). If the call succeeds, *out_type is set to the new output |
258 // type. Otherwise, *out_type is set to nullptr. | 252 // type. Otherwise, *out_type is set to nullptr. |
259 AddResult AddTransforms( | 253 AddResult AddTransforms( |
260 const StreamType& in_type, | 254 const StreamType& in_type, |
261 const std::vector<std::unique_ptr<StreamTypeSet>>& out_type_sets, | 255 const std::vector<std::unique_ptr<StreamTypeSet>>& out_type_sets, |
262 Graph* graph, | 256 Graph* graph, |
263 OutputRef* output, | 257 OutputRef* output, |
264 std::unique_ptr<StreamType>* out_type) { | 258 std::unique_ptr<StreamType>* out_type) { |
265 DCHECK(graph); | 259 DCHECK(graph); |
266 DCHECK(out_type); | 260 DCHECK(out_type); |
267 | 261 |
268 switch (in_type.scheme()) { | 262 switch (in_type.scheme()) { |
269 case StreamType::Scheme::kLpcm: | 263 case StreamType::Scheme::kLpcm: |
270 return AddTransformsForLpcm( | 264 return AddTransformsForLpcm(*in_type.lpcm(), out_type_sets, graph, output, |
271 *in_type.lpcm(), | 265 out_type); |
272 out_type_sets, | |
273 graph, | |
274 output, | |
275 out_type); | |
276 case StreamType::Scheme::kCompressedAudio: | 266 case StreamType::Scheme::kCompressedAudio: |
277 return AddTransformsForCompressedAudio( | 267 return AddTransformsForCompressedAudio( |
278 *in_type.compressed_audio(), | 268 *in_type.compressed_audio(), out_type_sets, graph, output, out_type); |
279 out_type_sets, | |
280 graph, | |
281 output, | |
282 out_type); | |
283 default: | 269 default: |
284 NOTREACHED() << "conversion not supported for scheme" | 270 NOTREACHED() << "conversion not supported for scheme" << in_type.scheme(); |
285 << in_type.scheme(); | |
286 *out_type = nullptr; | 271 *out_type = nullptr; |
287 return AddResult::kFailed; | 272 return AddResult::kFailed; |
288 } | 273 } |
289 } | 274 } |
290 | 275 |
291 } // namespace | 276 } // namespace |
292 | 277 |
293 bool BuildConversionPipeline( | 278 bool BuildConversionPipeline( |
294 const StreamType& in_type, | 279 const StreamType& in_type, |
295 const std::vector<std::unique_ptr<StreamTypeSet>>& out_type_sets, | 280 const std::vector<std::unique_ptr<StreamTypeSet>>& out_type_sets, |
296 Graph* graph, | 281 Graph* graph, |
297 OutputRef* output, | 282 OutputRef* output, |
298 std::unique_ptr<StreamType>* out_type) { | 283 std::unique_ptr<StreamType>* out_type) { |
299 DCHECK(graph); | 284 DCHECK(graph); |
300 DCHECK(output); | 285 DCHECK(output); |
301 DCHECK(out_type); | 286 DCHECK(out_type); |
302 | 287 |
303 OutputRef out = *output; | 288 OutputRef out = *output; |
304 const StreamType* type_to_convert = &in_type; | 289 const StreamType* type_to_convert = &in_type; |
305 std::unique_ptr<StreamType> converted_type; | 290 std::unique_ptr<StreamType> converted_type; |
306 while (true) { | 291 while (true) { |
307 switch (AddTransforms( | 292 switch (AddTransforms(*type_to_convert, out_type_sets, graph, &out, |
308 *type_to_convert, | 293 &converted_type)) { |
309 out_type_sets, | |
310 graph, | |
311 &out, | |
312 &converted_type)) { | |
313 case AddResult::kFailed: | 294 case AddResult::kFailed: |
314 // Failed to find a suitable conversion. Return the pipeline to its | 295 // Failed to find a suitable conversion. Return the pipeline to its |
315 // original state. | 296 // original state. |
316 graph->RemovePartsConnectedToOutput(*output); | 297 graph->RemovePartsConnectedToOutput(*output); |
317 *out_type = nullptr; | 298 *out_type = nullptr; |
318 return false; | 299 return false; |
319 case AddResult::kProgressed: | 300 case AddResult::kProgressed: |
320 // Made progress. Continue. | 301 // Made progress. Continue. |
321 break; | 302 break; |
322 case AddResult::kFinished: | 303 case AddResult::kFinished: |
323 // No further conversion required. | 304 // No further conversion required. |
324 *output = out; | 305 *output = out; |
325 *out_type = std::move(converted_type); | 306 *out_type = std::move(converted_type); |
326 return true; | 307 return true; |
327 } | 308 } |
328 | 309 |
329 type_to_convert = converted_type.get(); | 310 type_to_convert = converted_type.get(); |
330 } | 311 } |
331 } | 312 } |
332 | 313 |
333 } // namespace media | 314 } // namespace media |
334 } // namespace mojo | 315 } // namespace mojo |
OLD | NEW |