Commit 93253402 authored by Sergey Ulanov's avatar Sergey Ulanov Committed by Commit Bot

[Fuchsia] Fix FuchsiaSecureStreamDecryptor to handle unencrypted frames

Encrypted media streams may contain unencrypted flames. That case wasn't
handled correctly in FuchsiaSecureStreamDecryptor. Now it generates
input packet with a single subsample entry with encrypted_bytes = 0.

fuchsia.media.drm API currently needs key_id even for clear frames,
see fxb/38253. To workaround this bug FuchsiaSecureStreamDecryptor now
stores a key_id from the last OnNewKey() and then sets that key_id in
the FormatDetails for clear frames.

Bug: 1012525, b/141148085
Change-Id: I185e3a89086a3613b383b9ef75e59ef5e151cb75
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1846323
Commit-Queue: Sergey Ulanov <sergeyu@chromium.org>
Reviewed-by: default avatarDavid Dorwin <ddorwin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#704874}
parent 9a1716a9
......@@ -115,7 +115,7 @@ class FuchsiaCdm::CdmSession {
base::OnceCallback<void(base::Optional<CdmPromise::Exception>)>;
CdmSession(const FuchsiaCdm::SessionCallbacks* callbacks,
base::RepeatingClosure on_new_key)
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.
......@@ -184,11 +184,21 @@ 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));
}
......@@ -197,7 +207,7 @@ class FuchsiaCdm::CdmSession {
session_id_, has_additional_usable_key, std::move(keys_info));
if (has_additional_usable_key)
on_new_key_.Run();
on_new_key_.Run(new_key_id);
}
void OnSessionError(zx_status_t status) {
......@@ -216,7 +226,7 @@ class FuchsiaCdm::CdmSession {
}
const SessionCallbacks* const session_callbacks_;
base::RepeatingClosure on_new_key_;
FuchsiaSecureStreamDecryptor::NewKeyCB on_new_key_;
fuchsia::media::drm::LicenseSessionPtr session_;
std::string session_id_;
......@@ -457,12 +467,12 @@ FuchsiaCdmContext* FuchsiaCdm::GetFuchsiaCdmContext() {
return this;
}
void FuchsiaCdm::OnNewKey() {
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();
new_key_cb_for_video_.Run(key_id);
}
}
......
......@@ -87,7 +87,8 @@ class FuchsiaCdm : public ContentDecryptionModule,
uint32_t promise_id,
base::Optional<CdmPromise::Exception> exception);
void OnNewKey();
// 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_;
......@@ -98,7 +99,7 @@ class FuchsiaCdm : public ContentDecryptionModule,
FuchsiaDecryptor decryptor_;
base::Lock new_key_cb_for_video_lock_;
base::RepeatingClosure new_key_cb_for_video_
FuchsiaSecureStreamDecryptor::NewKeyCB new_key_cb_for_video_
GUARDED_BY(new_key_cb_for_video_lock_);
DISALLOW_COPY_AND_ASSIGN(FuchsiaCdm);
......
......@@ -39,8 +39,15 @@ std::string GetEncryptionMode(EncryptionMode mode) {
fuchsia::media::KeyId GetKeyId(const std::string& key_id) {
fuchsia::media::KeyId fuchsia_key_id;
DCHECK_EQ(key_id.size(), fuchsia_key_id.data.size());
std::copy(key_id.begin(), key_id.end(), fuchsia_key_id.data.begin());
// |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;
}
......@@ -65,7 +72,32 @@ fuchsia::media::EncryptionPattern GetEncryptionPattern(
return fuchsia_pattern;
}
fuchsia::media::FormatDetails GetFormatDetails(const DecryptConfig* config) {
// 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;
......@@ -192,7 +224,12 @@ void FuchsiaStreamDecryptorBase::SendInputPacket(
return;
}
packet.set_format(GetFormatDetails(buffer->decrypt_config()));
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));
}
......@@ -457,7 +494,8 @@ void FuchsiaSecureStreamDecryptor::OnOutputPacket(
client_->OnDecryptorOutputPacket(std::move(packet));
}
base::RepeatingClosure FuchsiaSecureStreamDecryptor::GetOnNewKeyClosure() {
FuchsiaSecureStreamDecryptor::NewKeyCB
FuchsiaSecureStreamDecryptor::GetOnNewKeyClosure() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return BindToCurrentLoop(base::BindRepeating(
......@@ -493,9 +531,17 @@ void FuchsiaSecureStreamDecryptor::OnNoKey() {
client_->OnDecryptorNoKey();
}
void FuchsiaSecureStreamDecryptor::OnNewKey() {
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;
......
......@@ -40,6 +40,9 @@ class FuchsiaStreamDecryptorBase : public StreamProcessorHelper::Client {
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:
......@@ -115,6 +118,8 @@ class FuchsiaSecureStreamDecryptor : public FuchsiaStreamDecryptorBase {
virtual ~Client() = default;
};
using NewKeyCB = base::RepeatingCallback<void(const std::string& key_id)>;
FuchsiaSecureStreamDecryptor(fuchsia::media::StreamProcessorPtr processor,
Client* client);
~FuchsiaSecureStreamDecryptor() override;
......@@ -136,7 +141,7 @@ class FuchsiaSecureStreamDecryptor : public FuchsiaStreamDecryptorBase {
// 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.
base::RepeatingClosure GetOnNewKeyClosure();
NewKeyCB GetOnNewKeyClosure();
// Drops all pending decryption requests.
void Reset();
......@@ -152,7 +157,7 @@ class FuchsiaSecureStreamDecryptor : public FuchsiaStreamDecryptorBase {
// Callback returned by GetOnNewKeyClosure(). When waiting for a key this
// method unpauses the stream to decrypt any pending buffers.
void OnNewKey();
void OnNewKey(const std::string& key_id);
Client* const client_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment