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

Side by Side Diff: services/media/framework/conversion_pipeline_builder.cc

Issue 1902183002: Motown: Change media type (stream type) representation (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Changes per review feedback. Created 4 years, 8 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
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
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
OLDNEW
« no previous file with comments | « services/media/factory_service/media_source_impl.cc ('k') | services/media/framework/formatting.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698