[go: nahoru, domu]

Add ClearKey support with MojoRenderer

ClearKey is software based and requires that the process it runs in
(browser) have access to the content that is being decrypted and
decoded. Since ClearKey communication is accomplished via JSON we would
need to use a JSON parser in an elevated browser process with direct
access to the hardware. This is not acceptable and has been addressed
in this change, which ensures that this decryption and decoding is
always handled in the render process. This change can be broken into two
primary pieces: the DecryptingRenderer and the DecryptingMediaResource.

The DecryptingMediaResource wraps an existing MediaResource and creates
DecryptingDemuxerStreams with any DemuxerStreams the MediaResource
implementation has. This DecryptingMediaResource is then given to the
renderer implementation as a MediaResource implementation which will
then only ever provide clear streams.

The DecryptingRenderer encapsulates a renderer implementation and a
DecryptingMediaResource. This DecryptingRenderer will ensure that the
DecryptingMediaResource is correctly initialized and will simply pass
through most function calls to the renderer implementation.

Bug: 913775
Change-Id: If348cd76328f2dd760751ceae087f0c89496e64b
Reviewed-on: https://chromium-review.googlesource.com/c/1387191
Reviewed-by: Luke Halliwell <halliwell@chromium.org>
Reviewed-by: Xiaohan Wang <xhwang@chromium.org>
Commit-Queue: Chad Duffin <chadduffin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#620076}
diff --git a/media/filters/decrypting_media_resource.cc b/media/filters/decrypting_media_resource.cc
new file mode 100644
index 0000000..882f1be
--- /dev/null
+++ b/media/filters/decrypting_media_resource.cc
@@ -0,0 +1,109 @@
+// Copyright 2018 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/filters/decrypting_media_resource.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "media/base/cdm_context.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/media_log.h"
+#include "media/base/pipeline_status.h"
+#include "media/filters/decrypting_demuxer_stream.h"
+
+namespace media {
+
+DecryptingMediaResource::DecryptingMediaResource(
+    MediaResource* media_resource,
+    CdmContext* cdm_context,
+    MediaLog* media_log,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+    : media_resource_(media_resource),
+      cdm_context_(cdm_context),
+      media_log_(media_log),
+      task_runner_(task_runner),
+      weak_factory_(this) {
+  DCHECK(media_resource);
+  DCHECK(cdm_context_);
+  DCHECK(cdm_context_->GetDecryptor());
+  DCHECK(cdm_context_->GetDecryptor()->CanAlwaysDecrypt());
+  DCHECK(media_log_);
+  DCHECK(task_runner->BelongsToCurrentThread());
+}
+
+DecryptingMediaResource::~DecryptingMediaResource() = default;
+
+MediaResource::Type DecryptingMediaResource::GetType() const {
+  return media_resource_->GetType();
+}
+
+std::vector<DemuxerStream*> DecryptingMediaResource::GetAllStreams() {
+  if (streams_.size())
+    return streams_;
+
+  return media_resource_->GetAllStreams();
+}
+
+MediaUrlParams DecryptingMediaResource::GetMediaUrlParams() const {
+  return media_resource_->GetMediaUrlParams();
+}
+
+void DecryptingMediaResource::Initialize(InitCB init_cb) {
+  DCHECK(init_cb);
+
+  auto streams = media_resource_->GetAllStreams();
+
+  // Save the callback so that we can invoke it when the
+  // DecryptingDemuxerStreams have finished initialization.
+  init_cb_ = std::move(init_cb);
+  num_dds_pending_init_ = streams.size();
+
+  for (auto* stream : streams) {
+    // TODO(chadduffin): Implement proper handling of the media::WaitingCB such
+    // that when the DecryptingDemuxerStream is waiting for a decryption key
+    // the firing of the callback will be bubbled up to the media pipeline.
+    auto decrypting_demuxer_stream = std::make_unique<DecryptingDemuxerStream>(
+        task_runner_, media_log_, base::DoNothing());
+
+    // DecryptingDemuxerStream always invokes the callback asynchronously so
+    // that we have no reentrancy issues. "All public APIs and callbacks are
+    // trampolined to the |task_runner_|."
+    decrypting_demuxer_stream->Initialize(
+        stream, cdm_context_,
+        base::BindRepeating(
+            &DecryptingMediaResource::OnDecryptingDemuxerInitialized,
+            weak_factory_.GetWeakPtr()));
+
+    streams_.push_back(decrypting_demuxer_stream.get());
+    owned_streams_.push_back(std::move(decrypting_demuxer_stream));
+  }
+}
+
+int DecryptingMediaResource::DecryptingDemuxerStreamCountForTesting() const {
+  return owned_streams_.size();
+}
+
+void DecryptingMediaResource::OnDecryptingDemuxerInitialized(
+    PipelineStatus status) {
+  DVLOG(2) << __func__ << ": DecryptingDemuxerStream initialization ended "
+           << "with the status: " << MediaLog::PipelineStatusToString(status);
+
+  // Decrement the count of DecryptingDemuxerStreams that need to be
+  // initialized.
+  --num_dds_pending_init_;
+
+  if (!init_cb_)
+    return;
+
+  if (status != PIPELINE_OK)
+    std::move(init_cb_).Run(false);
+  else if (num_dds_pending_init_ == 0)
+    std::move(init_cb_).Run(true);
+}
+
+}  // namespace media