| 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 |