Skip to content

Commit

Permalink
WebCodecs: define DecoderSelector
Browse files Browse the repository at this point in the history
This wraps media::DecoderSelector in more friendly (simplified)
interface for WebCodecs.

Re-using the selector is desirable because WebCodecs has common
requirements:
- walk a list of decoders to find the most suitable for
  a given config.
- do so in generic way for audio and video stream types
- (eventually) new smarts around decoder  selection (e.g.
  fallback/fallforward on resolution changes).

The wrapping eliminates (or hides) a few things WebCodecs doesn't need
- demuxer stream
- encryption
- decoder selection finalization

Bug: 1045247
Change-Id: I37fa629a243bb717bd2413d2bfd97e29f77eeebc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2150161
Reviewed-by: Dan Sanders <sandersd@chromium.org>
Commit-Queue: Chrome Cunningham <chcunningham@chromium.org>
Cr-Commit-Position: refs/heads/master@{#762167}
  • Loading branch information
chcunningham authored and Commit Bot committed Apr 23, 2020
1 parent c501c95 commit 419f14d
Show file tree
Hide file tree
Showing 7 changed files with 506 additions and 0 deletions.
1 change: 1 addition & 0 deletions media/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ test("media_unittests") {
"//media/test:pipeline_integration_tests",
"//media/test:run_all_unittests",
"//media/video:unit_tests",
"//media/webcodecs:unit_tests",
"//media/webrtc:unit_tests",
]

Expand Down
1 change: 1 addition & 0 deletions media/filters/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ jumbo_source_set("filters") {
visibility = [
"//media",
"//media/renderers",
"//media/webcodecs",
]

sources = [
Expand Down
1 change: 1 addition & 0 deletions media/media_options.gni
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ media_subcomponent_deps = [
"//media/muxers",
"//media/renderers",
"//media/video",
"//media/webcodecs",
]

if (is_fuchsia) {
Expand Down
39 changes: 39 additions & 0 deletions media/webcodecs/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright 2020 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.

import("//testing/test.gni")

source_set("webcodecs") {
# Do not expand the visibility here without double-checking with OWNERS, this
# is a roll-up target which is part of the //media component. Most other DEPs
# should be using //media and not directly DEP this roll-up target.
visibility = [ "//media" ]

sources = [
"wc_decoder_selector.cc",
"wc_decoder_selector.h",
]

public_deps = [
"//base",
"//media/base",
"//media/filters",
]

deps = []

configs += [ "//media:subcomponent_config" ]
}

source_set("unit_tests") {
testonly = true
sources = [ "wc_decoder_selector_unittest.cc" ]

deps = [
"//base/test:test_support",
"//media:test_support",
"//testing/gmock",
"//testing/gtest",
]
}
141 changes: 141 additions & 0 deletions media/webcodecs/wc_decoder_selector.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Copyright 2020 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 "media/webcodecs/wc_decoder_selector.h"

#include "base/bind.h"
#include "base/logging.h"
#include "base/single_thread_task_runner.h"
#include "media/base/channel_layout.h"
#include "media/base/demuxer_stream.h"
#include "media/filters/decrypting_demuxer_stream.h"

namespace media {

// Demuxing isn't part of WebCodecs. This shim allows us to reuse decoder
// selection logic from <video>.
// TODO(chcunningham): Maybe refactor DecoderSelector to separate dependency on
// DemuxerStream. DecoderSelection doesn't conceptually require a Demuxer. The
// tough part is re-working DecryptingDemuxerStream.
template <DemuxerStream::Type StreamType>
class ShimDemuxerStream : public DemuxerStream {
public:
using DecoderConfigType =
typename DecoderStreamTraits<StreamType>::DecoderConfigType;

~ShimDemuxerStream() override = default;

void Read(ReadCB read_cb) override { NOTREACHED(); }
bool IsReadPending() const override {
NOTREACHED();
return false;
}

void Configure(DecoderConfigType config);

AudioDecoderConfig audio_decoder_config() override {
DCHECK_EQ(type(), DemuxerStream::AUDIO);
return audio_decoder_config_;
}

VideoDecoderConfig video_decoder_config() override {
DCHECK_EQ(type(), DemuxerStream::VIDEO);
return video_decoder_config_;
}

Type type() const override { return stream_type; }

bool SupportsConfigChanges() override {
NOTREACHED();
return true;
}

private:
static const DemuxerStream::Type stream_type = StreamType;

AudioDecoderConfig audio_decoder_config_;
VideoDecoderConfig video_decoder_config_;
};

template <>
void ShimDemuxerStream<DemuxerStream::AUDIO>::Configure(
DecoderConfigType config) {
audio_decoder_config_ = config;
}

template <>
void ShimDemuxerStream<DemuxerStream::VIDEO>::Configure(
DecoderConfigType config) {
video_decoder_config_ = config;
}

template <DemuxerStream::Type StreamType>
WebCodecsDecoderSelector<StreamType>::WebCodecsDecoderSelector(
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
CreateDecodersCB create_decoders_cb,
typename Decoder::OutputCB output_cb)
: impl_(std::move(task_runner),
std::move(create_decoders_cb),
&null_media_log_),
demuxer_stream_(new ShimDemuxerStream<StreamType>()),
stream_traits_(CreateStreamTraits()),
output_cb_(output_cb) {
impl_.Initialize(stream_traits_.get(), demuxer_stream_.get(),
nullptr /*CdmContext*/, WaitingCB());
}

template <DemuxerStream::Type StreamType>
WebCodecsDecoderSelector<StreamType>::~WebCodecsDecoderSelector() {}

template <DemuxerStream::Type StreamType>
void WebCodecsDecoderSelector<StreamType>::SelectDecoder(
const DecoderConfig& config,
SelectDecoderCB select_decoder_cb) {
// |impl_| will internally use this the |config| from our ShimDemuxerStream.
demuxer_stream_->Configure(config);

// |impl_| uses a WeakFactory for its SelectDecoderCB, so we're safe to use
// Unretained here.
impl_.SelectDecoder(
base::BindOnce(&WebCodecsDecoderSelector<StreamType>::OnDecoderSelected,
base::Unretained(this), std::move(select_decoder_cb)),
output_cb_);
}

template <>
std::unique_ptr<WebCodecsAudioDecoderSelector::StreamTraits>
WebCodecsDecoderSelector<DemuxerStream::AUDIO>::CreateStreamTraits() {
// TODO(chcunningham): Consider plumbing real hw channel layout.
return std::make_unique<WebCodecsDecoderSelector::StreamTraits>(
&null_media_log_, CHANNEL_LAYOUT_NONE);
}

template <>
std::unique_ptr<WebCodecsVideoDecoderSelector::StreamTraits>
WebCodecsDecoderSelector<DemuxerStream::VIDEO>::CreateStreamTraits() {
return std::make_unique<WebCodecsDecoderSelector::StreamTraits>(
&null_media_log_);
}

template <DemuxerStream::Type StreamType>
void WebCodecsDecoderSelector<StreamType>::OnDecoderSelected(
SelectDecoderCB select_decoder_cb,
std::unique_ptr<Decoder> decoder,
std::unique_ptr<DecryptingDemuxerStream> decrypting_demuxer_stream) {
DCHECK(!decrypting_demuxer_stream);

// We immediately finalize decoder selection. From a spec POV we strongly
// prefer to avoid replicating our internal design of having to wait for the
// first frame to arrive before we consider configuration successful.
// TODO(chcunningham): Measure first frame decode failures and find other ways
// to solve (or minimize) the problem.
impl_.FinalizeDecoderSelection();

std::move(select_decoder_cb).Run(std::move(decoder));
}

template class WebCodecsDecoderSelector<DemuxerStream::VIDEO>;
template class WebCodecsDecoderSelector<DemuxerStream::AUDIO>;

} // namespace media
83 changes: 83 additions & 0 deletions media/webcodecs/wc_decoder_selector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright 2020 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.

#ifndef MEDIA_WEBCODECS_WC_DECODER_SELECTOR_H_
#define MEDIA_WEBCODECS_WC_DECODER_SELECTOR_H_

#include <memory>

#include "media/base/demuxer_stream.h"
#include "media/base/media_export.h"
#include "media/base/media_util.h"
#include "media/filters/decoder_selector.h"
#include "media/filters/decoder_stream_traits.h"

namespace media {

template <DemuxerStream::Type StreamType>
class ShimDemuxerStream;

template <DemuxerStream::Type StreamType>
class MEDIA_EXPORT WebCodecsDecoderSelector {
public:
typedef DecoderStreamTraits<StreamType> StreamTraits;
typedef typename StreamTraits::DecoderType Decoder;
typedef typename StreamTraits::DecoderConfigType DecoderConfig;

// Callback to create a list of decoders to select from.
using CreateDecodersCB =
base::RepeatingCallback<std::vector<std::unique_ptr<Decoder>>()>;

// Emits the result of a single call to SelectDecoder(). Parameter is
// the initialized Decoder. nullptr if selection failed. The caller owns the
// Decoder.
using SelectDecoderCB = base::OnceCallback<void(std::unique_ptr<Decoder>)>;

WebCodecsDecoderSelector(
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
CreateDecodersCB create_decoders_cb,
typename Decoder::OutputCB output_cb);

// Aborts any pending decoder selection.
~WebCodecsDecoderSelector();

// Selects and initializes a decoder using |config|. Decoder will
// be returned via |select_decoder_cb| posted to |task_runner_|. Subsequent
// calls will again select from the full list of decoders.
void SelectDecoder(const DecoderConfig& config,
SelectDecoderCB select_decoder_cb);

private:
// Helper to create |stream_traits_|.
std::unique_ptr<StreamTraits> CreateStreamTraits();

// Proxy SelectDecoderCB from impl_ to our |select_decoder_cb|.
void OnDecoderSelected(SelectDecoderCB select_decoder_cb,
std::unique_ptr<Decoder> decoder,
std::unique_ptr<DecryptingDemuxerStream>);

// Implements heavy lifting for decoder selection.
DecoderSelector<StreamType> impl_;

// Shim to satisfy dependencies of |impl_|. Provides DecoderConfig to |impl_|.
std::unique_ptr<ShimDemuxerStream<StreamType>> demuxer_stream_;

// Helper to unify API for configuring audio/video decoders.
std::unique_ptr<StreamTraits> stream_traits_;

// Repeating callback for decoder outputs.
typename Decoder::OutputCB output_cb_;

// TODO(chcunningham): Route MEDIA_LOG for WebCodecs.
NullMediaLog null_media_log_;
};

typedef WebCodecsDecoderSelector<DemuxerStream::VIDEO>
WebCodecsVideoDecoderSelector;
typedef WebCodecsDecoderSelector<DemuxerStream::AUDIO>
WebCodecsAudioDecoderSelector;

} // namespace media

#endif // MEDIA_WEBCODECS_WC_DECODER_SELECTOR_H_
Loading

0 comments on commit 419f14d

Please sign in to comment.