summaryrefslogtreecommitdiff
path: root/chromium/media/fuchsia
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-01-23 17:21:03 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-01-23 16:25:15 +0000
commitc551f43206405019121bd2b2c93714319a0a3300 (patch)
tree1f48c30631c421fd4bbb3c36da20183c8a2ed7d7 /chromium/media/fuchsia
parent7961cea6d1041e3e454dae6a1da660b453efd238 (diff)
downloadqtwebengine-chromium-c551f43206405019121bd2b2c93714319a0a3300.tar.gz
BASELINE: Update Chromium to 79.0.3945.139
Change-Id: I336b7182fab9bca80b709682489c07db112eaca5 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/media/fuchsia')
-rw-r--r--chromium/media/fuchsia/cdm/BUILD.gn20
-rw-r--r--chromium/media/fuchsia/cdm/DEPS1
-rw-r--r--chromium/media/fuchsia/cdm/client/BUILD.gn20
-rw-r--r--chromium/media/fuchsia/cdm/client/fuchsia_cdm_util.cc18
-rw-r--r--chromium/media/fuchsia/cdm/client/fuchsia_cdm_util.h22
-rw-r--r--chromium/media/fuchsia/cdm/client/mojo_fuchsia_cdm_provider.cc30
-rw-r--r--chromium/media/fuchsia/cdm/client/mojo_fuchsia_cdm_provider.h41
-rw-r--r--chromium/media/fuchsia/cdm/fuchsia_cdm.cc99
-rw-r--r--chromium/media/fuchsia/cdm/fuchsia_cdm.h20
-rw-r--r--chromium/media/fuchsia/cdm/fuchsia_cdm_context.h27
-rw-r--r--chromium/media/fuchsia/cdm/fuchsia_cdm_factory.cc13
-rw-r--r--chromium/media/fuchsia/cdm/fuchsia_cdm_factory.h14
-rw-r--r--chromium/media/fuchsia/cdm/fuchsia_cdm_provider.h28
-rw-r--r--chromium/media/fuchsia/cdm/fuchsia_decryptor.cc96
-rw-r--r--chromium/media/fuchsia/cdm/fuchsia_decryptor.h68
-rw-r--r--chromium/media/fuchsia/cdm/fuchsia_stream_decryptor.cc555
-rw-r--r--chromium/media/fuchsia/cdm/fuchsia_stream_decryptor.h186
-rw-r--r--chromium/media/fuchsia/common/BUILD.gn27
-rw-r--r--chromium/media/fuchsia/common/stream_processor_helper.cc393
-rw-r--r--chromium/media/fuchsia/common/stream_processor_helper.h184
-rw-r--r--chromium/media/fuchsia/common/sysmem_buffer_pool.cc169
-rw-r--r--chromium/media/fuchsia/common/sysmem_buffer_pool.h118
-rw-r--r--chromium/media/fuchsia/common/sysmem_buffer_reader.cc56
-rw-r--r--chromium/media/fuchsia/common/sysmem_buffer_reader.h40
-rw-r--r--chromium/media/fuchsia/common/sysmem_buffer_writer.cc190
-rw-r--r--chromium/media/fuchsia/common/sysmem_buffer_writer.h59
-rw-r--r--chromium/media/fuchsia/common/sysmem_buffer_writer_queue.cc179
-rw-r--r--chromium/media/fuchsia/common/sysmem_buffer_writer_queue.h108
28 files changed, 2748 insertions, 33 deletions
diff --git a/chromium/media/fuchsia/cdm/BUILD.gn b/chromium/media/fuchsia/cdm/BUILD.gn
index 04defe57532..bc242ec7e93 100644
--- a/chromium/media/fuchsia/cdm/BUILD.gn
+++ b/chromium/media/fuchsia/cdm/BUILD.gn
@@ -8,15 +8,27 @@ source_set("cdm") {
sources = [
"fuchsia_cdm.cc",
"fuchsia_cdm.h",
+ "fuchsia_cdm_context.h",
"fuchsia_cdm_factory.cc",
"fuchsia_cdm_factory.h",
+ "fuchsia_cdm_provider.h",
+ "fuchsia_decryptor.cc",
+ "fuchsia_decryptor.h",
+ "fuchsia_stream_decryptor.cc",
+ "fuchsia_stream_decryptor.h",
]
+ public_deps = [
+ "//third_party/fuchsia-sdk/sdk:media_drm",
+ ]
+
+ configs += [ "//media:subcomponent_config" ]
+
deps = [
"//fuchsia/base",
- "//media",
- "//media/fuchsia/mojom",
- "//services/service_manager/public/cpp",
- "//third_party/fuchsia-sdk/sdk:media_drm",
+ "//media/base",
+ "//media/cdm",
+ "//media/fuchsia/common",
+ "//url",
]
}
diff --git a/chromium/media/fuchsia/cdm/DEPS b/chromium/media/fuchsia/cdm/DEPS
index 66b87e0e4e1..6e81cc5a759 100644
--- a/chromium/media/fuchsia/cdm/DEPS
+++ b/chromium/media/fuchsia/cdm/DEPS
@@ -1,4 +1,5 @@
include_rules = [
"+fuchsia/base",
+ "+mojo/public",
"+services/service_manager/public",
]
diff --git a/chromium/media/fuchsia/cdm/client/BUILD.gn b/chromium/media/fuchsia/cdm/client/BUILD.gn
new file mode 100644
index 00000000000..2b9537cae32
--- /dev/null
+++ b/chromium/media/fuchsia/cdm/client/BUILD.gn
@@ -0,0 +1,20 @@
+# Copyright 2019 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.
+
+assert(is_fuchsia)
+
+source_set("client") {
+ sources = [
+ "fuchsia_cdm_util.cc",
+ "fuchsia_cdm_util.h",
+ "mojo_fuchsia_cdm_provider.cc",
+ "mojo_fuchsia_cdm_provider.h",
+ ]
+
+ deps = [
+ "//media",
+ "//media/fuchsia/mojom",
+ "//services/service_manager/public/cpp",
+ ]
+}
diff --git a/chromium/media/fuchsia/cdm/client/fuchsia_cdm_util.cc b/chromium/media/fuchsia/cdm/client/fuchsia_cdm_util.cc
new file mode 100644
index 00000000000..e002b5be864
--- /dev/null
+++ b/chromium/media/fuchsia/cdm/client/fuchsia_cdm_util.cc
@@ -0,0 +1,18 @@
+// Copyright 2019 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/fuchsia/cdm/client/fuchsia_cdm_util.h"
+
+#include "media/fuchsia/cdm/client/mojo_fuchsia_cdm_provider.h"
+#include "media/fuchsia/cdm/fuchsia_cdm_factory.h"
+
+namespace media {
+
+std::unique_ptr<CdmFactory> CreateFuchsiaCdmFactory(
+ service_manager::InterfaceProvider* interface_provider) {
+ return std::make_unique<FuchsiaCdmFactory>(
+ std::make_unique<MojoFuchsiaCdmProvider>(interface_provider));
+}
+
+} // namespace media
diff --git a/chromium/media/fuchsia/cdm/client/fuchsia_cdm_util.h b/chromium/media/fuchsia/cdm/client/fuchsia_cdm_util.h
new file mode 100644
index 00000000000..ddc9b60405d
--- /dev/null
+++ b/chromium/media/fuchsia/cdm/client/fuchsia_cdm_util.h
@@ -0,0 +1,22 @@
+// Copyright 2019 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_FUCHSIA_CDM_CLIENT_FUCHSIA_CDM_UTIL_H_
+#define MEDIA_FUCHSIA_CDM_CLIENT_FUCHSIA_CDM_UTIL_H_
+
+#include <memory>
+
+namespace service_manager {
+class InterfaceProvider;
+}
+
+namespace media {
+class CdmFactory;
+
+std::unique_ptr<CdmFactory> CreateFuchsiaCdmFactory(
+ service_manager::InterfaceProvider* interface_provider);
+
+} // namespace media
+
+#endif // MEDIA_FUCHSIA_CDM_CLIENT_FUCHSIA_CDM_UTIL_H_
diff --git a/chromium/media/fuchsia/cdm/client/mojo_fuchsia_cdm_provider.cc b/chromium/media/fuchsia/cdm/client/mojo_fuchsia_cdm_provider.cc
new file mode 100644
index 00000000000..5366f06cc96
--- /dev/null
+++ b/chromium/media/fuchsia/cdm/client/mojo_fuchsia_cdm_provider.cc
@@ -0,0 +1,30 @@
+// Copyright 2019 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/fuchsia/cdm/client/mojo_fuchsia_cdm_provider.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
+
+namespace media {
+
+MojoFuchsiaCdmProvider::MojoFuchsiaCdmProvider(
+ service_manager::InterfaceProvider* interface_provider)
+ : interface_provider_(interface_provider) {
+ DCHECK(interface_provider_);
+}
+
+MojoFuchsiaCdmProvider::~MojoFuchsiaCdmProvider() = default;
+
+void MojoFuchsiaCdmProvider::CreateCdmInterface(
+ const std::string& key_system,
+ fidl::InterfaceRequest<fuchsia::media::drm::ContentDecryptionModule>
+ cdm_request) {
+ if (!cdm_provider_) {
+ interface_provider_->GetInterface(
+ cdm_provider_.BindNewPipeAndPassReceiver());
+ }
+
+ cdm_provider_->CreateCdmInterface(key_system, std::move(cdm_request));
+}
+
+} // namespace media
diff --git a/chromium/media/fuchsia/cdm/client/mojo_fuchsia_cdm_provider.h b/chromium/media/fuchsia/cdm/client/mojo_fuchsia_cdm_provider.h
new file mode 100644
index 00000000000..2c34ce45808
--- /dev/null
+++ b/chromium/media/fuchsia/cdm/client/mojo_fuchsia_cdm_provider.h
@@ -0,0 +1,41 @@
+// Copyright 2019 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_FUCHSIA_CDM_CLIENT_MOJO_FUCHSIA_CDM_PROVIDER_H_
+#define MEDIA_FUCHSIA_CDM_CLIENT_MOJO_FUCHSIA_CDM_PROVIDER_H_
+
+#include "base/macros.h"
+#include "media/fuchsia/cdm/fuchsia_cdm_provider.h"
+#include "media/fuchsia/mojom/fuchsia_cdm_provider.mojom.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace service_manager {
+class InterfaceProvider;
+}
+
+namespace media {
+
+class MojoFuchsiaCdmProvider : public FuchsiaCdmProvider {
+ public:
+ // |interface_provider| must outlive this class.
+ explicit MojoFuchsiaCdmProvider(
+ service_manager::InterfaceProvider* interface_provider);
+ ~MojoFuchsiaCdmProvider() override;
+
+ // FuchsiaCdmProvider implementation:
+ void CreateCdmInterface(
+ const std::string& key_system,
+ fidl::InterfaceRequest<fuchsia::media::drm::ContentDecryptionModule>
+ cdm_request) override;
+
+ private:
+ service_manager::InterfaceProvider* const interface_provider_;
+ mojo::Remote<media::mojom::FuchsiaCdmProvider> cdm_provider_;
+
+ DISALLOW_COPY_AND_ASSIGN(MojoFuchsiaCdmProvider);
+};
+
+} // namespace media
+
+#endif // MEDIA_FUCHSIA_CDM_CLIENT_MOJO_FUCHSIA_CDM_PROVIDER_H_
diff --git a/chromium/media/fuchsia/cdm/fuchsia_cdm.cc b/chromium/media/fuchsia/cdm/fuchsia_cdm.cc
index cbe083a91d6..2272683b8e8 100644
--- a/chromium/media/fuchsia/cdm/fuchsia_cdm.cc
+++ b/chromium/media/fuchsia/cdm/fuchsia_cdm.cc
@@ -11,6 +11,13 @@
#include "media/base/callback_registry.h"
#include "media/base/cdm_promise.h"
+#define REJECT_PROMISE_AND_RETURN_IF_BAD_CDM(promise, cdm) \
+ if (!cdm) { \
+ promise->reject(CdmPromise::Exception::INVALID_STATE_ERROR, 0, \
+ "CDM channel is disconnected."); \
+ return; \
+ }
+
namespace media {
namespace {
@@ -88,6 +95,15 @@ CdmPromise::Exception ToCdmPromiseException(fuchsia::media::drm::Error error) {
return CdmPromise::Exception::INVALID_STATE_ERROR;
case fuchsia::media::drm::Error::QUOTA_EXCEEDED:
return CdmPromise::Exception::QUOTA_EXCEEDED_ERROR;
+
+ case fuchsia::media::drm::Error::NOT_PROVISIONED:
+ // FuchsiaCdmManager is supposed to provision CDM.
+ NOTREACHED();
+ return CdmPromise::Exception::INVALID_STATE_ERROR;
+
+ case fuchsia::media::drm::Error::INTERNAL:
+ DLOG(ERROR) << "CDM failed due to an internal error.";
+ return CdmPromise::Exception::INVALID_STATE_ERROR;
}
}
@@ -98,8 +114,9 @@ class FuchsiaCdm::CdmSession {
using ResultCB =
base::OnceCallback<void(base::Optional<CdmPromise::Exception>)>;
- explicit CdmSession(const FuchsiaCdm::SessionCallbacks* callbacks)
- : session_callbacks_(callbacks) {
+ CdmSession(const FuchsiaCdm::SessionCallbacks* callbacks,
+ FuchsiaSecureStreamDecryptor::NewKeyCB on_new_key)
+ : session_callbacks_(callbacks), on_new_key_(on_new_key) {
// License session events, e.g. license request message, key status change.
// Fuchsia CDM service guarantees callback of functions (e.g.
// GenerateLicenseRequest) are called before event callbacks. So it's safe
@@ -167,17 +184,30 @@ class FuchsiaCdm::CdmSession {
}
void OnKeysChanged(std::vector<fuchsia::media::drm::KeyInfo> key_info) {
+ std::string new_key_id;
bool has_additional_usable_key = false;
CdmKeysInfo keys_info;
for (const auto& info : key_info) {
CdmKeyInformation::KeyStatus status = ToCdmKeyStatus(info.status);
has_additional_usable_key |= (status == CdmKeyInformation::USABLE);
+ if (status == CdmKeyInformation::USABLE && new_key_id.empty()) {
+ // The |key_id| is passed to |on_new_key_| to workaround fxb/38253 in
+ // FuchsiaSecureStreamDecryptor. It needs just one valid |key_id|, so it
+ // doesn't matter if |key_info| contains more than one key.
+ // TODO(crbug.com/1012525): Remove the hack once fxb/38253 is resolved.
+ new_key_id.assign(
+ reinterpret_cast<const char*>(info.key_id.data.data()),
+ info.key_id.data.size());
+ }
keys_info.emplace_back(new CdmKeyInformation(
info.key_id.data.data(), info.key_id.data.size(), status, 0));
}
session_callbacks_->keys_change_cb.Run(
session_id_, has_additional_usable_key, std::move(keys_info));
+
+ if (has_additional_usable_key)
+ on_new_key_.Run(new_key_id);
}
void OnSessionError(zx_status_t status) {
@@ -195,13 +225,16 @@ class FuchsiaCdm::CdmSession {
: base::nullopt);
}
+ const SessionCallbacks* const session_callbacks_;
+ FuchsiaSecureStreamDecryptor::NewKeyCB on_new_key_;
+
fuchsia::media::drm::LicenseSessionPtr session_;
std::string session_id_;
// Callback for license operation.
ResultCB result_cb_;
- const SessionCallbacks* session_callbacks_;
+ DISALLOW_COPY_AND_ASSIGN(CdmSession);
};
FuchsiaCdm::SessionCallbacks::SessionCallbacks() = default;
@@ -212,20 +245,50 @@ FuchsiaCdm::SessionCallbacks& FuchsiaCdm::SessionCallbacks::operator=(
FuchsiaCdm::FuchsiaCdm(fuchsia::media::drm::ContentDecryptionModulePtr cdm,
SessionCallbacks callbacks)
- : cdm_(std::move(cdm)), session_callbacks_(std::move(callbacks)) {
+ : cdm_(std::move(cdm)),
+ session_callbacks_(std::move(callbacks)),
+ decryptor_(cdm_.get()) {
DCHECK(cdm_);
- cdm_.set_error_handler([](zx_status_t status) {
- // Error will be handled in CdmSession::OnSessionError.
+ cdm_.set_error_handler([this](zx_status_t status) {
ZX_LOG(ERROR, status) << "The fuchsia.media.drm.ContentDecryptionModule"
<< " channel was terminated.";
+
+ // Reject all the pending promises.
+ promises_.Clear();
});
}
FuchsiaCdm::~FuchsiaCdm() = default;
+std::unique_ptr<FuchsiaSecureStreamDecryptor> FuchsiaCdm::CreateVideoDecryptor(
+ FuchsiaSecureStreamDecryptor::Client* client) {
+ fuchsia::media::drm::DecryptorParams params;
+
+ // TODO(crbug.com/997853): Enable secure mode when it's implemented in sysmem.
+ params.set_require_secure_mode(false);
+
+ params.mutable_input_details()->set_format_details_version_ordinal(0);
+ fuchsia::media::StreamProcessorPtr stream_processor;
+ cdm_->CreateDecryptor(std::move(params), stream_processor.NewRequest());
+
+ auto decryptor = std::make_unique<FuchsiaSecureStreamDecryptor>(
+ std::move(stream_processor), client);
+
+ // Save callback to use to notify the decryptor about a new key.
+ auto new_key_cb = decryptor->GetOnNewKeyClosure();
+ {
+ base::AutoLock auto_lock(new_key_cb_for_video_lock_);
+ new_key_cb_for_video_ = new_key_cb;
+ }
+
+ return decryptor;
+}
+
void FuchsiaCdm::SetServerCertificate(
const std::vector<uint8_t>& certificate,
std::unique_ptr<SimpleCdmPromise> promise) {
+ REJECT_PROMISE_AND_RETURN_IF_BAD_CDM(promise, cdm_);
+
uint32_t promise_id = promises_.SavePromise(std::move(promise));
cdm_->SetServerCertificate(
certificate,
@@ -261,9 +324,13 @@ void FuchsiaCdm::CreateSessionAndGenerateRequest(
return;
}
+ REJECT_PROMISE_AND_RETURN_IF_BAD_CDM(promise, cdm_);
+
uint32_t promise_id = promises_.SavePromise(std::move(promise));
- auto session = std::make_unique<CdmSession>(&session_callbacks_);
+ auto session = std::make_unique<CdmSession>(
+ &session_callbacks_,
+ base::BindRepeating(&FuchsiaCdm::OnNewKey, base::Unretained(this)));
CdmSession* session_ptr = session.get();
cdm_->CreateLicenseSession(
@@ -332,6 +399,8 @@ void FuchsiaCdm::UpdateSession(const std::string& session_id,
return;
}
+ REJECT_PROMISE_AND_RETURN_IF_BAD_CDM(promise, cdm_);
+
// Caller should NOT pass in an empty response.
DCHECK(!response.empty());
@@ -387,12 +456,24 @@ std::unique_ptr<CallbackRegistration> FuchsiaCdm::RegisterEventCB(
}
Decryptor* FuchsiaCdm::GetDecryptor() {
- NOTIMPLEMENTED();
- return nullptr;
+ return &decryptor_;
}
int FuchsiaCdm::GetCdmId() const {
return kInvalidCdmId;
}
+FuchsiaCdmContext* FuchsiaCdm::GetFuchsiaCdmContext() {
+ return this;
+}
+
+void FuchsiaCdm::OnNewKey(const std::string& key_id) {
+ decryptor_.OnNewKey();
+ {
+ base::AutoLock auto_lock(new_key_cb_for_video_lock_);
+ if (new_key_cb_for_video_)
+ new_key_cb_for_video_.Run(key_id);
+ }
+}
+
} // namespace media
diff --git a/chromium/media/fuchsia/cdm/fuchsia_cdm.h b/chromium/media/fuchsia/cdm/fuchsia_cdm.h
index 93e2a7efa9b..65fd5be7530 100644
--- a/chromium/media/fuchsia/cdm/fuchsia_cdm.h
+++ b/chromium/media/fuchsia/cdm/fuchsia_cdm.h
@@ -13,10 +13,14 @@
#include "media/base/cdm_context.h"
#include "media/base/cdm_promise_adapter.h"
#include "media/base/content_decryption_module.h"
+#include "media/fuchsia/cdm/fuchsia_cdm_context.h"
+#include "media/fuchsia/cdm/fuchsia_decryptor.h"
namespace media {
-class FuchsiaCdm : public ContentDecryptionModule, public CdmContext {
+class FuchsiaCdm : public ContentDecryptionModule,
+ public CdmContext,
+ public FuchsiaCdmContext {
public:
struct SessionCallbacks {
SessionCallbacks();
@@ -61,6 +65,11 @@ class FuchsiaCdm : public ContentDecryptionModule, public CdmContext {
EventCB event_cb) override;
Decryptor* GetDecryptor() override;
int GetCdmId() const override;
+ FuchsiaCdmContext* GetFuchsiaCdmContext() override;
+
+ // FuchsiaCdmContext implementation:
+ std::unique_ptr<FuchsiaSecureStreamDecryptor> CreateVideoDecryptor(
+ FuchsiaSecureStreamDecryptor::Client* client) override;
private:
class CdmSession;
@@ -78,7 +87,8 @@ class FuchsiaCdm : public ContentDecryptionModule, public CdmContext {
uint32_t promise_id,
base::Optional<CdmPromise::Exception> exception);
- void OnCdmError(zx_status_t status);
+ // TODO(crbug.com/1012525): Remove |key_id| once fxb/38253 is resolved.
+ void OnNewKey(const std::string& key_id);
CdmPromiseAdapter promises_;
base::flat_map<std::string, std::unique_ptr<CdmSession>> session_map_;
@@ -86,6 +96,12 @@ class FuchsiaCdm : public ContentDecryptionModule, public CdmContext {
fuchsia::media::drm::ContentDecryptionModulePtr cdm_;
SessionCallbacks session_callbacks_;
+ FuchsiaDecryptor decryptor_;
+
+ base::Lock new_key_cb_for_video_lock_;
+ FuchsiaSecureStreamDecryptor::NewKeyCB new_key_cb_for_video_
+ GUARDED_BY(new_key_cb_for_video_lock_);
+
DISALLOW_COPY_AND_ASSIGN(FuchsiaCdm);
};
diff --git a/chromium/media/fuchsia/cdm/fuchsia_cdm_context.h b/chromium/media/fuchsia/cdm/fuchsia_cdm_context.h
new file mode 100644
index 00000000000..9301cf657fc
--- /dev/null
+++ b/chromium/media/fuchsia/cdm/fuchsia_cdm_context.h
@@ -0,0 +1,27 @@
+// Copyright 2019 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_FUCHSIA_CDM_FUCHSIA_CDM_CONTEXT_H_
+#define MEDIA_FUCHSIA_CDM_FUCHSIA_CDM_CONTEXT_H_
+
+#include "media/fuchsia/cdm/fuchsia_stream_decryptor.h"
+
+namespace media {
+
+// Interface for Fuchsia-specific extensions to the CdmContext interface.
+class FuchsiaCdmContext {
+ public:
+ FuchsiaCdmContext() = default;
+
+ // Creates FuchsiaSecureStreamDecryptor instance for the CDM context.
+ virtual std::unique_ptr<FuchsiaSecureStreamDecryptor> CreateVideoDecryptor(
+ FuchsiaSecureStreamDecryptor::Client* client) = 0;
+
+ protected:
+ virtual ~FuchsiaCdmContext() = default;
+};
+
+} // namespace media
+
+#endif // MEDIA_FUCHSIA_CDM_FUCHSIA_CDM_CONTEXT_H_
diff --git a/chromium/media/fuchsia/cdm/fuchsia_cdm_factory.cc b/chromium/media/fuchsia/cdm/fuchsia_cdm_factory.cc
index 6e8059968ae..df0ee3cf802 100644
--- a/chromium/media/fuchsia/cdm/fuchsia_cdm_factory.cc
+++ b/chromium/media/fuchsia/cdm/fuchsia_cdm_factory.cc
@@ -4,23 +4,21 @@
#include "media/fuchsia/cdm/fuchsia_cdm_factory.h"
-#include <fuchsia/media/drm/cpp/fidl.h>
-
#include "base/bind.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/cdm_config.h"
#include "media/base/key_systems.h"
#include "media/cdm/aes_decryptor.h"
#include "media/fuchsia/cdm/fuchsia_cdm.h"
-#include "services/service_manager/public/cpp/interface_provider.h"
+#include "media/fuchsia/cdm/fuchsia_cdm_provider.h"
#include "url/origin.h"
namespace media {
FuchsiaCdmFactory::FuchsiaCdmFactory(
- service_manager::InterfaceProvider* interface_provider)
- : interface_provider_(interface_provider) {
- DCHECK(interface_provider_);
+ std::unique_ptr<FuchsiaCdmProvider> cdm_provider)
+ : cdm_provider_(std::move(cdm_provider)) {
+ DCHECK(cdm_provider_);
}
FuchsiaCdmFactory::~FuchsiaCdmFactory() = default;
@@ -49,9 +47,6 @@ void FuchsiaCdmFactory::Create(
return;
}
- if (!cdm_provider_)
- interface_provider_->GetInterface(mojo::MakeRequest(&cdm_provider_));
-
fuchsia::media::drm::ContentDecryptionModulePtr cdm_ptr;
cdm_provider_->CreateCdmInterface(key_system, cdm_ptr.NewRequest());
diff --git a/chromium/media/fuchsia/cdm/fuchsia_cdm_factory.h b/chromium/media/fuchsia/cdm/fuchsia_cdm_factory.h
index 6fdba9dac04..af820219722 100644
--- a/chromium/media/fuchsia/cdm/fuchsia_cdm_factory.h
+++ b/chromium/media/fuchsia/cdm/fuchsia_cdm_factory.h
@@ -5,22 +5,19 @@
#ifndef MEDIA_FUCHSIA_CDM_FUCHSIA_CDM_FACTORY_H_
#define MEDIA_FUCHSIA_CDM_FUCHSIA_CDM_FACTORY_H_
+#include <memory>
+
#include "base/macros.h"
#include "media/base/cdm_factory.h"
#include "media/base/media_export.h"
-#include "media/fuchsia/mojom/fuchsia_cdm_provider.mojom.h"
-
-namespace service_manager {
-class InterfaceProvider;
-}
+#include "media/fuchsia/cdm/fuchsia_cdm_provider.h"
namespace media {
class MEDIA_EXPORT FuchsiaCdmFactory : public CdmFactory {
public:
// |interface_provider| must outlive this class.
- explicit FuchsiaCdmFactory(
- service_manager::InterfaceProvider* interface_provider);
+ explicit FuchsiaCdmFactory(std::unique_ptr<FuchsiaCdmProvider> provider);
~FuchsiaCdmFactory() final;
// CdmFactory implementation.
@@ -34,8 +31,7 @@ class MEDIA_EXPORT FuchsiaCdmFactory : public CdmFactory {
const CdmCreatedCB& cdm_created_cb) final;
private:
- service_manager::InterfaceProvider* const interface_provider_;
- media::mojom::FuchsiaCdmProviderPtr cdm_provider_;
+ std::unique_ptr<FuchsiaCdmProvider> cdm_provider_;
DISALLOW_COPY_AND_ASSIGN(FuchsiaCdmFactory);
};
diff --git a/chromium/media/fuchsia/cdm/fuchsia_cdm_provider.h b/chromium/media/fuchsia/cdm/fuchsia_cdm_provider.h
new file mode 100644
index 00000000000..0e0a7b5565d
--- /dev/null
+++ b/chromium/media/fuchsia/cdm/fuchsia_cdm_provider.h
@@ -0,0 +1,28 @@
+// Copyright 2019 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_FUCHSIA_CDM_FUCHSIA_CDM_PROVIDER_H_
+#define MEDIA_FUCHSIA_CDM_FUCHSIA_CDM_PROVIDER_H_
+
+#include <fuchsia/media/drm/cpp/fidl.h>
+#include <string>
+
+#include "media/base/media_export.h"
+
+namespace media {
+
+// Interface to connect fuchsia::media::drm::ContentDecryptionModule to the
+// remote service.
+class MEDIA_EXPORT FuchsiaCdmProvider {
+ public:
+ virtual ~FuchsiaCdmProvider() = default;
+ virtual void CreateCdmInterface(
+ const std::string& key_system,
+ fidl::InterfaceRequest<fuchsia::media::drm::ContentDecryptionModule>
+ cdm_request) = 0;
+};
+
+} // namespace media
+
+#endif // MEDIA_FUCHSIA_CDM_FUCHSIA_CDM_PROVIDER_H_
diff --git a/chromium/media/fuchsia/cdm/fuchsia_decryptor.cc b/chromium/media/fuchsia/cdm/fuchsia_decryptor.cc
new file mode 100644
index 00000000000..9ae54062d9d
--- /dev/null
+++ b/chromium/media/fuchsia/cdm/fuchsia_decryptor.cc
@@ -0,0 +1,96 @@
+// Copyright 2019 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/fuchsia/cdm/fuchsia_decryptor.h"
+
+#include "base/fuchsia/fuchsia_logging.h"
+#include "base/logging.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/video_frame.h"
+#include "media/fuchsia/cdm/fuchsia_stream_decryptor.h"
+
+namespace media {
+
+FuchsiaDecryptor::FuchsiaDecryptor(
+ fuchsia::media::drm::ContentDecryptionModule* cdm)
+ : cdm_(cdm) {
+ DCHECK(cdm_);
+}
+
+FuchsiaDecryptor::~FuchsiaDecryptor() = default;
+
+void FuchsiaDecryptor::RegisterNewKeyCB(StreamType stream_type,
+ const NewKeyCB& new_key_cb) {
+ if (stream_type != kAudio)
+ return;
+
+ base::AutoLock auto_lock(new_key_cb_lock_);
+ new_key_cb_ = new_key_cb;
+}
+
+void FuchsiaDecryptor::Decrypt(StreamType stream_type,
+ scoped_refptr<DecoderBuffer> encrypted,
+ const DecryptCB& decrypt_cb) {
+ if (stream_type != StreamType::kAudio) {
+ decrypt_cb.Run(Status::kError, nullptr);
+ return;
+ }
+
+ if (!audio_decryptor_)
+ audio_decryptor_ = FuchsiaClearStreamDecryptor::Create(cdm_);
+
+ audio_decryptor_->Decrypt(std::move(encrypted), decrypt_cb);
+}
+
+void FuchsiaDecryptor::CancelDecrypt(StreamType stream_type) {
+ if (stream_type == StreamType::kAudio && audio_decryptor_) {
+ audio_decryptor_->CancelDecrypt();
+ }
+}
+
+void FuchsiaDecryptor::InitializeAudioDecoder(const AudioDecoderConfig& config,
+ const DecoderInitCB& init_cb) {
+ // Only decryption is supported.
+ init_cb.Run(false);
+}
+
+void FuchsiaDecryptor::InitializeVideoDecoder(const VideoDecoderConfig& config,
+ const DecoderInitCB& init_cb) {
+ // Only decryption is supported.
+ init_cb.Run(false);
+}
+
+void FuchsiaDecryptor::DecryptAndDecodeAudio(
+ scoped_refptr<DecoderBuffer> encrypted,
+ const AudioDecodeCB& audio_decode_cb) {
+ NOTREACHED();
+ audio_decode_cb.Run(Status::kError, AudioFrames());
+}
+
+void FuchsiaDecryptor::DecryptAndDecodeVideo(
+ scoped_refptr<DecoderBuffer> encrypted,
+ const VideoDecodeCB& video_decode_cb) {
+ NOTREACHED();
+ video_decode_cb.Run(Status::kError, nullptr);
+}
+
+void FuchsiaDecryptor::ResetDecoder(StreamType stream_type) {
+ NOTREACHED();
+}
+
+void FuchsiaDecryptor::DeinitializeDecoder(StreamType stream_type) {
+ NOTREACHED();
+}
+
+bool FuchsiaDecryptor::CanAlwaysDecrypt() {
+ return false;
+}
+
+void FuchsiaDecryptor::OnNewKey() {
+ base::AutoLock auto_lock(new_key_cb_lock_);
+ if (new_key_cb_)
+ new_key_cb_.Run();
+}
+
+} // namespace media
diff --git a/chromium/media/fuchsia/cdm/fuchsia_decryptor.h b/chromium/media/fuchsia/cdm/fuchsia_decryptor.h
new file mode 100644
index 00000000000..3af9e92f21b
--- /dev/null
+++ b/chromium/media/fuchsia/cdm/fuchsia_decryptor.h
@@ -0,0 +1,68 @@
+// Copyright 2019 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_FUCHSIA_CDM_FUCHSIA_DECRYPTOR_H_
+#define MEDIA_FUCHSIA_CDM_FUCHSIA_DECRYPTOR_H_
+
+#include <memory>
+
+#include "base/synchronization/lock.h"
+#include "base/thread_annotations.h"
+#include "media/base/decryptor.h"
+#include "media/fuchsia/cdm/fuchsia_stream_decryptor.h"
+
+namespace fuchsia {
+namespace media {
+namespace drm {
+class ContentDecryptionModule;
+} // namespace drm
+} // namespace media
+} // namespace fuchsia
+
+namespace media {
+
+class FuchsiaClearStreamDecryptor;
+
+class FuchsiaDecryptor : public Decryptor {
+ public:
+ // Caller should make sure |cdm| lives longer than this class.
+ explicit FuchsiaDecryptor(fuchsia::media::drm::ContentDecryptionModule* cdm);
+ ~FuchsiaDecryptor() override;
+
+ // media::Decryptor implementation:
+ void RegisterNewKeyCB(StreamType stream_type,
+ const NewKeyCB& key_added_cb) override;
+ void Decrypt(StreamType stream_type,
+ scoped_refptr<DecoderBuffer> encrypted,
+ const DecryptCB& decrypt_cb) override;
+ void CancelDecrypt(StreamType stream_type) override;
+ void InitializeAudioDecoder(const AudioDecoderConfig& config,
+ const DecoderInitCB& init_cb) override;
+ void InitializeVideoDecoder(const VideoDecoderConfig& config,
+ const DecoderInitCB& init_cb) override;
+ void DecryptAndDecodeAudio(scoped_refptr<DecoderBuffer> encrypted,
+ const AudioDecodeCB& audio_decode_cb) override;
+ void DecryptAndDecodeVideo(scoped_refptr<DecoderBuffer> encrypted,
+ const VideoDecodeCB& video_decode_cb) override;
+ void ResetDecoder(StreamType stream_type) override;
+ void DeinitializeDecoder(StreamType stream_type) override;
+ bool CanAlwaysDecrypt() override;
+
+ // Called by FuchsiaCdm to notify about the new key.
+ void OnNewKey();
+
+ private:
+ fuchsia::media::drm::ContentDecryptionModule* const cdm_;
+
+ base::Lock new_key_cb_lock_;
+ NewKeyCB new_key_cb_ GUARDED_BY(new_key_cb_lock_);
+
+ std::unique_ptr<FuchsiaClearStreamDecryptor> audio_decryptor_;
+
+ DISALLOW_COPY_AND_ASSIGN(FuchsiaDecryptor);
+};
+
+} // namespace media
+
+#endif // MEDIA_FUCHSIA_CDM_FUCHSIA_DECRYPTOR_H_
diff --git a/chromium/media/fuchsia/cdm/fuchsia_stream_decryptor.cc b/chromium/media/fuchsia/cdm/fuchsia_stream_decryptor.cc
new file mode 100644
index 00000000000..176a0251b0d
--- /dev/null
+++ b/chromium/media/fuchsia/cdm/fuchsia_stream_decryptor.cc
@@ -0,0 +1,555 @@
+// Copyright 2019 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/fuchsia/cdm/fuchsia_stream_decryptor.h"
+
+#include <fuchsia/media/cpp/fidl.h>
+#include <fuchsia/media/drm/cpp/fidl.h>
+
+#include "base/bind.h"
+#include "base/fuchsia/fuchsia_logging.h"
+#include "base/logging.h"
+#include "media/base/bind_to_current_loop.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/decrypt_config.h"
+#include "media/base/encryption_pattern.h"
+#include "media/base/subsample_entry.h"
+#include "media/fuchsia/common/sysmem_buffer_reader.h"
+#include "media/fuchsia/common/sysmem_buffer_writer.h"
+
+namespace media {
+namespace {
+
+// FuchsiaClearStreamDecryptor copies decrypted data immediately once it's
+// available, so it doesn't need more than one output buffer.
+const size_t kMinClearStreamOutputFrames = 1;
+
+std::string GetEncryptionMode(EncryptionMode mode) {
+ switch (mode) {
+ case EncryptionMode::kCenc:
+ return fuchsia::media::drm::ENCRYPTION_MODE_CENC;
+ case EncryptionMode::kCbcs:
+ return fuchsia::media::drm::ENCRYPTION_MODE_CBCS;
+ default:
+ NOTREACHED() << "unknown encryption mode " << static_cast<int>(mode);
+ return "";
+ }
+}
+
+fuchsia::media::KeyId GetKeyId(const std::string& key_id) {
+ fuchsia::media::KeyId fuchsia_key_id;
+ // |key_id| may be empty when sending clear frames while we don't have
+ // Key Id yet. Send a zero |key_id| in that case.
+ // TODO(crbug.com/1012525): Remove this case once fxb/38253 is resolved.
+ if (key_id.empty()) {
+ fuchsia_key_id.data = {0};
+ } else {
+ DCHECK_EQ(key_id.size(), fuchsia::media::KEY_ID_SIZE);
+ std::copy(key_id.begin(), key_id.end(), fuchsia_key_id.data.begin());
+ }
+ return fuchsia_key_id;
+}
+
+std::vector<fuchsia::media::SubsampleEntry> GetSubsamples(
+ const std::vector<SubsampleEntry>& subsamples) {
+ std::vector<fuchsia::media::SubsampleEntry> fuchsia_subsamples(
+ subsamples.size());
+
+ for (size_t i = 0; i < subsamples.size(); i++) {
+ fuchsia_subsamples[i].clear_bytes = subsamples[i].clear_bytes;
+ fuchsia_subsamples[i].encrypted_bytes = subsamples[i].cypher_bytes;
+ }
+
+ return fuchsia_subsamples;
+}
+
+fuchsia::media::EncryptionPattern GetEncryptionPattern(
+ EncryptionPattern pattern) {
+ fuchsia::media::EncryptionPattern fuchsia_pattern;
+ fuchsia_pattern.clear_blocks = pattern.skip_byte_block();
+ fuchsia_pattern.encrypted_blocks = pattern.crypt_byte_block();
+ return fuchsia_pattern;
+}
+
+// We shouldn't need to set Key ID for clear frames, but it's currently
+// required by the CDM API, see fxb/38253 . This function takes
+// |placeholder_key_id| to workaround that issue.
+// TODO(crbug.com/1012525): Remove |placeholder_key_id| once fxb/38253 is
+// resolved.
+fuchsia::media::FormatDetails GetClearFormatDetails(
+ size_t packet_size,
+ const std::string& placeholder_key_id) {
+ fuchsia::media::EncryptedFormat encrypted_format;
+ encrypted_format.set_mode(fuchsia::media::drm::ENCRYPTION_MODE_CENC)
+ .set_key_id(GetKeyId(placeholder_key_id))
+ .set_init_vector(std::vector<uint8_t>(DecryptConfig::kDecryptionKeySize));
+
+ std::vector<fuchsia::media::SubsampleEntry> subsamples(1);
+ subsamples[0].clear_bytes = packet_size;
+ subsamples[0].encrypted_bytes = 0;
+ encrypted_format.set_subsamples(subsamples);
+
+ fuchsia::media::FormatDetails format;
+ format.set_format_details_version_ordinal(0);
+ format.mutable_domain()->crypto().set_encrypted(std::move(encrypted_format));
+ return format;
+}
+
+fuchsia::media::FormatDetails GetEncryptedFormatDetails(
+ const DecryptConfig* config) {
+ DCHECK(config);
+
+ fuchsia::media::EncryptedFormat encrypted_format;
+ encrypted_format.set_mode(GetEncryptionMode(config->encryption_mode()))
+ .set_key_id(GetKeyId(config->key_id()))
+ .set_init_vector(
+ std::vector<uint8_t>(config->iv().begin(), config->iv().end()))
+ .set_subsamples(GetSubsamples(config->subsamples()));
+ if (config->encryption_mode() == EncryptionMode::kCbcs) {
+ DCHECK(config->encryption_pattern().has_value());
+ encrypted_format.set_pattern(
+ GetEncryptionPattern(config->encryption_pattern().value()));
+ }
+
+ fuchsia::media::FormatDetails format;
+ format.set_format_details_version_ordinal(0);
+ format.mutable_domain()->crypto().set_encrypted(std::move(encrypted_format));
+ return format;
+}
+
+} // namespace
+
+FuchsiaStreamDecryptorBase::FuchsiaStreamDecryptorBase(
+ fuchsia::media::StreamProcessorPtr processor)
+ : processor_(std::move(processor), this) {}
+
+FuchsiaStreamDecryptorBase::~FuchsiaStreamDecryptorBase() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void FuchsiaStreamDecryptorBase::DecryptInternal(
+ scoped_refptr<DecoderBuffer> encrypted) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ input_writer_queue_.EnqueueBuffer(std::move(encrypted));
+}
+
+void FuchsiaStreamDecryptorBase::ResetStream() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ // Close current stream and drop all the cached decoder buffers.
+ // Keep input and output buffers to avoid buffer re-allocation.
+ processor_.Reset();
+ input_writer_queue_.ResetQueue();
+}
+
+// StreamProcessorHelper::Client implementation:
+void FuchsiaStreamDecryptorBase::AllocateInputBuffers(
+ const fuchsia::media::StreamBufferConstraints& stream_constraints) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ base::Optional<fuchsia::sysmem::BufferCollectionConstraints>
+ buffer_constraints =
+ SysmemBufferWriter::GetRecommendedConstraints(stream_constraints);
+
+ if (!buffer_constraints.has_value()) {
+ OnError();
+ return;
+ }
+
+ input_pool_creator_ =
+ allocator_.MakeBufferPoolCreator(1 /* num_shared_token */);
+
+ input_pool_creator_->Create(
+ std::move(buffer_constraints).value(),
+ base::BindOnce(&FuchsiaStreamDecryptorBase::OnInputBufferPoolCreated,
+ base::Unretained(this)));
+}
+
+void FuchsiaStreamDecryptorBase::OnOutputFormat(
+ fuchsia::media::StreamOutputFormat format) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void FuchsiaStreamDecryptorBase::OnInputBufferPoolCreated(
+ std::unique_ptr<SysmemBufferPool> pool) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ if (!pool) {
+ DLOG(ERROR) << "Fail to allocate input buffer.";
+ OnError();
+ return;
+ }
+
+ input_pool_ = std::move(pool);
+
+ // Provide token before enabling writer. Tokens must be provided to
+ // StreamProcessor before getting the allocated buffers.
+ processor_.CompleteInputBuffersAllocation(input_pool_->TakeToken());
+
+ input_pool_->CreateWriter(base::BindOnce(
+ &FuchsiaStreamDecryptorBase::OnWriterCreated, base::Unretained(this)));
+}
+
+void FuchsiaStreamDecryptorBase::OnWriterCreated(
+ std::unique_ptr<SysmemBufferWriter> writer) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ if (!writer) {
+ OnError();
+ return;
+ }
+
+ input_writer_queue_.Start(
+ std::move(writer),
+ base::BindRepeating(&FuchsiaStreamDecryptorBase::SendInputPacket,
+ base::Unretained(this)),
+ base::BindRepeating(&FuchsiaStreamDecryptorBase::ProcessEndOfStream,
+ base::Unretained(this)));
+}
+
+void FuchsiaStreamDecryptorBase::SendInputPacket(
+ const DecoderBuffer* buffer,
+ StreamProcessorHelper::IoPacket packet) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ if (!packet.unit_end()) {
+ // The encrypted data size is too big. Decryptor should consider
+ // splitting the buffer and update the IV and subsample entries.
+ // TODO(crbug.com/1003651): Handle large encrypted buffer correctly. For
+ // now, just reject the decryption.
+ LOG(ERROR) << "DecoderBuffer doesn't fit in one packet.";
+ OnError();
+ return;
+ }
+
+ fuchsia::media::FormatDetails format =
+ (buffer->decrypt_config())
+ ? GetEncryptedFormatDetails(buffer->decrypt_config())
+ : GetClearFormatDetails(packet.size(), last_new_key_id_);
+
+ packet.set_format(std::move(format));
+ processor_.Process(std::move(packet));
+}
+
+void FuchsiaStreamDecryptorBase::ProcessEndOfStream() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ processor_.ProcessEos();
+}
+
+std::unique_ptr<FuchsiaClearStreamDecryptor>
+FuchsiaClearStreamDecryptor::Create(
+ fuchsia::media::drm::ContentDecryptionModule* cdm) {
+ DCHECK(cdm);
+
+ fuchsia::media::drm::DecryptorParams params;
+ params.set_require_secure_mode(false);
+ params.mutable_input_details()->set_format_details_version_ordinal(0);
+ fuchsia::media::StreamProcessorPtr stream_processor;
+ cdm->CreateDecryptor(std::move(params), stream_processor.NewRequest());
+
+ return std::make_unique<FuchsiaClearStreamDecryptor>(
+ std::move(stream_processor));
+}
+
+FuchsiaClearStreamDecryptor::FuchsiaClearStreamDecryptor(
+ fuchsia::media::StreamProcessorPtr processor)
+ : FuchsiaStreamDecryptorBase(std::move(processor)) {}
+
+FuchsiaClearStreamDecryptor::~FuchsiaClearStreamDecryptor() = default;
+
+void FuchsiaClearStreamDecryptor::Decrypt(
+ scoped_refptr<DecoderBuffer> encrypted,
+ Decryptor::DecryptCB decrypt_cb) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!decrypt_cb_);
+
+ decrypt_cb_ = std::move(decrypt_cb);
+ current_status_ = Decryptor::kSuccess;
+ DecryptInternal(std::move(encrypted));
+}
+
+void FuchsiaClearStreamDecryptor::CancelDecrypt() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ ResetStream();
+
+ // Fire |decrypt_cb_| immediately as required by Decryptor::CancelDecrypt.
+ if (decrypt_cb_)
+ std::move(decrypt_cb_).Run(Decryptor::kSuccess, nullptr);
+}
+
+void FuchsiaClearStreamDecryptor::AllocateOutputBuffers(
+ const fuchsia::media::StreamBufferConstraints& stream_constraints) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ if (!stream_constraints.has_packet_count_for_client_max() ||
+ !stream_constraints.has_packet_count_for_client_min()) {
+ DLOG(ERROR) << "StreamBufferConstraints doesn't contain required fields.";
+ OnError();
+ return;
+ }
+
+ size_t num_buffers_for_client = std::max(
+ kMinClearStreamOutputFrames,
+ static_cast<size_t>(stream_constraints.packet_count_for_client_min()));
+ size_t num_buffers_for_server =
+ stream_constraints.default_settings().packet_count_for_server();
+
+ output_pool_creator_ =
+ allocator_.MakeBufferPoolCreator(1 /* num_shared_token */);
+ output_pool_creator_->Create(
+ SysmemBufferReader::GetRecommendedConstraints(num_buffers_for_client),
+ base::BindOnce(&FuchsiaClearStreamDecryptor::OnOutputBufferPoolCreated,
+ base::Unretained(this), num_buffers_for_client,
+ num_buffers_for_server));
+}
+
+void FuchsiaClearStreamDecryptor::OnProcessEos() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ // Decryptor never pushes EOS frame.
+ NOTREACHED();
+}
+
+void FuchsiaClearStreamDecryptor::OnOutputPacket(
+ StreamProcessorHelper::IoPacket packet) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(decrypt_cb_);
+
+ DCHECK(output_reader_);
+ if (!output_pool_->is_live()) {
+ DLOG(ERROR) << "Output buffer pool is dead.";
+ return;
+ }
+
+ // If that's not the last packet for the current Decrypt() request then just
+ // store the output and wait for the next packet.
+ if (!packet.unit_end()) {
+ size_t pos = output_data_.size();
+ output_data_.resize(pos + packet.size());
+
+ bool read_success = output_reader_->Read(
+ packet.index(), packet.offset(),
+ base::make_span(output_data_.data() + pos, packet.size()));
+
+ if (!read_success) {
+ // If we've failed to read a partial packet then delay reporting the error
+ // until we've received the last packet to make sure we consume all output
+ // packets generated by the last Decrypt() call.
+ DLOG(ERROR) << "Fail to get decrypted result.";
+ current_status_ = Decryptor::kError;
+ output_data_.clear();
+ }
+
+ return;
+ }
+
+ // We've received the last packet. Assemble DecoderBuffer and pass it to the
+ // DecryptCB.
+ auto clear_buffer =
+ base::MakeRefCounted<DecoderBuffer>(output_data_.size() + packet.size());
+ clear_buffer->set_timestamp(packet.timestamp());
+
+ // Copy data received in the previous packets.
+ memcpy(clear_buffer->writable_data(), output_data_.data(),
+ output_data_.size());
+ output_data_.clear();
+
+ // Copy data received in the last packet
+ bool read_success = output_reader_->Read(
+ packet.index(), packet.offset(),
+ base::make_span(clear_buffer->writable_data() + output_data_.size(),
+ packet.size()));
+
+ if (!read_success) {
+ DLOG(ERROR) << "Fail to get decrypted result.";
+ current_status_ = Decryptor::kError;
+ }
+
+ std::move(decrypt_cb_)
+ .Run(current_status_, current_status_ == Decryptor::kSuccess
+ ? std::move(clear_buffer)
+ : nullptr);
+}
+
+void FuchsiaClearStreamDecryptor::OnNoKey() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ // Reset the queue. The client is expected to call Decrypt() with the same
+ // buffer again when it gets kNoKey.
+ input_writer_queue_.ResetQueue();
+
+ if (decrypt_cb_)
+ std::move(decrypt_cb_).Run(Decryptor::kNoKey, nullptr);
+}
+
+void FuchsiaClearStreamDecryptor::OnError() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ ResetStream();
+ if (decrypt_cb_)
+ std::move(decrypt_cb_).Run(Decryptor::kError, nullptr);
+}
+
+void FuchsiaClearStreamDecryptor::OnOutputBufferPoolCreated(
+ size_t num_buffers_for_client,
+ size_t num_buffers_for_server,
+ std::unique_ptr<SysmemBufferPool> pool) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ if (!pool) {
+ LOG(ERROR) << "Fail to allocate output buffer.";
+ OnError();
+ return;
+ }
+
+ output_pool_ = std::move(pool);
+
+ // Provide token before enabling reader. Tokens must be provided to
+ // StreamProcessor before getting the allocated buffers.
+ processor_.CompleteOutputBuffersAllocation(num_buffers_for_client,
+ num_buffers_for_server,
+ output_pool_->TakeToken());
+
+ output_pool_->CreateReader(base::BindOnce(
+ &FuchsiaClearStreamDecryptor::OnOutputBufferPoolReaderCreated,
+ base::Unretained(this)));
+}
+
+void FuchsiaClearStreamDecryptor::OnOutputBufferPoolReaderCreated(
+ std::unique_ptr<SysmemBufferReader> reader) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ if (!reader) {
+ LOG(ERROR) << "Fail to enable output buffer reader.";
+ OnError();
+ return;
+ }
+
+ DCHECK(!output_reader_);
+ output_reader_ = std::move(reader);
+}
+
+FuchsiaSecureStreamDecryptor::FuchsiaSecureStreamDecryptor(
+ fuchsia::media::StreamProcessorPtr processor,
+ Client* client)
+ : FuchsiaStreamDecryptorBase(std::move(processor)), client_(client) {}
+
+FuchsiaSecureStreamDecryptor::~FuchsiaSecureStreamDecryptor() = default;
+
+void FuchsiaSecureStreamDecryptor::SetOutputBufferCollectionToken(
+ fuchsia::sysmem::BufferCollectionTokenPtr token,
+ size_t num_buffers_for_decryptor,
+ size_t num_buffers_for_codec) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!complete_buffer_allocation_callback_);
+ complete_buffer_allocation_callback_ =
+ base::BindOnce(&StreamProcessorHelper::CompleteOutputBuffersAllocation,
+ base::Unretained(&processor_), num_buffers_for_decryptor,
+ num_buffers_for_codec, std::move(token));
+ if (waiting_output_buffers_) {
+ std::move(complete_buffer_allocation_callback_).Run();
+ waiting_output_buffers_ = false;
+ }
+}
+
+void FuchsiaSecureStreamDecryptor::Decrypt(
+ scoped_refptr<DecoderBuffer> encrypted) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ DecryptInternal(std::move(encrypted));
+}
+
+void FuchsiaSecureStreamDecryptor::Reset() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ ResetStream();
+ waiting_for_key_ = false;
+}
+
+void FuchsiaSecureStreamDecryptor::AllocateOutputBuffers(
+ const fuchsia::media::StreamBufferConstraints& stream_constraints) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ if (complete_buffer_allocation_callback_) {
+ std::move(complete_buffer_allocation_callback_).Run();
+ } else {
+ waiting_output_buffers_ = true;
+ }
+}
+
+void FuchsiaSecureStreamDecryptor::OnProcessEos() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ client_->OnDecryptorEndOfStreamPacket();
+}
+
+void FuchsiaSecureStreamDecryptor::OnOutputPacket(
+ StreamProcessorHelper::IoPacket packet) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ client_->OnDecryptorOutputPacket(std::move(packet));
+}
+
+FuchsiaSecureStreamDecryptor::NewKeyCB
+FuchsiaSecureStreamDecryptor::GetOnNewKeyClosure() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ return BindToCurrentLoop(base::BindRepeating(
+ &FuchsiaSecureStreamDecryptor::OnNewKey, weak_factory_.GetWeakPtr()));
+}
+
+void FuchsiaSecureStreamDecryptor::OnError() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ ResetStream();
+
+ // No need to reset other fields since OnError() is called for non-recoverable
+ // errors.
+
+ client_->OnDecryptorError();
+}
+
+void FuchsiaSecureStreamDecryptor::OnNoKey() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!waiting_for_key_);
+
+ // Reset stream position, but keep all pending buffers. They will be
+ // resubmitted later, when we have a new key.
+ input_writer_queue_.ResetPositionAndPause();
+
+ if (retry_on_no_key_) {
+ retry_on_no_key_ = false;
+ input_writer_queue_.Unpause();
+ return;
+ }
+
+ waiting_for_key_ = true;
+ client_->OnDecryptorNoKey();
+}
+
+void FuchsiaSecureStreamDecryptor::OnNewKey(const std::string& key_id) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ // Currently Widevine CDM requires a valid key_id for frames that are not
+ // encrypted, but we don't have a key_id in the beginning of the stream. To
+ // workaround this issue we save the |key_id| here and then use it for clear
+ // frames in SendInputPacket().
+ // TODO(crbug.com/1012525): Remove this hack once fxb/38253 is resolved: CDM
+ // shouldn't need |key_id| to handle clear frames.
+ last_new_key_id_ = key_id;
+
+ if (!waiting_for_key_) {
+ retry_on_no_key_ = true;
+ return;
+ }
+
+ DCHECK(!retry_on_no_key_);
+ waiting_for_key_ = false;
+ input_writer_queue_.Unpause();
+}
+
+} // namespace media
diff --git a/chromium/media/fuchsia/cdm/fuchsia_stream_decryptor.h b/chromium/media/fuchsia/cdm/fuchsia_stream_decryptor.h
new file mode 100644
index 00000000000..d0962b32c02
--- /dev/null
+++ b/chromium/media/fuchsia/cdm/fuchsia_stream_decryptor.h
@@ -0,0 +1,186 @@
+// Copyright 2019 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_FUCHSIA_CDM_FUCHSIA_STREAM_DECRYPTOR_H_
+#define MEDIA_FUCHSIA_CDM_FUCHSIA_STREAM_DECRYPTOR_H_
+
+#include <fuchsia/media/drm/cpp/fidl.h>
+
+#include <memory>
+
+#include "base/sequence_checker.h"
+#include "media/base/decryptor.h"
+#include "media/fuchsia/common/stream_processor_helper.h"
+#include "media/fuchsia/common/sysmem_buffer_pool.h"
+#include "media/fuchsia/common/sysmem_buffer_writer_queue.h"
+
+namespace media {
+class SysmemBufferReader;
+
+// Base class for media stream decryptor implementations.
+class FuchsiaStreamDecryptorBase : public StreamProcessorHelper::Client {
+ public:
+ explicit FuchsiaStreamDecryptorBase(
+ fuchsia::media::StreamProcessorPtr processor);
+ ~FuchsiaStreamDecryptorBase() override;
+
+ protected:
+ // StreamProcessorHelper::Client overrides.
+ void AllocateInputBuffers(
+ const fuchsia::media::StreamBufferConstraints& stream_constraints) final;
+ void OnOutputFormat(fuchsia::media::StreamOutputFormat format) final;
+
+ void DecryptInternal(scoped_refptr<DecoderBuffer> encrypted);
+ void ResetStream();
+
+ StreamProcessorHelper processor_;
+
+ BufferAllocator allocator_;
+
+ SysmemBufferWriterQueue input_writer_queue_;
+
+ // Key ID for which we received the last OnNewKey() event.
+ std::string last_new_key_id_;
+
+ SEQUENCE_CHECKER(sequence_checker_);
+
+ private:
+ void OnInputBufferPoolCreated(std::unique_ptr<SysmemBufferPool> pool);
+ void OnWriterCreated(std::unique_ptr<SysmemBufferWriter> writer);
+ void SendInputPacket(const DecoderBuffer* buffer,
+ StreamProcessorHelper::IoPacket packet);
+ void ProcessEndOfStream();
+
+ std::unique_ptr<SysmemBufferPool::Creator> input_pool_creator_;
+ std::unique_ptr<SysmemBufferPool> input_pool_;
+
+ DISALLOW_COPY_AND_ASSIGN(FuchsiaStreamDecryptorBase);
+};
+
+// Stream decryptor that copies output to clear DecodeBuffer's. Used for audio
+// streams.
+class FuchsiaClearStreamDecryptor : public FuchsiaStreamDecryptorBase {
+ public:
+ static std::unique_ptr<FuchsiaClearStreamDecryptor> Create(
+ fuchsia::media::drm::ContentDecryptionModule* cdm);
+
+ FuchsiaClearStreamDecryptor(fuchsia::media::StreamProcessorPtr processor);
+ ~FuchsiaClearStreamDecryptor() override;
+
+ // Decrypt() behavior should match media::Decryptor interface.
+ void Decrypt(scoped_refptr<DecoderBuffer> encrypted,
+ Decryptor::DecryptCB decrypt_cb);
+ void CancelDecrypt();
+
+ private:
+ // StreamProcessorHelper::Client overrides.
+ void AllocateOutputBuffers(
+ const fuchsia::media::StreamBufferConstraints& stream_constraints) final;
+ void OnProcessEos() final;
+ void OnOutputPacket(StreamProcessorHelper::IoPacket packet) final;
+ void OnNoKey() final;
+ void OnError() final;
+
+ void OnOutputBufferPoolCreated(size_t num_buffers_for_client,
+ size_t num_buffers_for_server,
+ std::unique_ptr<SysmemBufferPool> pool);
+ void OnOutputBufferPoolReaderCreated(
+ std::unique_ptr<SysmemBufferReader> reader);
+
+ Decryptor::DecryptCB decrypt_cb_;
+
+ std::unique_ptr<SysmemBufferPool::Creator> output_pool_creator_;
+ std::unique_ptr<SysmemBufferPool> output_pool_;
+ std::unique_ptr<SysmemBufferReader> output_reader_;
+
+ // Used to re-assemble decrypted output that was split between multiple sysmem
+ // buffers.
+ Decryptor::Status current_status_ = Decryptor::kSuccess;
+ std::vector<uint8_t> output_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(FuchsiaClearStreamDecryptor);
+};
+
+// Stream decryptor that decrypts data to protected sysmem buffers. Used for
+// video stream.
+class FuchsiaSecureStreamDecryptor : public FuchsiaStreamDecryptorBase {
+ public:
+ class Client {
+ public:
+ virtual void OnDecryptorOutputPacket(
+ StreamProcessorHelper::IoPacket packet) = 0;
+ virtual void OnDecryptorEndOfStreamPacket() = 0;
+ virtual void OnDecryptorError() = 0;
+ virtual void OnDecryptorNoKey() = 0;
+
+ protected:
+ virtual ~Client() = default;
+ };
+
+ using NewKeyCB = base::RepeatingCallback<void(const std::string& key_id)>;
+
+ FuchsiaSecureStreamDecryptor(fuchsia::media::StreamProcessorPtr processor,
+ Client* client);
+ ~FuchsiaSecureStreamDecryptor() override;
+
+ void SetOutputBufferCollectionToken(
+ fuchsia::sysmem::BufferCollectionTokenPtr token,
+ size_t num_buffers_for_decryptor,
+ size_t num_buffers_for_codec);
+
+ // Enqueues the specified buffer to the input queue. Caller is allowed to
+ // queue as many buffers as it needs without waiting for results from the
+ // previous Decrypt() calls.
+ void Decrypt(scoped_refptr<DecoderBuffer> encrypted);
+
+ // Returns closure that should be called when the key changes. This class
+ // uses this notification to handle NO_KEY errors. Note that this class can
+ // queue multiple input buffers so it's also responsible for resubmitting
+ // queued buffers after a new key is received. This is different from
+ // FuchsiaClearStreamDecryptor and media::Decryptor: they report NO_KEY error
+ // to the caller and expect the caller to resubmit same buffers again after
+ // the key is updated.
+ NewKeyCB GetOnNewKeyClosure();
+
+ // Drops all pending decryption requests.
+ void Reset();
+
+ private:
+ // StreamProcessorHelper::Client overrides.
+ void AllocateOutputBuffers(
+ const fuchsia::media::StreamBufferConstraints& stream_constraints) final;
+ void OnProcessEos() final;
+ void OnOutputPacket(StreamProcessorHelper::IoPacket packet) final;
+ void OnNoKey() final;
+ void OnError() final;
+
+ // Callback returned by GetOnNewKeyClosure(). When waiting for a key this
+ // method unpauses the stream to decrypt any pending buffers.
+ void OnNewKey(const std::string& key_id);
+
+ Client* const client_;
+
+ bool waiting_output_buffers_ = false;
+ base::OnceClosure complete_buffer_allocation_callback_;
+
+ // Set to true if some keys have been updated recently. New key notifications
+ // are received from a LicenseSession, while DECRYPTOR_NO_KEY error is
+ // received from StreamProcessor. These are separate FIDL connections that are
+ // handled on different threads, so they are not synchronized. As result
+ // OnNewKey() may be called before we get OnNoKey(). To handle this case
+ // correctly OnNewKey() sets |retry_on_no_key_| and then OnNoKey() tries to
+ // restart the stream immediately if this flag is set.
+ bool retry_on_no_key_ = false;
+
+ // Set to true if the stream is paused while we are waiting for new keys.
+ bool waiting_for_key_ = false;
+
+ base::WeakPtrFactory<FuchsiaSecureStreamDecryptor> weak_factory_{this};
+
+ DISALLOW_COPY_AND_ASSIGN(FuchsiaSecureStreamDecryptor);
+};
+
+} // namespace media
+
+#endif // MEDIA_FUCHSIA_CDM_FUCHSIA_STREAM_DECRYPTOR_H_
diff --git a/chromium/media/fuchsia/common/BUILD.gn b/chromium/media/fuchsia/common/BUILD.gn
new file mode 100644
index 00000000000..578edf6eacd
--- /dev/null
+++ b/chromium/media/fuchsia/common/BUILD.gn
@@ -0,0 +1,27 @@
+# Copyright 2019 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.
+
+assert(is_fuchsia)
+
+source_set("common") {
+ sources = [
+ "stream_processor_helper.cc",
+ "stream_processor_helper.h",
+ "sysmem_buffer_pool.cc",
+ "sysmem_buffer_pool.h",
+ "sysmem_buffer_reader.cc",
+ "sysmem_buffer_reader.h",
+ "sysmem_buffer_writer.cc",
+ "sysmem_buffer_writer.h",
+ "sysmem_buffer_writer_queue.cc",
+ "sysmem_buffer_writer_queue.h",
+ ]
+
+ deps = [
+ "//base",
+ "//media/base",
+ "//third_party/fuchsia-sdk/sdk:media",
+ "//third_party/fuchsia-sdk/sdk:sysmem",
+ ]
+}
diff --git a/chromium/media/fuchsia/common/stream_processor_helper.cc b/chromium/media/fuchsia/common/stream_processor_helper.cc
new file mode 100644
index 00000000000..a7415803541
--- /dev/null
+++ b/chromium/media/fuchsia/common/stream_processor_helper.cc
@@ -0,0 +1,393 @@
+// Copyright 2019 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/fuchsia/common/stream_processor_helper.h"
+
+#include "base/bind.h"
+#include "base/fuchsia/fuchsia_logging.h"
+
+namespace media {
+
+StreamProcessorHelper::IoPacket::IoPacket(size_t index,
+ size_t offset,
+ size_t size,
+ base::TimeDelta timestamp,
+ bool unit_end,
+ base::OnceClosure destroy_cb)
+ : index_(index),
+ offset_(offset),
+ size_(size),
+ timestamp_(timestamp),
+ unit_end_(unit_end),
+ destroy_cb_(std::move(destroy_cb)) {}
+
+StreamProcessorHelper::IoPacket::~IoPacket() = default;
+
+StreamProcessorHelper::IoPacket::IoPacket(IoPacket&&) = default;
+StreamProcessorHelper::IoPacket& StreamProcessorHelper::IoPacket::operator=(
+ IoPacket&&) = default;
+
+// static
+StreamProcessorHelper::IoPacket StreamProcessorHelper::IoPacket::CreateInput(
+ size_t index,
+ size_t size,
+ base::TimeDelta timestamp,
+ bool unit_end,
+ base::OnceClosure destroy_cb) {
+ return IoPacket(index, 0 /* offset */, size, timestamp, unit_end,
+ std::move(destroy_cb));
+}
+
+// static
+StreamProcessorHelper::IoPacket StreamProcessorHelper::IoPacket::CreateOutput(
+ size_t index,
+ size_t offset,
+ size_t size,
+ base::TimeDelta timestamp,
+ bool unit_end,
+ base::OnceClosure destroy_cb) {
+ return IoPacket(index, offset, size, timestamp, unit_end,
+ std::move(destroy_cb));
+}
+
+StreamProcessorHelper::StreamProcessorHelper(
+ fuchsia::media::StreamProcessorPtr processor,
+ Client* client)
+ : processor_(std::move(processor)), client_(client), weak_factory_(this) {
+ DCHECK(processor_);
+ DCHECK(client_);
+ weak_this_ = weak_factory_.GetWeakPtr();
+
+ processor_.set_error_handler(
+ [this](zx_status_t status) {
+ ZX_LOG(ERROR, status)
+ << "The fuchsia.media.StreamProcessor channel was terminated.";
+ OnError();
+ });
+
+ processor_.events().OnStreamFailed =
+ fit::bind_member(this, &StreamProcessorHelper::OnStreamFailed);
+ processor_.events().OnInputConstraints =
+ fit::bind_member(this, &StreamProcessorHelper::OnInputConstraints);
+ processor_.events().OnFreeInputPacket =
+ fit::bind_member(this, &StreamProcessorHelper::OnFreeInputPacket);
+ processor_.events().OnOutputConstraints =
+ fit::bind_member(this, &StreamProcessorHelper::OnOutputConstraints);
+ processor_.events().OnOutputFormat =
+ fit::bind_member(this, &StreamProcessorHelper::OnOutputFormat);
+ processor_.events().OnOutputPacket =
+ fit::bind_member(this, &StreamProcessorHelper::OnOutputPacket);
+ processor_.events().OnOutputEndOfStream =
+ fit::bind_member(this, &StreamProcessorHelper::OnOutputEndOfStream);
+
+ processor_->EnableOnStreamFailed();
+}
+
+StreamProcessorHelper::~StreamProcessorHelper() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
+
+void StreamProcessorHelper::Process(IoPacket input) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(processor_);
+
+ fuchsia::media::Packet packet;
+ packet.mutable_header()->set_buffer_lifetime_ordinal(
+ input_buffer_lifetime_ordinal_);
+ packet.mutable_header()->set_packet_index(input.index());
+ packet.set_buffer_index(packet.header().packet_index());
+ packet.set_timestamp_ish(input.timestamp().InNanoseconds());
+ packet.set_stream_lifetime_ordinal(stream_lifetime_ordinal_);
+ packet.set_start_offset(input.offset());
+ packet.set_valid_length_bytes(input.size());
+ packet.set_known_end_access_unit(input.unit_end());
+
+ active_stream_ = true;
+
+ if (!input.format().IsEmpty()) {
+ processor_->QueueInputFormatDetails(stream_lifetime_ordinal_,
+ fidl::Clone(input.format()));
+ }
+
+ DCHECK(input_packets_.find(input.index()) == input_packets_.end());
+ input_packets_.insert_or_assign(input.index(), std::move(input));
+ processor_->QueueInputPacket(std::move(packet));
+}
+
+void StreamProcessorHelper::ProcessEos() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(processor_);
+
+ active_stream_ = true;
+ processor_->QueueInputEndOfStream(stream_lifetime_ordinal_);
+ processor_->FlushEndOfStreamAndCloseStream(stream_lifetime_ordinal_);
+}
+
+void StreamProcessorHelper::Reset() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ if (!active_stream_) {
+ // Nothing to do if we don't have an active stream.
+ return;
+ }
+
+ if (processor_) {
+ processor_->CloseCurrentStream(stream_lifetime_ordinal_,
+ /*release_input_buffers=*/false,
+ /*release_output_buffers=*/false);
+ }
+
+ stream_lifetime_ordinal_ += 2;
+ active_stream_ = false;
+}
+
+void StreamProcessorHelper::OnStreamFailed(uint64_t stream_lifetime_ordinal,
+ fuchsia::media::StreamError error) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ if (stream_lifetime_ordinal_ != stream_lifetime_ordinal) {
+ return;
+ }
+
+ if (error == fuchsia::media::StreamError::DECRYPTOR_NO_KEY) {
+ // Always reset the stream since the current one has failed.
+ Reset();
+
+ client_->OnNoKey();
+ return;
+ }
+
+ OnError();
+}
+
+void StreamProcessorHelper::OnInputConstraints(
+ fuchsia::media::StreamBufferConstraints constraints) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ // Buffer lifetime ordinal is an odd number incremented by 2 for each buffer
+ // generation as required by StreamProcessor.
+ input_buffer_lifetime_ordinal_ += 2;
+
+ // Default settings are used in CompleteInputBuffersAllocation to finish
+ // StreamProcessor input buffers setup.
+ if (!constraints.has_default_settings() ||
+ !constraints.default_settings().has_packet_count_for_server() ||
+ !constraints.default_settings().has_packet_count_for_client()) {
+ DLOG(ERROR)
+ << "Received OnInputConstraints() with missing required fields.";
+ OnError();
+ return;
+ }
+
+ DCHECK(input_packets_.empty());
+ input_buffer_constraints_ = std::move(constraints);
+
+ client_->AllocateInputBuffers(input_buffer_constraints_);
+}
+
+void StreamProcessorHelper::OnFreeInputPacket(
+ fuchsia::media::PacketHeader free_input_packet) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ if (!free_input_packet.has_buffer_lifetime_ordinal() ||
+ !free_input_packet.has_packet_index()) {
+ DLOG(ERROR) << "Received OnFreeInputPacket() with missing required fields.";
+ OnError();
+ return;
+ }
+
+ if (free_input_packet.buffer_lifetime_ordinal() !=
+ input_buffer_lifetime_ordinal_) {
+ return;
+ }
+
+ auto it = input_packets_.find(free_input_packet.packet_index());
+ if (it == input_packets_.end()) {
+ DLOG(ERROR) << "Received OnFreeInputPacket() with invalid packet index.";
+ OnError();
+ return;
+ }
+
+ // The packet should be destroyed only after it's removed from
+ // |input_packets_|. Otherwise packet destruction observer may call Process()
+ // for the next packet while the current packet is still in |input_packets_|.
+ auto packet = std::move(it->second);
+ input_packets_.erase(it);
+}
+
+void StreamProcessorHelper::OnOutputConstraints(
+ fuchsia::media::StreamOutputConstraints output_constraints) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ if (!output_constraints.has_stream_lifetime_ordinal()) {
+ DLOG(ERROR)
+ << "Received OnOutputConstraints() with missing required fields.";
+ OnError();
+ return;
+ }
+
+ if (output_constraints.stream_lifetime_ordinal() !=
+ stream_lifetime_ordinal_) {
+ return;
+ }
+
+ if (!output_constraints.has_buffer_constraints_action_required() ||
+ !output_constraints.buffer_constraints_action_required()) {
+ return;
+ }
+
+ if (!output_constraints.has_buffer_constraints()) {
+ DLOG(ERROR) << "Received OnOutputConstraints() which requires buffer "
+ "constraints action, but without buffer constraints.";
+ OnError();
+ return;
+ }
+
+ // StreamProcessor API expects odd buffer lifetime ordinal, which is
+ // incremented by 2 for each buffer generation.
+ output_buffer_lifetime_ordinal_ += 2;
+
+ output_buffer_constraints_ =
+ std::move(*output_constraints.mutable_buffer_constraints());
+
+ client_->AllocateOutputBuffers(output_buffer_constraints_);
+}
+
+void StreamProcessorHelper::OnOutputFormat(
+ fuchsia::media::StreamOutputFormat output_format) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ if (!output_format.has_stream_lifetime_ordinal() ||
+ !output_format.has_format_details()) {
+ DLOG(ERROR) << "Received OnOutputFormat() with missing required fields.";
+ OnError();
+ return;
+ }
+
+ if (output_format.stream_lifetime_ordinal() != stream_lifetime_ordinal_) {
+ return;
+ }
+
+ client_->OnOutputFormat(std::move(output_format));
+}
+
+void StreamProcessorHelper::OnOutputPacket(fuchsia::media::Packet output_packet,
+ bool error_detected_before,
+ bool error_detected_during) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ if (!output_packet.has_header() ||
+ !output_packet.header().has_buffer_lifetime_ordinal() ||
+ !output_packet.header().has_packet_index() ||
+ !output_packet.has_stream_lifetime_ordinal() ||
+ !output_packet.has_buffer_index()) {
+ DLOG(ERROR) << "Received OnOutputPacket() with missing required fields.";
+ OnError();
+ return;
+ }
+
+ if (output_packet.header().buffer_lifetime_ordinal() !=
+ output_buffer_lifetime_ordinal_) {
+ return;
+ }
+
+ if (output_packet.stream_lifetime_ordinal() != stream_lifetime_ordinal_) {
+ // Output packets from old streams still need to be recycled.
+ OnRecycleOutputBuffer(output_buffer_lifetime_ordinal_,
+ output_packet.header().packet_index());
+ return;
+ }
+
+ auto buffer_index = output_packet.buffer_index();
+ auto packet_index = output_packet.header().packet_index();
+ base::TimeDelta timestamp;
+ if (output_packet.has_timestamp_ish()) {
+ timestamp = base::TimeDelta::FromNanoseconds(output_packet.timestamp_ish());
+ }
+
+ client_->OnOutputPacket(IoPacket::CreateOutput(
+ buffer_index, output_packet.start_offset(),
+ output_packet.valid_length_bytes(), timestamp,
+ output_packet.known_end_access_unit(),
+ base::BindOnce(&StreamProcessorHelper::OnRecycleOutputBuffer, weak_this_,
+ output_buffer_lifetime_ordinal_, packet_index)));
+}
+
+void StreamProcessorHelper::OnOutputEndOfStream(
+ uint64_t stream_lifetime_ordinal,
+ bool error_detected_before) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ if (stream_lifetime_ordinal != stream_lifetime_ordinal_) {
+ return;
+ }
+
+ stream_lifetime_ordinal_ += 2;
+ active_stream_ = false;
+
+ client_->OnProcessEos();
+}
+
+void StreamProcessorHelper::OnError() {
+ processor_.Unbind();
+ client_->OnError();
+}
+
+void StreamProcessorHelper::CompleteInputBuffersAllocation(
+ fuchsia::sysmem::BufferCollectionTokenPtr sysmem_token) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(!input_buffer_constraints_.IsEmpty());
+ fuchsia::media::StreamBufferPartialSettings settings;
+ settings.set_buffer_lifetime_ordinal(input_buffer_lifetime_ordinal_);
+ settings.set_buffer_constraints_version_ordinal(
+ input_buffer_constraints_.buffer_constraints_version_ordinal());
+ settings.set_single_buffer_mode(false);
+ settings.set_packet_count_for_server(
+ input_buffer_constraints_.default_settings().packet_count_for_server());
+ settings.set_packet_count_for_client(
+ input_buffer_constraints_.default_settings().packet_count_for_client());
+ settings.set_sysmem_token(std::move(sysmem_token));
+ processor_->SetInputBufferPartialSettings(std::move(settings));
+}
+
+void StreamProcessorHelper::CompleteOutputBuffersAllocation(
+ size_t num_buffers_for_client,
+ size_t num_buffers_for_server,
+ fuchsia::sysmem::BufferCollectionTokenPtr collection_token) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(!output_buffer_constraints_.IsEmpty());
+ DCHECK_LE(num_buffers_for_client,
+ output_buffer_constraints_.packet_count_for_client_max());
+
+ // Pass new output buffer settings to the stream processor.
+ fuchsia::media::StreamBufferPartialSettings settings;
+ settings.set_buffer_lifetime_ordinal(output_buffer_lifetime_ordinal_);
+ settings.set_buffer_constraints_version_ordinal(
+ output_buffer_constraints_.buffer_constraints_version_ordinal());
+ settings.set_packet_count_for_client(num_buffers_for_client);
+ settings.set_packet_count_for_server(num_buffers_for_server);
+ settings.set_sysmem_token(std::move(collection_token));
+ processor_->SetOutputBufferPartialSettings(std::move(settings));
+ processor_->CompleteOutputBufferPartialSettings(
+ output_buffer_lifetime_ordinal_);
+}
+
+void StreamProcessorHelper::OnRecycleOutputBuffer(
+ uint64_t buffer_lifetime_ordinal,
+ uint32_t packet_index) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ if (!processor_)
+ return;
+
+ if (buffer_lifetime_ordinal != output_buffer_lifetime_ordinal_)
+ return;
+
+ fuchsia::media::PacketHeader header;
+ header.set_buffer_lifetime_ordinal(buffer_lifetime_ordinal);
+ header.set_packet_index(packet_index);
+ processor_->RecycleOutputPacket(std::move(header));
+}
+
+} // namespace media
diff --git a/chromium/media/fuchsia/common/stream_processor_helper.h b/chromium/media/fuchsia/common/stream_processor_helper.h
new file mode 100644
index 00000000000..ed084c32645
--- /dev/null
+++ b/chromium/media/fuchsia/common/stream_processor_helper.h
@@ -0,0 +1,184 @@
+// Copyright 2019 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_FUCHSIA_COMMON_STREAM_PROCESSOR_HELPER_H_
+#define MEDIA_FUCHSIA_COMMON_STREAM_PROCESSOR_HELPER_H_
+
+#include <fuchsia/media/cpp/fidl.h>
+#include <fuchsia/sysmem/cpp/fidl.h>
+
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/containers/flat_map.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/time.h"
+
+namespace media {
+
+// Helper class of fuchsia::media::StreamProcessor. It's responsible for:
+// 1. Data validation check.
+// 2. Stream/Buffer life time management.
+// 3. Configure StreamProcessor and input/output buffer settings.
+class StreamProcessorHelper {
+ public:
+ class IoPacket {
+ public:
+ static IoPacket CreateInput(size_t index,
+ size_t size,
+ base::TimeDelta timestamp,
+ bool unit_end,
+ base::OnceClosure destroy_cb);
+
+ static IoPacket CreateOutput(size_t index,
+ size_t offset,
+ size_t size,
+ base::TimeDelta timestamp,
+ bool unit_end,
+ base::OnceClosure destroy_cb);
+
+ IoPacket(size_t index,
+ size_t offset,
+ size_t size,
+ base::TimeDelta timestamp,
+ bool unit_end,
+ base::OnceClosure destroy_cb);
+ ~IoPacket();
+
+ IoPacket(IoPacket&&);
+ IoPacket& operator=(IoPacket&&);
+
+ size_t index() const { return index_; }
+ size_t offset() const { return offset_; }
+ size_t size() const { return size_; }
+ base::TimeDelta timestamp() const { return timestamp_; }
+ bool unit_end() const { return unit_end_; }
+ void set_format(fuchsia::media::FormatDetails format) {
+ format_ = std::move(format);
+ }
+ const fuchsia::media::FormatDetails& format() const { return format_; }
+
+ private:
+ size_t index_;
+ size_t offset_;
+ size_t size_;
+ base::TimeDelta timestamp_;
+ bool unit_end_;
+ fuchsia::media::FormatDetails format_;
+ base::ScopedClosureRunner destroy_cb_;
+
+ DISALLOW_COPY_AND_ASSIGN(IoPacket);
+ };
+
+ class Client {
+ public:
+ // Allocate input/output buffers with the given constraints. Client should
+ // call ProvideInput/OutputBufferCollectionToken to finish the buffer
+ // allocation flow.
+ virtual void AllocateInputBuffers(
+ const fuchsia::media::StreamBufferConstraints& stream_constraints) = 0;
+ virtual void AllocateOutputBuffers(
+ const fuchsia::media::StreamBufferConstraints& stream_constraints) = 0;
+
+ // Called when all the pushed packets are processed.
+ virtual void OnProcessEos() = 0;
+
+ // Called when output format is available.
+ virtual void OnOutputFormat(fuchsia::media::StreamOutputFormat format) = 0;
+
+ // Called when output packet is available. Deleting |packet| will notify
+ // StreamProcessor the output buffer is available to be re-used. Client
+ // should delete |packet| on the same thread as this function.
+ virtual void OnOutputPacket(IoPacket packet) = 0;
+
+ // Only available for decryption, which indicates currently the
+ // StreamProcessor doesn't have the content key to process.
+ virtual void OnNoKey() = 0;
+
+ // Called when any fatal errors happens.
+ virtual void OnError() = 0;
+
+ protected:
+ virtual ~Client() = default;
+ };
+
+ StreamProcessorHelper(fuchsia::media::StreamProcessorPtr processor,
+ Client* client);
+ ~StreamProcessorHelper();
+
+ // Process one packet. Caller can reuse the underlying buffer when the
+ // |packet| is destroyed.
+ void Process(IoPacket packet);
+
+ // Push End-Of-Stream to StreamProcessor. No more data should be sent to
+ // StreamProcessor without calling Reset.
+ void ProcessEos();
+
+ // Provide input/output BufferCollectionToken to finish StreamProcessor buffer
+ // setup flow.
+ void CompleteInputBuffersAllocation(
+ fuchsia::sysmem::BufferCollectionTokenPtr token);
+ void CompleteOutputBuffersAllocation(
+ size_t num_buffers_for_client,
+ size_t num_buffers_for_server,
+ fuchsia::sysmem::BufferCollectionTokenPtr token);
+
+ void Reset();
+
+ private:
+ // Event handlers for |processor_|.
+ void OnStreamFailed(uint64_t stream_lifetime_ordinal,
+ fuchsia::media::StreamError error);
+ void OnInputConstraints(
+ fuchsia::media::StreamBufferConstraints input_constraints);
+ void OnFreeInputPacket(fuchsia::media::PacketHeader free_input_packet);
+ void OnOutputConstraints(
+ fuchsia::media::StreamOutputConstraints output_constraints);
+ void OnOutputFormat(fuchsia::media::StreamOutputFormat output_format);
+ void OnOutputPacket(fuchsia::media::Packet output_packet,
+ bool error_detected_before,
+ bool error_detected_during);
+ void OnOutputEndOfStream(uint64_t stream_lifetime_ordinal,
+ bool error_detected_before);
+
+ void OnError();
+
+ void OnRecycleOutputBuffer(uint64_t buffer_lifetime_ordinal,
+ uint32_t packet_index);
+
+ uint64_t stream_lifetime_ordinal_ = 1;
+
+ // Set to true if we've sent an input packet with the current
+ // stream_lifetime_ordinal_.
+ bool active_stream_ = false;
+
+ // Input buffers.
+ uint64_t input_buffer_lifetime_ordinal_ = 1;
+ fuchsia::media::StreamBufferConstraints input_buffer_constraints_;
+
+ // Map from packet index to corresponding input IoPacket. IoPacket should be
+ // owned by this class until StreamProcessor released the buffer.
+ base::flat_map<size_t, IoPacket> input_packets_;
+
+ // Output buffers.
+ uint64_t output_buffer_lifetime_ordinal_ = 1;
+ fuchsia::media::StreamBufferConstraints output_buffer_constraints_;
+
+ fuchsia::media::StreamProcessorPtr processor_;
+ Client* const client_;
+
+ // FIDL interfaces are thread-affine (see crbug.com/1012875).
+ THREAD_CHECKER(thread_checker_);
+
+ base::WeakPtr<StreamProcessorHelper> weak_this_;
+ base::WeakPtrFactory<StreamProcessorHelper> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(StreamProcessorHelper);
+};
+
+} // namespace media
+
+#endif // MEDIA_FUCHSIA_COMMON_STREAM_PROCESSOR_HELPER_H_
diff --git a/chromium/media/fuchsia/common/sysmem_buffer_pool.cc b/chromium/media/fuchsia/common/sysmem_buffer_pool.cc
new file mode 100644
index 00000000000..6c30ad8a0df
--- /dev/null
+++ b/chromium/media/fuchsia/common/sysmem_buffer_pool.cc
@@ -0,0 +1,169 @@
+// Copyright 2019 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/fuchsia/common/sysmem_buffer_pool.h"
+
+#include <zircon/rights.h>
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/fuchsia/default_context.h"
+#include "base/fuchsia/fuchsia_logging.h"
+#include "media/fuchsia/common/sysmem_buffer_reader.h"
+#include "media/fuchsia/common/sysmem_buffer_writer.h"
+
+namespace media {
+
+SysmemBufferPool::Creator::Creator(
+ fuchsia::sysmem::BufferCollectionPtr collection,
+ std::vector<fuchsia::sysmem::BufferCollectionTokenPtr> shared_tokens)
+ : collection_(std::move(collection)),
+ shared_tokens_(std::move(shared_tokens)) {
+ collection_.set_error_handler(
+ [this](zx_status_t status) {
+ ZX_DLOG(ERROR, status)
+ << "Connection to BufferCollection was disconnected.";
+ collection_.Unbind();
+
+ if (create_cb_)
+ std::move(create_cb_).Run(nullptr);
+ });
+}
+
+SysmemBufferPool::Creator::~Creator() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
+
+void SysmemBufferPool::Creator::Create(
+ fuchsia::sysmem::BufferCollectionConstraints constraints,
+ CreateCB create_cb) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(!create_cb_);
+ create_cb_ = std::move(create_cb);
+ // BufferCollection needs to be synchronized to ensure that all token
+ // duplicate requests have been processed and sysmem knows about all clients
+ // that will be using this buffer collection.
+ collection_->Sync([this, constraints = std::move(constraints)]() mutable {
+ collection_->SetConstraints(true /* has constraints */,
+ std::move(constraints));
+
+ DCHECK(create_cb_);
+ std::move(create_cb_)
+ .Run(std::make_unique<SysmemBufferPool>(std::move(collection_),
+ std::move(shared_tokens_)));
+ });
+}
+
+SysmemBufferPool::SysmemBufferPool(
+ fuchsia::sysmem::BufferCollectionPtr collection,
+ std::vector<fuchsia::sysmem::BufferCollectionTokenPtr> shared_tokens)
+ : collection_(std::move(collection)),
+ shared_tokens_(std::move(shared_tokens)) {
+ collection_.set_error_handler([this](zx_status_t status) {
+ ZX_LOG(ERROR, status) << "fuchsia.sysmem.BufferCollection disconnected.";
+ OnError();
+ });
+}
+
+SysmemBufferPool::~SysmemBufferPool() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ if (collection_)
+ collection_->Close();
+}
+
+fuchsia::sysmem::BufferCollectionTokenPtr SysmemBufferPool::TakeToken() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(!shared_tokens_.empty());
+ auto token = std::move(shared_tokens_.back());
+ shared_tokens_.pop_back();
+ return token;
+}
+
+void SysmemBufferPool::CreateReader(CreateReaderCB create_cb) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(!create_reader_cb_);
+ create_reader_cb_ = std::move(create_cb);
+ collection_->WaitForBuffersAllocated(
+ fit::bind_member(this, &SysmemBufferPool::OnBuffersAllocated));
+}
+
+void SysmemBufferPool::CreateWriter(CreateWriterCB create_cb) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(!create_writer_cb_);
+ create_writer_cb_ = std::move(create_cb);
+ collection_->WaitForBuffersAllocated(
+ fit::bind_member(this, &SysmemBufferPool::OnBuffersAllocated));
+}
+
+void SysmemBufferPool::OnBuffersAllocated(
+ zx_status_t status,
+ fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection_info) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ if (status != ZX_OK) {
+ ZX_LOG(ERROR, status) << "Fail to allocate sysmem buffers.";
+ OnError();
+ return;
+ }
+
+ if (create_reader_cb_) {
+ std::move(create_reader_cb_)
+ .Run(SysmemBufferReader::Create(std::move(buffer_collection_info)));
+ } else if (create_writer_cb_) {
+ std::move(create_writer_cb_)
+ .Run(SysmemBufferWriter::Create(std::move(buffer_collection_info)));
+ }
+}
+
+void SysmemBufferPool::OnError() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ collection_.Unbind();
+ if (create_reader_cb_)
+ std::move(create_reader_cb_).Run(nullptr);
+ if (create_writer_cb_)
+ std::move(create_writer_cb_).Run(nullptr);
+}
+
+BufferAllocator::BufferAllocator() {
+ allocator_ = base::fuchsia::ComponentContextForCurrentProcess()
+ ->svc()
+ ->Connect<fuchsia::sysmem::Allocator>();
+
+ allocator_.set_error_handler([](zx_status_t status) {
+ // Just log a warning. We will handle BufferCollection the failure when
+ // trying to create a new BufferCollection.
+ ZX_DLOG(WARNING, status)
+ << "The fuchsia.sysmem.Allocator channel was disconnected.";
+ });
+}
+
+BufferAllocator::~BufferAllocator() = default;
+
+std::unique_ptr<SysmemBufferPool::Creator>
+BufferAllocator::MakeBufferPoolCreator(size_t num_of_tokens) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ // Create a new sysmem buffer collection token for the allocated buffers.
+ fuchsia::sysmem::BufferCollectionTokenPtr collection_token;
+ allocator_->AllocateSharedCollection(collection_token.NewRequest());
+
+ // Create collection token for sharing with other components.
+ std::vector<fuchsia::sysmem::BufferCollectionTokenPtr> shared_tokens;
+ for (size_t i = 0; i < num_of_tokens; i++) {
+ fuchsia::sysmem::BufferCollectionTokenPtr token;
+ collection_token->Duplicate(ZX_RIGHT_SAME_RIGHTS, token.NewRequest());
+ shared_tokens.push_back(std::move(token));
+ }
+
+ fuchsia::sysmem::BufferCollectionPtr buffer_collection;
+
+ // Convert the token to a BufferCollection connection.
+ allocator_->BindSharedCollection(std::move(collection_token),
+ buffer_collection.NewRequest());
+
+ return std::make_unique<SysmemBufferPool::Creator>(
+ std::move(buffer_collection), std::move(shared_tokens));
+}
+
+} // namespace media
diff --git a/chromium/media/fuchsia/common/sysmem_buffer_pool.h b/chromium/media/fuchsia/common/sysmem_buffer_pool.h
new file mode 100644
index 00000000000..c4c6ceabb54
--- /dev/null
+++ b/chromium/media/fuchsia/common/sysmem_buffer_pool.h
@@ -0,0 +1,118 @@
+// Copyright 2019 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_FUCHSIA_COMMON_SYSMEM_BUFFER_POOL_H_
+#define MEDIA_FUCHSIA_COMMON_SYSMEM_BUFFER_POOL_H_
+
+#include <fuchsia/media/cpp/fidl.h>
+#include <fuchsia/sysmem/cpp/fidl.h>
+#include <lib/sys/cpp/component_context.h>
+
+#include <list>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/containers/span.h"
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+
+namespace media {
+
+class SysmemBufferReader;
+class SysmemBufferWriter;
+
+// Pool of buffers allocated by sysmem. It owns BufferCollection. It doesn't
+// provide any function read/write the buffers. Call should use
+// ReadableBufferPool/WritableBufferPool for read/write.
+class SysmemBufferPool {
+ public:
+ using CreateReaderCB =
+ base::OnceCallback<void(std::unique_ptr<SysmemBufferReader>)>;
+ using CreateWriterCB =
+ base::OnceCallback<void(std::unique_ptr<SysmemBufferWriter>)>;
+
+ // Creates SysmemBufferPool asynchronously. It also owns the channel to
+ // fuchsia services.
+ class Creator {
+ public:
+ using CreateCB =
+ base::OnceCallback<void(std::unique_ptr<SysmemBufferPool>)>;
+ Creator(
+ fuchsia::sysmem::BufferCollectionPtr collection,
+ std::vector<fuchsia::sysmem::BufferCollectionTokenPtr> shared_tokens);
+ ~Creator();
+
+ void Create(fuchsia::sysmem::BufferCollectionConstraints constraints,
+ CreateCB build_cb);
+
+ private:
+ fuchsia::sysmem::BufferCollectionPtr collection_;
+ std::vector<fuchsia::sysmem::BufferCollectionTokenPtr> shared_tokens_;
+ CreateCB create_cb_;
+
+ THREAD_CHECKER(thread_checker_);
+
+ DISALLOW_COPY_AND_ASSIGN(Creator);
+ };
+
+ SysmemBufferPool(
+ fuchsia::sysmem::BufferCollectionPtr collection,
+ std::vector<fuchsia::sysmem::BufferCollectionTokenPtr> shared_tokens);
+ ~SysmemBufferPool();
+
+ fuchsia::sysmem::BufferCollectionTokenPtr TakeToken();
+
+ // Create Reader/Writer to access raw memory. The returned Reader/Writer is
+ // owned by SysmemBufferPool and lives as long as SysmemBufferPool.
+ void CreateReader(CreateReaderCB create_cb);
+ void CreateWriter(CreateWriterCB create_cb);
+
+ // Returns if this object is still usable. Caller must check this before
+ // calling SysmemBufferReader/Writer APIs.
+ bool is_live() const { return collection_.is_bound(); }
+
+ private:
+ friend class BufferAllocator;
+
+ void OnBuffersAllocated(
+ zx_status_t status,
+ fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection_info);
+ void OnError();
+
+ fuchsia::sysmem::BufferCollectionPtr collection_;
+ std::vector<fuchsia::sysmem::BufferCollectionTokenPtr> shared_tokens_;
+
+ CreateReaderCB create_reader_cb_;
+ CreateWriterCB create_writer_cb_;
+
+ // FIDL interfaces are thread-affine (see crbug.com/1012875).
+ THREAD_CHECKER(thread_checker_);
+
+ DISALLOW_COPY_AND_ASSIGN(SysmemBufferPool);
+};
+
+// Wrapper of sysmem Allocator.
+class BufferAllocator {
+ public:
+ BufferAllocator();
+ ~BufferAllocator();
+
+ std::unique_ptr<SysmemBufferPool::Creator> MakeBufferPoolCreator(
+ size_t num_shared_token);
+
+ // TODO(sergeyu): Update FuchsiaVideoDecoder to use SysmemBufferPool and
+ // remove this function.
+ fuchsia::sysmem::Allocator* raw() { return allocator_.get(); }
+
+ private:
+ fuchsia::sysmem::AllocatorPtr allocator_;
+
+ THREAD_CHECKER(thread_checker_);
+
+ DISALLOW_COPY_AND_ASSIGN(BufferAllocator);
+};
+
+} // namespace media
+
+#endif // MEDIA_FUCHSIA_COMMON_SYSMEM_BUFFER_POOL_H_
diff --git a/chromium/media/fuchsia/common/sysmem_buffer_reader.cc b/chromium/media/fuchsia/common/sysmem_buffer_reader.cc
new file mode 100644
index 00000000000..8667a877398
--- /dev/null
+++ b/chromium/media/fuchsia/common/sysmem_buffer_reader.cc
@@ -0,0 +1,56 @@
+// Copyright 2019 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/fuchsia/common/sysmem_buffer_reader.h"
+
+#include "base/fuchsia/fuchsia_logging.h"
+
+namespace media {
+
+SysmemBufferReader::SysmemBufferReader(
+ fuchsia::sysmem::BufferCollectionInfo_2 info)
+ : collection_info_(std::move(info)) {}
+
+SysmemBufferReader::~SysmemBufferReader() = default;
+
+bool SysmemBufferReader::Read(size_t index,
+ size_t offset,
+ base::span<uint8_t> data) {
+ DCHECK_LT(index, collection_info_.buffer_count);
+ const fuchsia::sysmem::BufferMemorySettings& settings =
+ collection_info_.settings.buffer_settings;
+ fuchsia::sysmem::VmoBuffer& buffer = collection_info_.buffers[index];
+ DCHECK_LE(buffer.vmo_usable_start + offset + data.size(),
+ settings.size_bytes);
+
+ size_t vmo_offset = buffer.vmo_usable_start + offset;
+
+ // Invalidate cache.
+ if (settings.coherency_domain == fuchsia::sysmem::CoherencyDomain::RAM) {
+ zx_status_t status = buffer.vmo.op_range(
+ ZX_VMO_OP_CACHE_CLEAN_INVALIDATE, vmo_offset, data.size(), nullptr, 0);
+ ZX_LOG_IF(ERROR, status != ZX_OK, status) << "Fail to invalidate cache";
+ }
+
+ zx_status_t status = buffer.vmo.read(data.data(), vmo_offset, data.size());
+ ZX_LOG_IF(ERROR, status != ZX_OK, status) << "Fail to read";
+ return status == ZX_OK;
+}
+
+// static
+std::unique_ptr<SysmemBufferReader> SysmemBufferReader::Create(
+ fuchsia::sysmem::BufferCollectionInfo_2 info) {
+ return std::make_unique<SysmemBufferReader>(std::move(info));
+}
+
+// static
+fuchsia::sysmem::BufferCollectionConstraints
+SysmemBufferReader::GetRecommendedConstraints(size_t max_used_output_frames) {
+ fuchsia::sysmem::BufferCollectionConstraints buffer_constraints;
+ buffer_constraints.usage.cpu = fuchsia::sysmem::cpuUsageRead;
+ buffer_constraints.min_buffer_count_for_camping = max_used_output_frames;
+ return buffer_constraints;
+}
+
+} // namespace media
diff --git a/chromium/media/fuchsia/common/sysmem_buffer_reader.h b/chromium/media/fuchsia/common/sysmem_buffer_reader.h
new file mode 100644
index 00000000000..0c314162e68
--- /dev/null
+++ b/chromium/media/fuchsia/common/sysmem_buffer_reader.h
@@ -0,0 +1,40 @@
+// Copyright 2019 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_FUCHSIA_COMMON_SYSMEM_BUFFER_READER_H_
+#define MEDIA_FUCHSIA_COMMON_SYSMEM_BUFFER_READER_H_
+
+#include <fuchsia/media/cpp/fidl.h>
+#include <fuchsia/sysmem/cpp/fidl.h>
+
+#include <memory>
+
+#include "base/containers/span.h"
+
+namespace media {
+
+// Helper class to read content from fuchsia::sysmem::BufferCollection.
+class SysmemBufferReader {
+ public:
+ static fuchsia::sysmem::BufferCollectionConstraints GetRecommendedConstraints(
+ size_t max_used_output_frames);
+
+ static std::unique_ptr<SysmemBufferReader> Create(
+ fuchsia::sysmem::BufferCollectionInfo_2 info);
+
+ explicit SysmemBufferReader(fuchsia::sysmem::BufferCollectionInfo_2 info);
+ ~SysmemBufferReader();
+
+ // Read the buffer content at |index| into |data|, starting from |offset|.
+ bool Read(size_t index, size_t offset, base::span<uint8_t> data);
+
+ private:
+ fuchsia::sysmem::BufferCollectionInfo_2 collection_info_;
+
+ DISALLOW_COPY_AND_ASSIGN(SysmemBufferReader);
+};
+
+} // namespace media
+
+#endif // MEDIA_FUCHSIA_COMMON_SYSMEM_BUFFER_READER_H_
diff --git a/chromium/media/fuchsia/common/sysmem_buffer_writer.cc b/chromium/media/fuchsia/common/sysmem_buffer_writer.cc
new file mode 100644
index 00000000000..addfafb6e94
--- /dev/null
+++ b/chromium/media/fuchsia/common/sysmem_buffer_writer.cc
@@ -0,0 +1,190 @@
+// Copyright 2019 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/fuchsia/common/sysmem_buffer_writer.h"
+
+#include <zircon/rights.h>
+#include <algorithm>
+
+#include "base/bits.h"
+#include "base/fuchsia/fuchsia_logging.h"
+#include "base/process/process_metrics.h"
+
+namespace media {
+
+class SysmemBufferWriter::Buffer {
+ public:
+ Buffer() = default;
+
+ ~Buffer() {
+ if (!base_address_) {
+ return;
+ }
+
+ size_t mapped_bytes =
+ base::bits::Align(offset_ + size_, base::GetPageSize());
+ zx_status_t status = zx::vmar::root_self()->unmap(
+ reinterpret_cast<uintptr_t>(base_address_), mapped_bytes);
+ ZX_DCHECK(status == ZX_OK, status) << "zx_vmar_unmap";
+ }
+
+ Buffer(Buffer&&) = default;
+ Buffer& operator=(Buffer&&) = default;
+
+ bool Initialize(zx::vmo vmo,
+ size_t offset,
+ size_t size,
+ fuchsia::sysmem::CoherencyDomain coherency_domain) {
+ DCHECK(!base_address_);
+ DCHECK(vmo);
+
+ // zx_vmo_write() doesn't work for sysmem-allocated VMOs (see ZX-4854), so
+ // the VMOs have to be mapped.
+ size_t bytes_to_map = base::bits::Align(offset + size, base::GetPageSize());
+ uintptr_t addr;
+ zx_status_t status = zx::vmar::root_self()->map(
+ /*vmar_offset=*/0, vmo, /*vmo_offset=*/0, bytes_to_map,
+ ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, &addr);
+ if (status != ZX_OK) {
+ ZX_DLOG(ERROR, status) << "zx_vmar_map";
+ return false;
+ }
+
+ base_address_ = reinterpret_cast<uint8_t*>(addr);
+ offset_ = offset;
+ size_ = size;
+ coherency_domain_ = coherency_domain;
+
+ return true;
+ }
+
+ bool is_used() const { return is_used_; }
+ size_t size() const { return size_; }
+
+ // Copies as much data as possible from |data| to this input buffer.
+ size_t Write(base::span<const uint8_t> data) {
+ DCHECK(!is_used_);
+ is_used_ = true;
+
+ size_t bytes_to_fill = std::min(size_, data.size());
+ memcpy(base_address_ + offset_, data.data(), bytes_to_fill);
+
+ // Flush CPU cache if StreamProcessor reads from RAM.
+ if (coherency_domain_ == fuchsia::sysmem::CoherencyDomain::RAM) {
+ zx_status_t status = zx_cache_flush(base_address_ + offset_,
+ bytes_to_fill, ZX_CACHE_FLUSH_DATA);
+ ZX_DCHECK(status == ZX_OK, status) << "zx_cache_flush";
+ }
+
+ return bytes_to_fill;
+ }
+
+ void Release() { is_used_ = false; }
+
+ private:
+ uint8_t* base_address_ = nullptr;
+
+ // Buffer settings provided by sysmem.
+ size_t offset_ = 0;
+ size_t size_ = 0;
+ fuchsia::sysmem::CoherencyDomain coherency_domain_;
+
+ // Set to true when this buffer is being used by the codec.
+ bool is_used_ = false;
+};
+
+SysmemBufferWriter::SysmemBufferWriter(std::vector<Buffer> buffers)
+ : buffers_(std::move(buffers)) {}
+
+SysmemBufferWriter::~SysmemBufferWriter() = default;
+
+size_t SysmemBufferWriter::Write(size_t index, base::span<const uint8_t> data) {
+ DCHECK_LT(index, buffers_.size());
+ DCHECK(!buffers_[index].is_used());
+
+ return buffers_[index].Write(data);
+}
+
+base::Optional<size_t> SysmemBufferWriter::Acquire() {
+ auto it = std::find_if(
+ buffers_.begin(), buffers_.end(),
+ [](const SysmemBufferWriter::Buffer& buf) { return !buf.is_used(); });
+
+ if (it == buffers_.end())
+ return base::nullopt;
+
+ return it - buffers_.begin();
+}
+
+void SysmemBufferWriter::Release(size_t index) {
+ DCHECK_LT(index, buffers_.size());
+ buffers_[index].Release();
+}
+
+void SysmemBufferWriter::ReleaseAll() {
+ for (auto& buf : buffers_) {
+ buf.Release();
+ }
+}
+
+size_t SysmemBufferWriter::num_buffers() const {
+ return buffers_.size();
+}
+
+// static
+std::unique_ptr<SysmemBufferWriter> SysmemBufferWriter::Create(
+ fuchsia::sysmem::BufferCollectionInfo_2 info) {
+ std::vector<SysmemBufferWriter::Buffer> buffers;
+ buffers.resize(info.buffer_count);
+
+ fuchsia::sysmem::BufferMemorySettings& settings =
+ info.settings.buffer_settings;
+ for (size_t i = 0; i < info.buffer_count; ++i) {
+ fuchsia::sysmem::VmoBuffer& buffer = info.buffers[i];
+ if (!buffers[i].Initialize(std::move(buffer.vmo), buffer.vmo_usable_start,
+ settings.size_bytes,
+ settings.coherency_domain)) {
+ return nullptr;
+ }
+ }
+
+ return std::make_unique<SysmemBufferWriter>(std::move(buffers));
+}
+
+// static
+base::Optional<fuchsia::sysmem::BufferCollectionConstraints>
+SysmemBufferWriter::GetRecommendedConstraints(
+ const fuchsia::media::StreamBufferConstraints& stream_constraints) {
+ fuchsia::sysmem::BufferCollectionConstraints buffer_constraints;
+
+ if (!stream_constraints.has_default_settings() ||
+ !stream_constraints.default_settings().has_packet_count_for_client()) {
+ DLOG(ERROR)
+ << "Received StreamBufferConstaints with missing required fields.";
+ return base::nullopt;
+ }
+
+ // Currently we have to map buffers VMOs to write to them (see ZX-4854) and
+ // memory cannot be mapped as write-only (see ZX-4872), so request RW access
+ // even though we will never need to read from these buffers.
+ buffer_constraints.usage.cpu =
+ fuchsia::sysmem::cpuUsageRead | fuchsia::sysmem::cpuUsageWrite;
+
+ buffer_constraints.min_buffer_count_for_camping =
+ stream_constraints.default_settings().packet_count_for_client();
+ buffer_constraints.has_buffer_memory_constraints = true;
+
+ const int kDefaultPacketSize = 512 * 1024;
+ buffer_constraints.buffer_memory_constraints.min_size_bytes =
+ stream_constraints.has_per_packet_buffer_bytes_recommended()
+ ? stream_constraints.per_packet_buffer_bytes_recommended()
+ : kDefaultPacketSize;
+
+ buffer_constraints.buffer_memory_constraints.ram_domain_supported = true;
+ buffer_constraints.buffer_memory_constraints.cpu_domain_supported = true;
+
+ return buffer_constraints;
+}
+
+} // namespace media
diff --git a/chromium/media/fuchsia/common/sysmem_buffer_writer.h b/chromium/media/fuchsia/common/sysmem_buffer_writer.h
new file mode 100644
index 00000000000..9aed936f2b2
--- /dev/null
+++ b/chromium/media/fuchsia/common/sysmem_buffer_writer.h
@@ -0,0 +1,59 @@
+// Copyright 2019 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_FUCHSIA_COMMON_SYSMEM_BUFFER_WRITER_H_
+#define MEDIA_FUCHSIA_COMMON_SYSMEM_BUFFER_WRITER_H_
+
+#include <fuchsia/media/cpp/fidl.h>
+#include <fuchsia/sysmem/cpp/fidl.h>
+
+#include <memory>
+
+#include "base/containers/span.h"
+#include "base/optional.h"
+
+namespace media {
+
+// Helper class to write content into fuchsia::sysmem::BufferCollection.
+class SysmemBufferWriter {
+ public:
+ class Buffer;
+
+ static base::Optional<fuchsia::sysmem::BufferCollectionConstraints>
+ GetRecommendedConstraints(
+ const fuchsia::media::StreamBufferConstraints& stream_constraints);
+
+ static std::unique_ptr<SysmemBufferWriter> Create(
+ fuchsia::sysmem::BufferCollectionInfo_2 info);
+
+ explicit SysmemBufferWriter(std::vector<Buffer> buffers);
+ ~SysmemBufferWriter();
+
+ // Write the content of |data| into buffer at |index|. Return num of bytes
+ // written into the buffer. Write a used buffer will fail. It will mark the
+ // buffer as "used".
+ size_t Write(size_t index, base::span<const uint8_t> data);
+
+ // Acquire unused buffer for write. If |min_size| is provided, the returned
+ // buffer will have available size larger than |min_size|. This will NOT
+ // mark the buffer as "used".
+ base::Optional<size_t> Acquire();
+
+ // Notify the pool buffer at |index| is free to write new data.
+ void Release(size_t index);
+
+ // Mark all buffers as unused.
+ void ReleaseAll();
+
+ size_t num_buffers() const;
+
+ private:
+ std::vector<Buffer> buffers_;
+
+ DISALLOW_COPY_AND_ASSIGN(SysmemBufferWriter);
+};
+
+} // namespace media
+
+#endif // MEDIA_FUCHSIA_COMMON_SYSMEM_BUFFER_WRITER_H_
diff --git a/chromium/media/fuchsia/common/sysmem_buffer_writer_queue.cc b/chromium/media/fuchsia/common/sysmem_buffer_writer_queue.cc
new file mode 100644
index 00000000000..79a75eff644
--- /dev/null
+++ b/chromium/media/fuchsia/common/sysmem_buffer_writer_queue.cc
@@ -0,0 +1,179 @@
+// Copyright 2019 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/fuchsia/common/sysmem_buffer_writer_queue.h"
+
+#include <zircon/rights.h>
+#include <algorithm>
+
+#include "base/bits.h"
+#include "base/fuchsia/fuchsia_logging.h"
+#include "base/process/process_metrics.h"
+#include "media/base/decoder_buffer.h"
+
+namespace media {
+
+struct SysmemBufferWriterQueue::PendingBuffer {
+ PendingBuffer(scoped_refptr<DecoderBuffer> buffer) : buffer(buffer) {
+ DCHECK(buffer);
+ }
+ ~PendingBuffer() = default;
+
+ PendingBuffer(PendingBuffer&& other) = default;
+ PendingBuffer& operator=(PendingBuffer&& other) = default;
+
+ const uint8_t* data() const { return buffer->data() + buffer_pos; }
+ size_t bytes_left() const { return buffer->data_size() - buffer_pos; }
+ void AdvanceCurrentPos(size_t bytes) {
+ DCHECK_LE(bytes, bytes_left());
+ buffer_pos += bytes;
+ }
+
+ scoped_refptr<DecoderBuffer> buffer;
+ size_t buffer_pos = 0;
+
+ // Set to true when the consumer has finished processing the buffer and it can
+ // be released.
+ bool is_complete = false;
+
+ // Index of the last buffer in the sysmem buffer collection that was used for
+ // this input buffer. Valid only when |bytes_left()==0|.
+ size_t tail_sysmem_buffer_index = 0;
+};
+
+SysmemBufferWriterQueue::SysmemBufferWriterQueue() = default;
+SysmemBufferWriterQueue::~SysmemBufferWriterQueue() = default;
+
+void SysmemBufferWriterQueue::EnqueueBuffer(
+ scoped_refptr<DecoderBuffer> buffer) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ pending_buffers_.push_back(PendingBuffer(buffer));
+ PumpPackets();
+}
+
+void SysmemBufferWriterQueue::Start(std::unique_ptr<SysmemBufferWriter> writer,
+ SendPacketCB send_packet_cb,
+ EndOfStreamCB end_of_stream_cb) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(!writer_);
+
+ writer_ = std::move(writer);
+ send_packet_cb_ = std::move(send_packet_cb);
+ end_of_stream_cb_ = std::move(end_of_stream_cb);
+
+ PumpPackets();
+}
+
+void SysmemBufferWriterQueue::PumpPackets() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ auto weak_this = weak_factory_.GetWeakPtr();
+
+ while (writer_ && !is_paused_ &&
+ input_queue_position_ < pending_buffers_.size()) {
+ PendingBuffer* current_buffer = &pending_buffers_[input_queue_position_];
+
+ if (current_buffer->buffer->end_of_stream()) {
+ pending_buffers_.pop_front();
+ end_of_stream_cb_.Run();
+ if (!weak_this)
+ return;
+ continue;
+ }
+
+ base::Optional<size_t> index_opt = writer_->Acquire();
+
+ if (!index_opt.has_value()) {
+ // No input buffer available.
+ return;
+ }
+
+ size_t sysmem_buffer_index = index_opt.value();
+
+ size_t bytes_filled = writer_->Write(
+ sysmem_buffer_index,
+ base::make_span(current_buffer->data(), current_buffer->bytes_left()));
+ current_buffer->AdvanceCurrentPos(bytes_filled);
+
+ bool buffer_end = current_buffer->bytes_left() == 0;
+
+ auto packet = StreamProcessorHelper::IoPacket::CreateInput(
+ sysmem_buffer_index, bytes_filled, current_buffer->buffer->timestamp(),
+ buffer_end,
+ base::BindOnce(&SysmemBufferWriterQueue::ReleaseBuffer,
+ weak_factory_.GetWeakPtr(), sysmem_buffer_index));
+
+ if (buffer_end) {
+ current_buffer->tail_sysmem_buffer_index = sysmem_buffer_index;
+ input_queue_position_ += 1;
+ }
+
+ send_packet_cb_.Run(current_buffer->buffer.get(), std::move(packet));
+ if (!weak_this)
+ return;
+ }
+}
+
+void SysmemBufferWriterQueue::ResetQueue() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ pending_buffers_.clear();
+ input_queue_position_ = 0;
+ is_paused_ = false;
+}
+
+void SysmemBufferWriterQueue::ResetBuffers() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ writer_.reset();
+ send_packet_cb_ = SendPacketCB();
+ end_of_stream_cb_ = EndOfStreamCB();
+}
+
+void SysmemBufferWriterQueue::ResetPositionAndPause() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ for (auto& buffer : pending_buffers_) {
+ buffer.buffer_pos = 0;
+ buffer.is_complete = false;
+ }
+ input_queue_position_ = 0;
+ is_paused_ = true;
+}
+
+void SysmemBufferWriterQueue::Unpause() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(is_paused_);
+ is_paused_ = false;
+ PumpPackets();
+}
+
+void SysmemBufferWriterQueue::ReleaseBuffer(size_t buffer_index) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(writer_);
+
+ // Mark the input buffer as complete.
+ for (size_t i = 0; i < input_queue_position_; ++i) {
+ if (pending_buffers_[i].tail_sysmem_buffer_index == buffer_index)
+ pending_buffers_[i].is_complete = true;
+ }
+
+ // Remove all complete buffers from the head of the queue since we no longer
+ // need them. Note that currently StreamProcessor doesn't guarantee that input
+ // buffers are released in the same order they were sent (see
+ // https://fuchsia.googlesource.com/fuchsia/+/3b12c8c5/sdk/fidl/fuchsia.media/stream_processor.fidl#1646
+ // ). This means that some complete buffers will need to stay in the queue
+ // until all preceding packets are released as well.
+ while (!pending_buffers_.empty() && pending_buffers_.front().is_complete) {
+ pending_buffers_.pop_front();
+ DCHECK_GT(input_queue_position_, 0U);
+ input_queue_position_--;
+ }
+
+ writer_->Release(buffer_index);
+ PumpPackets();
+}
+
+size_t SysmemBufferWriterQueue::num_buffers() const {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ return writer_ ? writer_->num_buffers() : 0;
+}
+
+} // namespace media
diff --git a/chromium/media/fuchsia/common/sysmem_buffer_writer_queue.h b/chromium/media/fuchsia/common/sysmem_buffer_writer_queue.h
new file mode 100644
index 00000000000..b3d7d0e03db
--- /dev/null
+++ b/chromium/media/fuchsia/common/sysmem_buffer_writer_queue.h
@@ -0,0 +1,108 @@
+// Copyright 2019 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_FUCHSIA_COMMON_SYSMEM_BUFFER_WRITER_QUEUE_H_
+#define MEDIA_FUCHSIA_COMMON_SYSMEM_BUFFER_WRITER_QUEUE_H_
+
+#include <fuchsia/media/cpp/fidl.h>
+#include <fuchsia/sysmem/cpp/fidl.h>
+
+#include <memory>
+
+#include "base/containers/span.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/threading/thread_checker.h"
+#include "media/fuchsia/common/stream_processor_helper.h"
+#include "media/fuchsia/common/sysmem_buffer_writer.h"
+
+namespace media {
+
+class DecoderBuffer;
+
+// A SysmemBufferWriter wrapper that keeps a queue of pending DecodeBuffers,
+// writes them to sysmem buffers and generates StreamProcessor packets.
+class SysmemBufferWriterQueue {
+ public:
+ // Callback passed to StartSender(). |buffer| corresponds to the original
+ // buffer from which the |packet| was generated.
+ using SendPacketCB =
+ base::RepeatingCallback<void(const DecoderBuffer* buffer,
+ StreamProcessorHelper::IoPacket packet)>;
+
+ // Called when processing DecoderBuffer that's marked as end-of-stream.
+ using EndOfStreamCB = base::RepeatingClosure;
+
+ SysmemBufferWriterQueue();
+ ~SysmemBufferWriterQueue();
+
+ // Enqueues buffer to the queue.
+ void EnqueueBuffer(scoped_refptr<DecoderBuffer> buffer);
+
+ // Sets the buffer writer to use and starts sending outgoing packets using
+ // |send_packet_cb|. |end_of_stream_cb| will be called when processing each
+ // end-of-stream buffer.
+ void Start(std::unique_ptr<SysmemBufferWriter> writer,
+ SendPacketCB send_packet_cb,
+ EndOfStreamCB end_of_stream_cb);
+
+ // Resets all pending buffers. Keeps the underlying sysmem buffers.
+ void ResetQueue();
+
+ // Resets the buffers. Keeps the current pending buffers, so they will still
+ // be sent once the new collection is allocated and passed to Start().
+ void ResetBuffers();
+
+ // Resets pending queue position to the start of the queue and pauses the
+ // writer. All pending buffers will be resent when Unpause() is
+ // called.
+ void ResetPositionAndPause();
+
+ // Normally this should be called after restarting a stream in a
+ // StreamProcessor.
+ void Unpause();
+
+ // Number of buffers in the sysmem collection or 0 if sysmem buffers has not
+ // been allocated (i.e. before Start()).
+ size_t num_buffers() const;
+
+ private:
+ struct PendingBuffer;
+ class SysmemBuffer;
+
+ // Pumps pending buffers to SendPacketCB.
+ void PumpPackets();
+
+ // Callback called when a packet is destroyed. It marks the buffer as unused
+ // and tries to reuse it for other buffers if any.
+ void ReleaseBuffer(size_t buffer_index);
+
+ // Buffers that are waiting to be sent. A buffer is removed from the queue
+ // when it and all previous buffers have finished decoding.
+ std::deque<PendingBuffer> pending_buffers_;
+
+ // Position of the current buffer in |pending_buffers_|.
+ size_t input_queue_position_ = 0;
+
+ // Indicates that the stream is paused and no packets should be sent until
+ // Unpause() is called.
+ bool is_paused_ = false;
+
+ // Buffers for sysmem buffer collection. Not set until Start() is called.
+ std::unique_ptr<SysmemBufferWriter> writer_;
+
+ SendPacketCB send_packet_cb_;
+ EndOfStreamCB end_of_stream_cb_;
+
+ // FIDL interfaces are thread-affine (see crbug.com/1012875).
+ THREAD_CHECKER(thread_checker_);
+
+ base::WeakPtrFactory<SysmemBufferWriterQueue> weak_factory_{this};
+
+ DISALLOW_COPY_AND_ASSIGN(SysmemBufferWriterQueue);
+};
+
+} // namespace media
+
+#endif // MEDIA_FUCHSIA_COMMON_SYSMEM_BUFFER_WRITER_QUEUE_H_