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

Unified 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, 11 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « services/media/framework/conversion_pipeline_builder.h ('k') | services/media/framework/engine.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: services/media/framework/conversion_pipeline_builder.cc
diff --git a/services/media/framework/conversion_pipeline_builder.cc b/services/media/framework/conversion_pipeline_builder.cc
new file mode 100644
index 0000000000000000000000000000000000000000..c1af544dca40776599ec224f897068e9720c1b64
--- /dev/null
+++ b/services/media/framework/conversion_pipeline_builder.cc
@@ -0,0 +1,341 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/media/framework/conversion_pipeline_builder.h"
+#include "services/media/framework/formatting.h"
+#include "services/media/framework/parts/decoder.h"
+#include "services/media/framework/parts/lpcm_reformatter.h"
+
+namespace mojo {
+namespace media {
+
+namespace {
+
+enum class AddResult {
+ kFailed, // Can't convert.
+ kProgressed, // Added a conversion transform.
+ kFinished // Done adding conversion transforms.
+};
+
+// Produces a score for in_type with respect to out_type_set. The score
+// is used to compare type sets to see which represents the best goal for
+// conversion. Higher scores are preferred. A score of zero indicates that
+// in_type is incompatible with out_type_set.
+int Score(
+ const LpcmStreamType& in_type,
+ const LpcmStreamTypeSet& out_type_set) {
+ // TODO(dalesat): Plenty of room for more subtlety here. Maybe actually
+ // measure conversion costs (cpu, quality, etc) and reflect them here.
+
+ int score = 1; // We can convert anything, so 1 is the minimum score.
+
+ if (in_type.sample_format() == out_type_set.sample_format() ||
+ out_type_set.sample_format() == LpcmStreamType::SampleFormat::kAny) {
+ // Prefer not to convert sample format.
+ score += 10;
+ } else {
+ // Prefer higher-quality formats.
+ switch (out_type_set.sample_format()) {
+ case LpcmStreamType::SampleFormat::kUnsigned8:
+ break;
+ case LpcmStreamType::SampleFormat::kSigned16:
+ score += 1;
+ break;
+ case LpcmStreamType::SampleFormat::kSigned24In32:
+ score += 2;
+ break;
+ case LpcmStreamType::SampleFormat::kFloat:
+ score += 3;
+ break;
+ default:
+ NOTREACHED() << "unsupported sample format "
+ << out_type_set.sample_format();
+ }
+ }
+
+ if (out_type_set.channels().contains(in_type.channels())) {
+ // Prefer not to mixdown/up.
+ score += 10;
+ } else {
+ return 0; // TODO(dalesat): Remove when we have mixdown/up.
+ }
+
+ if (out_type_set.frames_per_second().
+ contains(in_type.frames_per_second())) {
+ // Very much prefer not to resample.
+ score += 50;
+ } else {
+ return 0; // TODO(dalesat): Remove when we have resamplers.
+ }
+
+ return score;
+}
+
+// Finds the media type set that best matches in_type.
+const StreamTypeSetPtr* FindBestLpcm(
+ const LpcmStreamType& in_type,
+ const StreamTypeSetsPtr& out_type_sets) {
+ const StreamTypeSetPtr* best = nullptr;
+ int best_score = 0;
+ for (const StreamTypeSetPtr& out_type_set : *out_type_sets) {
+ switch (out_type_set->scheme()) {
+ case StreamType::Scheme::kAnyElementary:
+ case StreamType::Scheme::kAnyAudio:
+ case StreamType::Scheme::kAny:
+ // Wildcard scheme allows any type without conversion.
+ return &out_type_set;
+ case StreamType::Scheme::kLpcm: {
+ int score = Score(in_type, *out_type_set->lpcm());
+ if (best_score < score) {
+ best_score = score;
+ best = &out_type_set;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return best;
+}
+
+// Attempts to add transforms to the pipeline given an input compressed audio
+// stream type with (in_type) and the set of output types we need to convert to
+// (out_type_sets). If the call succeeds, *out_type is set to the new output
+// type. Otherwise, *out_type is set to nullptr.
+AddResult AddTransformsForCompressedAudio(
+ const CompressedAudioStreamType& in_type,
+ const StreamTypePtr& in_type_ptr,
+ const StreamTypeSetsPtr& out_type_sets,
+ Engine* engine,
+ Engine::Output* output,
+ StreamTypePtr* out_type) {
+ DCHECK(out_type);
+ DCHECK(engine);
+
+ // See if we have a matching COMPRESSED_AUDIO type.
+ for (const StreamTypeSetPtr& out_type_set : *out_type_sets) {
+ switch (out_type_set->scheme()) {
+ case StreamType::Scheme::kAnyElementary:
+ case StreamType::Scheme::kAnyAudio:
+ case StreamType::Scheme::kAny:
+ // Wildcard scheme allows any type without conversion.
+ *out_type = in_type.Clone();
+ return AddResult::kFinished;
+ case StreamType::Scheme::kCompressedAudio: {
+ if (out_type_set->compressed_audio()->contains(in_type)) {
+ // No transform needed.
+ *out_type = in_type.Clone();
+ return AddResult::kFinished;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ // TODO(dalesat): Support a different compressed output type by transcoding.
+ }
+
+ // Find the best LPCM output type.
+ const StreamTypeSetPtr* best = FindBestLpcm(in_type, out_type_sets);
+ if (best == nullptr) {
+ // No candidates found.
+ *out_type = nullptr;
+ return AddResult::kFailed;
+ }
+
+ DCHECK_EQ((*best)->scheme(), StreamType::Scheme::kLpcm);
+
+ // Need to decode. Create a decoder and go from there.
+ DecoderPtr decoder;
+ Result result = Decoder::Create(in_type_ptr, &decoder);
+ if (result != Result::kOk) {
+ // No decoder found.
+ *out_type = nullptr;
+ return AddResult::kFailed;
+ }
+
+ *output = engine->ConnectOutputToPart(*output, engine->Add(decoder)).output();
+ *out_type = decoder->output_stream_type();
+
+ return AddResult::kProgressed;
+}
+
+// Attempts to add transforms to the pipeline given an input LPCM stream type
+// (in_type) and the output lpcm stream type set for the type we need to convert
+// to (out_type_set). If the call succeeds, *out_type is set to the new output
+// type. Otherwise, *out_type is set to nullptr.
+AddResult AddTransformsForLpcm(
+ const LpcmStreamType& in_type,
+ const LpcmStreamTypeSet& out_type_set,
+ Engine* engine,
+ Engine::Output* output,
+ StreamTypePtr* out_type) {
+ DCHECK(engine);
+ DCHECK(out_type);
+
+ // TODO(dalesat): Room for more intelligence here wrt transform ordering and
+ // transforms that handle more than one conversion.
+ if (in_type.sample_format() != out_type_set.sample_format() &&
+ out_type_set.sample_format() != LpcmStreamType::SampleFormat::kAny) {
+ // The reformatter will fix interleave conversion.
+ *output = engine->ConnectOutputToPart(
+ *output,
+ engine->Add(LpcmReformatter::Create(in_type, out_type_set))).output();
+ }
+
+ if (!out_type_set.channels().contains(in_type.channels())) {
+ // TODO(dalesat): Insert mixdown/up transform.
+ NOTREACHED() << "conversion requires mixdown/up - not supported";
+ *out_type = nullptr;
+ return AddResult::kFailed;
+ }
+
+ if (!out_type_set.frames_per_second().contains(in_type.frames_per_second())) {
+ // TODO(dalesat): Insert resampler.
+ NOTREACHED() << "conversion requires resampling - not supported";
+ *out_type = nullptr;
+ return AddResult::kFailed;
+ }
+
+ // Build the resulting media type.
+ *out_type = LpcmStreamType::Create(
+ out_type_set.sample_format() == LpcmStreamType::SampleFormat::kAny ?
+ in_type.sample_format() :
+ out_type_set.sample_format(),
+ in_type.channels(),
+ in_type.frames_per_second());
+
+ return AddResult::kFinished;
+}
+
+// Attempts to add transforms to the pipeline given an input media type with
+// scheme LPCM (in_type) and the set of output types we need to convert to
+// (out_type_sets). If the call succeeds, *out_type is set to the new output
+// type. Otherwise, *out_type is set to nullptr.
+AddResult AddTransformsForLpcm(
+ const LpcmStreamType& in_type,
+ const StreamTypeSetsPtr& out_type_sets,
+ Engine* engine,
+ Engine::Output* output,
+ StreamTypePtr* out_type) {
+ DCHECK(engine);
+ DCHECK(out_type);
+
+ const StreamTypeSetPtr* best = FindBestLpcm(in_type, out_type_sets);
+ if (best == nullptr) {
+ // TODO(dalesat): Support a compressed output type by encoding.
+ NOTREACHED() << "conversion using encoder not supported";
+ *out_type = nullptr;
+ return AddResult::kFailed;
+ }
+
+ switch ((*best)->scheme()) {
+ case StreamType::Scheme::kAnyElementary:
+ case StreamType::Scheme::kAnyAudio:
+ case StreamType::Scheme::kAny:
+ // Wildcard scheme allows any type without conversion.
+ *out_type = in_type.Clone();
+ return AddResult::kFinished;
+ case StreamType::Scheme::kLpcm:
+ return AddTransformsForLpcm(
+ in_type,
+ *(*best)->lpcm(),
+ engine,
+ output,
+ out_type);
+ default:
+ NOTREACHED() << "FindBestLpcm produced unexpected type set scheme"
+ << (*best)->scheme();
+ return AddResult::kFailed;
+ }
+}
+
+// Attempts to add transforms to the pipeline given an input media type of any
+// scheme (in_type) and the set of output types we need to convert to
+// (out_type_sets). If the call succeeds, *out_type is set to the new output
+// type. Otherwise, *out_type is set to nullptr.
+AddResult AddTransforms(
+ const StreamTypePtr& in_type,
+ const StreamTypeSetsPtr& out_type_sets,
+ Engine* engine,
+ Engine::Output* output,
+ StreamTypePtr* out_type) {
+ DCHECK(in_type);
+ DCHECK(engine);
+ DCHECK(out_type);
+
+ switch (in_type->scheme()) {
+ case StreamType::Scheme::kLpcm:
+ return AddTransformsForLpcm(
+ *in_type->lpcm(),
+ out_type_sets,
+ engine,
+ output,
+ out_type);
+ case StreamType::Scheme::kCompressedAudio:
+ return AddTransformsForCompressedAudio(
+ *in_type->compressed_audio(),
+ in_type,
+ out_type_sets,
+ engine,
+ output,
+ out_type);
+ default:
+ NOTREACHED() << "conversion not supported for scheme"
+ << in_type->scheme();
+ *out_type = nullptr;
+ return AddResult::kFailed;
+ }
+}
+
+} // namespace
+
+bool BuildConversionPipeline(
+ const StreamTypePtr& in_type,
+ const StreamTypeSetsPtr& out_type_sets,
+ Engine* engine,
+ Engine::Output* output,
+ StreamTypePtr* out_type) {
+ DCHECK(in_type);
+ DCHECK(out_type_sets);
+ DCHECK(engine);
+ DCHECK(output);
+ DCHECK(out_type);
+
+ Engine::Output out = *output;
+
+ const StreamTypePtr* type_to_convert = &in_type;
+ StreamTypePtr next_in_type;
+ while (true) {
+ StreamTypePtr converted_type;
+ switch (AddTransforms(
+ *type_to_convert,
+ out_type_sets,
+ engine,
+ &out,
+ &converted_type)) {
+ case AddResult::kFailed:
+ // Failed to find a suitable conversion. Return the pipeline to its
+ // original state.
+ engine->RemovePartsConnectedToOutput(*output);
+ *out_type = nullptr;
+ return false;
+ case AddResult::kProgressed:
+ // Made progress. Continue.
+ break;
+ case AddResult::kFinished:
+ // No further conversion required.
+ *output = out;
+ *out_type = std::move(converted_type);
+ return true;
+ }
+
+ next_in_type = std::move(converted_type);
+ type_to_convert = &next_in_type;
+ }
+}
+
+} // namespace media
+} // namespace mojo
« 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