| 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(const LpcmStreamType& in_type, | 25 int Score(const AudioStreamType& in_type, |
| 26 const LpcmStreamTypeSet& out_type_set) { | 26 const AudioStreamTypeSet& out_type_set) { |
| 27 // TODO(dalesat): Plenty of room for more subtlety here. Maybe actually | 27 // TODO(dalesat): Plenty of room for more subtlety here. Maybe actually |
| 28 // measure conversion costs (cpu, quality, etc) and reflect them here. | 28 // measure conversion costs (cpu, quality, etc) and reflect them here. |
| 29 | 29 |
| 30 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. |
| 31 | 31 |
| 32 if (in_type.sample_format() == out_type_set.sample_format() || | 32 if (in_type.sample_format() == out_type_set.sample_format() || |
| 33 out_type_set.sample_format() == LpcmStreamType::SampleFormat::kAny) { | 33 out_type_set.sample_format() == AudioStreamType::SampleFormat::kAny) { |
| 34 // Prefer not to convert sample format. | 34 // Prefer not to convert sample format. |
| 35 score += 10; | 35 score += 10; |
| 36 } else { | 36 } else { |
| 37 // Prefer higher-quality formats. | 37 // Prefer higher-quality formats. |
| 38 switch (out_type_set.sample_format()) { | 38 switch (out_type_set.sample_format()) { |
| 39 case LpcmStreamType::SampleFormat::kUnsigned8: | 39 case AudioStreamType::SampleFormat::kUnsigned8: |
| 40 break; | 40 break; |
| 41 case LpcmStreamType::SampleFormat::kSigned16: | 41 case AudioStreamType::SampleFormat::kSigned16: |
| 42 score += 1; | 42 score += 1; |
| 43 break; | 43 break; |
| 44 case LpcmStreamType::SampleFormat::kSigned24In32: | 44 case AudioStreamType::SampleFormat::kSigned24In32: |
| 45 score += 2; | 45 score += 2; |
| 46 break; | 46 break; |
| 47 case LpcmStreamType::SampleFormat::kFloat: | 47 case AudioStreamType::SampleFormat::kFloat: |
| 48 score += 3; | 48 score += 3; |
| 49 break; | 49 break; |
| 50 default: | 50 default: |
| 51 NOTREACHED() << "unsupported sample format " | 51 NOTREACHED() << "unsupported sample format " |
| 52 << out_type_set.sample_format(); | 52 << out_type_set.sample_format(); |
| 53 } | 53 } |
| 54 } | 54 } |
| 55 | 55 |
| 56 if (out_type_set.channels().contains(in_type.channels())) { | 56 if (out_type_set.channels().contains(in_type.channels())) { |
| 57 // Prefer not to mixdown/up. | 57 // Prefer not to mixdown/up. |
| 58 score += 10; | 58 score += 10; |
| 59 } else { | 59 } else { |
| 60 return 0; // TODO(dalesat): Remove when we have mixdown/up. | 60 return 0; // TODO(dalesat): Remove when we have mixdown/up. |
| 61 } | 61 } |
| 62 | 62 |
| 63 if (out_type_set.frames_per_second().contains(in_type.frames_per_second())) { | 63 if (out_type_set.frames_per_second().contains(in_type.frames_per_second())) { |
| 64 // Very much prefer not to resample. | 64 // Very much prefer not to resample. |
| 65 score += 50; | 65 score += 50; |
| 66 } else { | 66 } else { |
| 67 return 0; // TODO(dalesat): Remove when we have resamplers. | 67 return 0; // TODO(dalesat): Remove when we have resamplers. |
| 68 } | 68 } |
| 69 | 69 |
| 70 return score; | 70 return score; |
| 71 } | 71 } |
| 72 | 72 |
| 73 // Finds the media type set that best matches in_type. | 73 // Finds the stream type set that best matches in_type. |
| 74 const std::unique_ptr<StreamTypeSet>* FindBestLpcm( | 74 const std::unique_ptr<StreamTypeSet>* FindBestLpcm( |
| 75 const LpcmStreamType& in_type, | 75 const AudioStreamType& in_type, |
| 76 const std::vector<std::unique_ptr<StreamTypeSet>>& out_type_sets) { | 76 const std::vector<std::unique_ptr<StreamTypeSet>>& out_type_sets) { |
| 77 const std::unique_ptr<StreamTypeSet>* best = nullptr; | 77 const std::unique_ptr<StreamTypeSet>* best = nullptr; |
| 78 int best_score = 0; | 78 int best_score = 0; |
| 79 |
| 79 for (const std::unique_ptr<StreamTypeSet>& out_type_set : out_type_sets) { | 80 for (const std::unique_ptr<StreamTypeSet>& out_type_set : out_type_sets) { |
| 80 switch (out_type_set->scheme()) { | 81 if (out_type_set->medium() == StreamType::Medium::kAudio && |
| 81 case StreamType::Scheme::kAnyElementary: | 82 out_type_set->IncludesEncoding(StreamType::kAudioEncodingLpcm)) { |
| 82 case StreamType::Scheme::kAnyAudio: | 83 int score = Score(in_type, *out_type_set->audio()); |
| 83 case StreamType::Scheme::kAny: | 84 if (best_score < score) { |
| 84 // Wildcard scheme allows any type without conversion. | 85 best_score = score; |
| 85 return &out_type_set; | 86 best = &out_type_set; |
| 86 case StreamType::Scheme::kLpcm: { | |
| 87 int score = Score(in_type, *out_type_set->lpcm()); | |
| 88 if (best_score < score) { | |
| 89 best_score = score; | |
| 90 best = &out_type_set; | |
| 91 } | |
| 92 break; | |
| 93 } | 87 } |
| 94 default: | |
| 95 break; | |
| 96 } | 88 } |
| 97 } | 89 } |
| 90 |
| 98 return best; | 91 return best; |
| 99 } | 92 } |
| 100 | 93 |
| 101 // Attempts to add transforms to the pipeline given an input compressed audio | 94 // Attempts to add transforms to the pipeline given an input compressed audio |
| 102 // stream type with (in_type) and the set of output types we need to convert to | 95 // stream type with (in_type) and the set of output types we need to convert to |
| 103 // (out_type_sets). If the call succeeds, *out_type is set to the new output | 96 // (out_type_sets). If the call succeeds, *out_type is set to the new output |
| 104 // type. Otherwise, *out_type is set to nullptr. | 97 // type. Otherwise, *out_type is set to nullptr. |
| 105 AddResult AddTransformsForCompressedAudio( | 98 AddResult AddTransformsForCompressedAudio( |
| 106 const CompressedAudioStreamType& in_type, | 99 const AudioStreamType& in_type, |
| 107 const std::vector<std::unique_ptr<StreamTypeSet>>& out_type_sets, | 100 const std::vector<std::unique_ptr<StreamTypeSet>>& out_type_sets, |
| 108 Graph* graph, | 101 Graph* graph, |
| 109 OutputRef* output, | 102 OutputRef* output, |
| 110 std::unique_ptr<StreamType>* out_type) { | 103 std::unique_ptr<StreamType>* out_type) { |
| 111 DCHECK(out_type); | 104 DCHECK(out_type); |
| 112 DCHECK(graph); | 105 DCHECK(graph); |
| 113 | 106 |
| 114 // See if we have a matching COMPRESSED_AUDIO type. | 107 // See if we have a matching audio type. |
| 115 for (const std::unique_ptr<StreamTypeSet>& out_type_set : out_type_sets) { | 108 for (const std::unique_ptr<StreamTypeSet>& out_type_set : out_type_sets) { |
| 116 switch (out_type_set->scheme()) { | 109 if (out_type_set->medium() == StreamType::Medium::kAudio) { |
| 117 case StreamType::Scheme::kAnyElementary: | 110 if (out_type_set->audio()->contains(in_type)) { |
| 118 case StreamType::Scheme::kAnyAudio: | 111 // No transform needed. |
| 119 case StreamType::Scheme::kAny: | |
| 120 // Wildcard scheme allows any type without conversion. | |
| 121 *out_type = in_type.Clone(); | 112 *out_type = in_type.Clone(); |
| 122 return AddResult::kFinished; | 113 return AddResult::kFinished; |
| 123 case StreamType::Scheme::kCompressedAudio: { | |
| 124 if (out_type_set->compressed_audio()->contains(in_type)) { | |
| 125 // No transform needed. | |
| 126 *out_type = in_type.Clone(); | |
| 127 return AddResult::kFinished; | |
| 128 } | |
| 129 break; | |
| 130 } | 114 } |
| 131 default: | 115 // TODO(dalesat): Support a different compressed output type by |
| 132 break; | 116 // transcoding. |
| 133 } | 117 } |
| 134 // TODO(dalesat): Support a different compressed output type by transcoding. | |
| 135 } | 118 } |
| 136 | 119 |
| 137 // Find the best LPCM output type. | 120 // Find the best LPCM output type. |
| 138 const std::unique_ptr<StreamTypeSet>* best = | 121 const std::unique_ptr<StreamTypeSet>* best = |
| 139 FindBestLpcm(in_type, out_type_sets); | 122 FindBestLpcm(in_type, out_type_sets); |
| 140 if (best == nullptr) { | 123 if (best == nullptr) { |
| 141 // No candidates found. | 124 // No candidates found. |
| 142 *out_type = nullptr; | 125 *out_type = nullptr; |
| 143 return AddResult::kFailed; | 126 return AddResult::kFailed; |
| 144 } | 127 } |
| 145 | 128 |
| 146 DCHECK_EQ((*best)->scheme(), StreamType::Scheme::kLpcm); | 129 DCHECK_EQ((*best)->medium(), StreamType::Medium::kAudio); |
| 130 DCHECK((*best)->IncludesEncoding(StreamType::kAudioEncodingLpcm)); |
| 147 | 131 |
| 148 // Need to decode. Create a decoder and go from there. | 132 // Need to decode. Create a decoder and go from there. |
| 149 std::shared_ptr<Decoder> decoder; | 133 std::shared_ptr<Decoder> decoder; |
| 150 Result result = Decoder::Create(in_type, &decoder); | 134 Result result = Decoder::Create(in_type, &decoder); |
| 151 if (result != Result::kOk) { | 135 if (result != Result::kOk) { |
| 152 // No decoder found. | 136 // No decoder found. |
| 153 *out_type = nullptr; | 137 *out_type = nullptr; |
| 154 return AddResult::kFailed; | 138 return AddResult::kFailed; |
| 155 } | 139 } |
| 156 | 140 |
| 157 *output = graph->ConnectOutputToPart(*output, graph->Add(decoder)).output(); | 141 *output = graph->ConnectOutputToPart(*output, graph->Add(decoder)).output(); |
| 158 *out_type = decoder->output_stream_type(); | 142 *out_type = decoder->output_stream_type(); |
| 159 | 143 |
| 160 return AddResult::kProgressed; | 144 return AddResult::kProgressed; |
| 161 } | 145 } |
| 162 | 146 |
| 163 // Attempts to add transforms to the pipeline given an input LPCM stream type | 147 // Attempts to add transforms to the pipeline given an input LPCM stream type |
| 164 // (in_type) and the output lpcm stream type set for the type we need to convert | 148 // (in_type) and the output lpcm stream type set for the type we need to |
| 165 // to (out_type_set). If the call succeeds, *out_type is set to the new output | 149 // convert to (out_type_set). If the call succeeds, *out_type is set to the new |
| 166 // type. Otherwise, *out_type is set to nullptr. | 150 // output type. Otherwise, *out_type is set to nullptr. |
| 167 AddResult AddTransformsForLpcm(const LpcmStreamType& in_type, | 151 AddResult AddTransformsForLpcm(const AudioStreamType& in_type, |
| 168 const LpcmStreamTypeSet& out_type_set, | 152 const AudioStreamTypeSet& out_type_set, |
| 169 Graph* graph, | 153 Graph* graph, |
| 170 OutputRef* output, | 154 OutputRef* output, |
| 171 std::unique_ptr<StreamType>* out_type) { | 155 std::unique_ptr<StreamType>* out_type) { |
| 172 DCHECK(graph); | 156 DCHECK(graph); |
| 173 DCHECK(out_type); | 157 DCHECK(out_type); |
| 174 | 158 |
| 175 // TODO(dalesat): Room for more intelligence here wrt transform ordering and | 159 // TODO(dalesat): Room for more intelligence here wrt transform ordering and |
| 176 // transforms that handle more than one conversion. | 160 // transforms that handle more than one conversion. |
| 177 if (in_type.sample_format() != out_type_set.sample_format() && | 161 if (in_type.sample_format() != out_type_set.sample_format() && |
| 178 out_type_set.sample_format() != LpcmStreamType::SampleFormat::kAny) { | 162 out_type_set.sample_format() != AudioStreamType::SampleFormat::kAny) { |
| 179 *output = | 163 *output = |
| 180 graph | 164 graph |
| 181 ->ConnectOutputToPart(*output, graph->Add(LpcmReformatter::Create( | 165 ->ConnectOutputToPart(*output, graph->Add(LpcmReformatter::Create( |
| 182 in_type, out_type_set))) | 166 in_type, out_type_set))) |
| 183 .output(); | 167 .output(); |
| 184 } | 168 } |
| 185 | 169 |
| 186 if (!out_type_set.channels().contains(in_type.channels())) { | 170 if (!out_type_set.channels().contains(in_type.channels())) { |
| 187 // TODO(dalesat): Insert mixdown/up transform. | 171 // TODO(dalesat): Insert mixdown/up transform. |
| 188 NOTREACHED() << "conversion requires mixdown/up - not supported"; | 172 NOTREACHED() << "conversion requires mixdown/up - not supported"; |
| 189 *out_type = nullptr; | 173 *out_type = nullptr; |
| 190 return AddResult::kFailed; | 174 return AddResult::kFailed; |
| 191 } | 175 } |
| 192 | 176 |
| 193 if (!out_type_set.frames_per_second().contains(in_type.frames_per_second())) { | 177 if (!out_type_set.frames_per_second().contains(in_type.frames_per_second())) { |
| 194 // TODO(dalesat): Insert resampler. | 178 // TODO(dalesat): Insert resampler. |
| 195 NOTREACHED() << "conversion requires resampling - not supported"; | 179 NOTREACHED() << "conversion requires resampling - not supported"; |
| 196 *out_type = nullptr; | 180 *out_type = nullptr; |
| 197 return AddResult::kFailed; | 181 return AddResult::kFailed; |
| 198 } | 182 } |
| 199 | 183 |
| 200 // Build the resulting media type. | 184 // Build the resulting media type. |
| 201 *out_type = LpcmStreamType::Create( | 185 *out_type = AudioStreamType::Create( |
| 202 out_type_set.sample_format() == LpcmStreamType::SampleFormat::kAny | 186 StreamType::kAudioEncodingLpcm, nullptr, |
| 187 out_type_set.sample_format() == AudioStreamType::SampleFormat::kAny |
| 203 ? in_type.sample_format() | 188 ? in_type.sample_format() |
| 204 : out_type_set.sample_format(), | 189 : out_type_set.sample_format(), |
| 205 in_type.channels(), in_type.frames_per_second()); | 190 in_type.channels(), in_type.frames_per_second()); |
| 206 | 191 |
| 207 return AddResult::kFinished; | 192 return AddResult::kFinished; |
| 208 } | 193 } |
| 209 | 194 |
| 210 // Attempts to add transforms to the pipeline given an input media type with | 195 // Attempts to add transforms to the pipeline given an input audio stream type |
| 211 // scheme LPCM (in_type) and the set of output types we need to convert to | 196 // witn lpcm encoding (in_type) and the set of output types we need to convert |
| 212 // (out_type_sets). If the call succeeds, *out_type is set to the new output | 197 // to (out_type_sets). If the call succeeds, *out_type is set to the new |
| 213 // type. Otherwise, *out_type is set to nullptr. | 198 // output type. Otherwise, *out_type is set to nullptr. |
| 214 AddResult AddTransformsForLpcm( | 199 AddResult AddTransformsForLpcm( |
| 215 const LpcmStreamType& in_type, | 200 const AudioStreamType& in_type, |
| 216 const std::vector<std::unique_ptr<StreamTypeSet>>& out_type_sets, | 201 const std::vector<std::unique_ptr<StreamTypeSet>>& out_type_sets, |
| 217 Graph* graph, | 202 Graph* graph, |
| 218 OutputRef* output, | 203 OutputRef* output, |
| 219 std::unique_ptr<StreamType>* out_type) { | 204 std::unique_ptr<StreamType>* out_type) { |
| 220 DCHECK(graph); | 205 DCHECK(graph); |
| 221 DCHECK(out_type); | 206 DCHECK(out_type); |
| 222 | 207 |
| 223 const std::unique_ptr<StreamTypeSet>* best = | 208 const std::unique_ptr<StreamTypeSet>* best = |
| 224 FindBestLpcm(in_type, out_type_sets); | 209 FindBestLpcm(in_type, out_type_sets); |
| 225 if (best == nullptr) { | 210 if (best == nullptr) { |
| 226 // TODO(dalesat): Support a compressed output type by encoding. | 211 // TODO(dalesat): Support a compressed output type by encoding. |
| 227 NOTREACHED() << "conversion using encoder not supported"; | 212 NOTREACHED() << "conversion using encoder not supported"; |
| 228 *out_type = nullptr; | 213 *out_type = nullptr; |
| 229 return AddResult::kFailed; | 214 return AddResult::kFailed; |
| 230 } | 215 } |
| 231 | 216 |
| 232 switch ((*best)->scheme()) { | 217 DCHECK_EQ((*best)->medium(), StreamType::Medium::kAudio); |
| 233 case StreamType::Scheme::kAnyElementary: | 218 |
| 234 case StreamType::Scheme::kAnyAudio: | 219 return AddTransformsForLpcm(in_type, *(*best)->audio(), graph, output, |
| 235 case StreamType::Scheme::kAny: | 220 out_type); |
| 236 // Wildcard scheme allows any type without conversion. | |
| 237 *out_type = in_type.Clone(); | |
| 238 return AddResult::kFinished; | |
| 239 case StreamType::Scheme::kLpcm: | |
| 240 return AddTransformsForLpcm(in_type, *(*best)->lpcm(), graph, output, | |
| 241 out_type); | |
| 242 default: | |
| 243 NOTREACHED() << "FindBestLpcm produced unexpected type set scheme" | |
| 244 << (*best)->scheme(); | |
| 245 return AddResult::kFailed; | |
| 246 } | |
| 247 } | 221 } |
| 248 | 222 |
| 249 // Attempts to add transforms to the pipeline given an input media type of any | 223 // Attempts to add transforms to the pipeline given an input media type of any |
| 250 // scheme (in_type) and the set of output types we need to convert to | 224 // medium and encoding (in_type) and the set of output types we need to |
| 251 // (out_type_sets). If the call succeeds, *out_type is set to the new output | 225 // convert to (out_type_sets). If the call succeeds, *out_type is set to the new |
| 252 // type. Otherwise, *out_type is set to nullptr. | 226 // output type. Otherwise, *out_type is set to nullptr. |
| 253 AddResult AddTransforms( | 227 AddResult AddTransforms( |
| 254 const StreamType& in_type, | 228 const StreamType& in_type, |
| 255 const std::vector<std::unique_ptr<StreamTypeSet>>& out_type_sets, | 229 const std::vector<std::unique_ptr<StreamTypeSet>>& out_type_sets, |
| 256 Graph* graph, | 230 Graph* graph, |
| 257 OutputRef* output, | 231 OutputRef* output, |
| 258 std::unique_ptr<StreamType>* out_type) { | 232 std::unique_ptr<StreamType>* out_type) { |
| 259 DCHECK(graph); | 233 DCHECK(graph); |
| 260 DCHECK(out_type); | 234 DCHECK(out_type); |
| 261 | 235 |
| 262 switch (in_type.scheme()) { | 236 switch (in_type.medium()) { |
| 263 case StreamType::Scheme::kLpcm: | 237 case StreamType::Medium::kAudio: |
| 264 return AddTransformsForLpcm(*in_type.lpcm(), out_type_sets, graph, output, | 238 if (in_type.encoding() == StreamType::kAudioEncodingLpcm) { |
| 265 out_type); | 239 return AddTransformsForLpcm(*in_type.audio(), out_type_sets, graph, |
| 266 case StreamType::Scheme::kCompressedAudio: | 240 output, out_type); |
| 267 return AddTransformsForCompressedAudio( | 241 } else { |
| 268 *in_type.compressed_audio(), out_type_sets, graph, output, out_type); | 242 return AddTransformsForCompressedAudio(*in_type.audio(), out_type_sets, |
| 243 graph, output, out_type); |
| 244 } |
| 269 default: | 245 default: |
| 270 NOTREACHED() << "conversion not supported for scheme" << in_type.scheme(); | 246 NOTREACHED() << "conversion not supported for medium" << in_type.medium(); |
| 271 *out_type = nullptr; | 247 *out_type = nullptr; |
| 272 return AddResult::kFailed; | 248 return AddResult::kFailed; |
| 273 } | 249 } |
| 274 } | 250 } |
| 275 | 251 |
| 276 } // namespace | 252 } // namespace |
| 277 | 253 |
| 278 bool BuildConversionPipeline( | 254 bool BuildConversionPipeline( |
| 279 const StreamType& in_type, | 255 const StreamType& in_type, |
| 280 const std::vector<std::unique_ptr<StreamTypeSet>>& out_type_sets, | 256 const std::vector<std::unique_ptr<StreamTypeSet>>& out_type_sets, |
| (...skipping 25 matching lines...) Expand all Loading... |
| 306 *out_type = std::move(converted_type); | 282 *out_type = std::move(converted_type); |
| 307 return true; | 283 return true; |
| 308 } | 284 } |
| 309 | 285 |
| 310 type_to_convert = converted_type.get(); | 286 type_to_convert = converted_type.get(); |
| 311 } | 287 } |
| 312 } | 288 } |
| 313 | 289 |
| 314 } // namespace media | 290 } // namespace media |
| 315 } // namespace mojo | 291 } // namespace mojo |
| OLD | NEW |