Commit 0b35c5fd authored by Ted Meyer's avatar Ted Meyer Committed by Commit Bot

Encode InitializationVectors into VP9 video frames

Using a vector of subsamples and a starting initialization vector,
start incrementing the IV based on how large each video frame is. These
values are used by accelerators to support encrypted playback.

Since subsamples and frames can overlap, we have to cover multiple
scenarios:
frames & subsamples line up:
┌───────────────────┬────────────────────┐
│ frame 1           │ frame 2            │
┝━━━━━━━━━━━━━━━━━━━┿━━━━━━━━━━━━━━━━━━━━┥
│ clear1 | cipher 1 │ clear 2 | cipher 2 │
└───────────────────┴────────────────────┘

subsample clear covers all first frame and partial second frame:
┌───────────────────┬────────────────────┐
│ frame 1           │ frame 2            │
┝━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━┥
│ clear1                  | cipher 1     │
└────────────────────────────────────────┘

one frame has more than one subsample, and subsamples might not align:
┌─────────────────────────┬────────────────────┐
│ frame 1                 │ frame 2            │
┝━━━━━━━━━━━━━━━━━━━┯━━━━━┷━━━━━━━━━━━━━━━━━━━━┥
│ clear1 | cipher 1 │ clear 2       | cipher 2 │
└───────────────────┴──────────────────────────┘
or:
┌────────────────────────────────────────┬────────────────────┐
│ frame 1                                │ frame 2            │
┝━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━┿━━━━━━━━━━━━━━━━━━━━┥
│ clear1 | cipher 1 │ clear 2 | cipher 2 │ clear 3 | cipher 3 │
└───────────────────┴────────────────────┴────────────────────┘
or:
┌─────────────────────────────────────────────┬───────────────┐
│ frame 1                                     │ frame 2       │
┝━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━┯━━━━┷━━━━━━━━━━━━━━━┥
│ clear1 | cipher 1 │ clear 2 | cipher 2 │ clear 3 | cipher 3 │
└───────────────────┴────────────────────┴────────────────────┘

a cipher section crosses a frame boundary: (this is an error and is caught)
┌───────────────────┬────────────────────┐
│ frame 1           │ frame 2            │
┝━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━┥
│ clear1      | cipher 1                 │
└────────────────────────────────────────┘

Bug: 881922
Bug: 836557
Change-Id: I7e69be34221702f77ec67daa8647c1767661bb51
Reviewed-on: https://chromium-review.googlesource.com/c/1307059Reviewed-by: default avatarXiaohan Wang <xhwang@chromium.org>
Reviewed-by: default avatarRintaro Kuroiwa <rkuroiwa@chromium.org>
Commit-Queue: Ted Meyer <tmathmeyer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#611378}
parent 2e4facbd
......@@ -76,6 +76,13 @@ std::unique_ptr<DecryptConfig> DecryptConfig::Clone() const {
return base::WrapUnique(new DecryptConfig(*this));
}
std::unique_ptr<DecryptConfig> DecryptConfig::CopyNewSubsamplesIV(
const std::vector<SubsampleEntry>& subsamples,
const std::string& iv) {
return std::make_unique<DecryptConfig>(encryption_mode_, key_id_, iv,
subsamples, encryption_pattern_);
}
bool DecryptConfig::HasPattern() const {
return encryption_pattern_.has_value();
}
......
......@@ -74,6 +74,12 @@ class MEDIA_EXPORT DecryptConfig {
std::unique_ptr<DecryptConfig> Clone() const;
// Makes a new config which has the same configuration options (mode, pattern)
// while providing a new vector of subsamples and initialization vector.
std::unique_ptr<DecryptConfig> CopyNewSubsamplesIV(
const std::vector<SubsampleEntry>& subsamples,
const std::string& iv);
// Returns whether this config has EncryptionPattern set or not.
bool HasPattern() const;
......
This diff is collapsed.
......@@ -22,6 +22,7 @@
#include "base/containers/circular_deque.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "media/base/decrypt_config.h"
#include "media/base/media_export.h"
#include "media/base/video_color_space.h"
......@@ -368,16 +369,29 @@ class MEDIA_EXPORT Vp9Parser {
// |stream_size| in bytes. |stream| must point to the beginning of a single
// frame or a single superframe, is owned by caller and must remain valid
// until the next call to SetStream().
void SetStream(const uint8_t* stream, off_t stream_size);
void SetStream(const uint8_t* stream,
off_t stream_size,
std::unique_ptr<DecryptConfig> stream_config);
// Parse the next frame in the current stream buffer, filling |fhdr| with
// the parsed frame header and updating current segmentation and loop filter
// state.
// Also fills |frame_decrypt_config| _if_ the parser was set to use a super
// frame decrypt config.
// Return kOk if a frame has successfully been parsed,
// kEOStream if there is no more data in the current stream buffer,
// kAwaitingRefresh if this frame awaiting frame context update, or
// kInvalidStream on error.
Result ParseNextFrame(Vp9FrameHeader* fhdr);
Result ParseNextFrame(Vp9FrameHeader* fhdr,
std::unique_ptr<DecryptConfig>* frame_decrypt_config);
// Perform the same superframe parsing logic, but don't attempt to parse
// the normal frame headers afterwards, and then only return the decrypt
// config, since the frame itself isn't useful for the testing.
// Returns |true| if a frame would have been sent to |ParseUncompressedHeader|
// |false| if there was an error parsing the superframe.
std::unique_ptr<DecryptConfig> NextFrameDecryptContextForTesting();
std::string IncrementIVForTesting(const std::string& iv, uint32_t by);
// Return current parsing context.
const Context& context() const { return context_; }
......@@ -393,8 +407,12 @@ class MEDIA_EXPORT Vp9Parser {
private:
// Stores start pointer and size of each frame within the current superframe.
struct FrameInfo {
FrameInfo() = default;
FrameInfo();
FrameInfo(const FrameInfo& copy_from);
FrameInfo(const uint8_t* ptr, off_t size);
~FrameInfo();
FrameInfo& operator=(const FrameInfo& copy_from);
bool IsValid() const { return ptr != nullptr; }
void Reset() { ptr = nullptr; }
......@@ -403,6 +421,8 @@ class MEDIA_EXPORT Vp9Parser {
// Size of the frame in bytes.
off_t size = 0;
std::unique_ptr<DecryptConfig> decrypt_config;
};
base::circular_deque<FrameInfo> ParseSuperframe();
......@@ -439,6 +459,9 @@ class MEDIA_EXPORT Vp9Parser {
Context context_;
// Encrypted stream info.
std::unique_ptr<DecryptConfig> stream_decrypt_config_;
FrameInfo curr_frame_info_;
Vp9FrameHeader curr_frame_header_;
......
......@@ -36,9 +36,10 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// Parse until the end of stream/unsupported stream/error in stream is found.
while (ivf_parser.ParseNextFrame(&ivf_frame_header, &ivf_payload)) {
media::Vp9FrameHeader vp9_frame_header;
vp9_parser.SetStream(ivf_payload, ivf_frame_header.frame_size);
vp9_parser.SetStream(ivf_payload, ivf_frame_header.frame_size, nullptr);
// TODO(kcwu): further fuzzing the case of Vp9Parser::kAwaitingRefresh.
while (vp9_parser.ParseNextFrame(&vp9_frame_header) ==
std::unique_ptr<media::DecryptConfig> null_config;
while (vp9_parser.ParseNextFrame(&vp9_frame_header, &null_config) ==
media::Vp9Parser::kOk) {
// Repeat until all frames processed.
}
......
This diff is collapsed.
......@@ -34,15 +34,14 @@ void VP9Decoder::SetStream(int32_t id,
const DecryptConfig* decrypt_config) {
DCHECK(ptr);
DCHECK(size);
if (decrypt_config) {
NOTIMPLEMENTED();
state_ = kError;
return;
}
DVLOG(4) << "New input stream id: " << id << " at: " << (void*)ptr
<< " size: " << size;
stream_id_ = id;
parser_.SetStream(ptr, size);
if (decrypt_config) {
parser_.SetStream(ptr, size, decrypt_config->Clone());
} else {
parser_.SetStream(ptr, size, nullptr);
}
}
bool VP9Decoder::Flush() {
......@@ -65,9 +64,11 @@ void VP9Decoder::Reset() {
VP9Decoder::DecodeResult VP9Decoder::Decode() {
while (1) {
// Read a new frame header if one is not awaiting decoding already.
std::unique_ptr<DecryptConfig> decrypt_config;
if (!curr_frame_hdr_) {
std::unique_ptr<Vp9FrameHeader> hdr(new Vp9FrameHeader());
Vp9Parser::Result res = parser_.ParseNextFrame(hdr.get());
Vp9Parser::Result res =
parser_.ParseNextFrame(hdr.get(), &decrypt_config);
switch (res) {
case Vp9Parser::kOk:
curr_frame_hdr_ = std::move(hdr);
......@@ -179,6 +180,8 @@ VP9Decoder::DecodeResult VP9Decoder::Decode() {
pic->set_visible_rect(new_render_rect);
pic->set_bitstream_id(stream_id_);
pic->set_decrypt_config(std::move(decrypt_config));
// For VP9, container color spaces override video stream color spaces.
if (container_color_space_.IsSpecified()) {
pic->set_colorspace(container_color_space_);
......
......@@ -619,9 +619,10 @@ class VP9ConfigChangeDetector : public ConfigChangeDetector {
// Detects stream configuration changes.
// Returns false on failure.
bool DetectConfig(const uint8_t* stream, unsigned int size) override {
parser_.SetStream(stream, size);
parser_.SetStream(stream, size, nullptr);
Vp9FrameHeader fhdr;
while (parser_.ParseNextFrame(&fhdr) == Vp9Parser::kOk) {
std::unique_ptr<DecryptConfig> null_config;
while (parser_.ParseNextFrame(&fhdr, &null_config) == Vp9Parser::kOk) {
visible_rect_ = gfx::Rect(fhdr.render_width, fhdr.render_height);
color_space_ = fhdr.GetColorSpace();
......
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