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 { ...@@ -76,6 +76,13 @@ std::unique_ptr<DecryptConfig> DecryptConfig::Clone() const {
return base::WrapUnique(new DecryptConfig(*this)); 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 { bool DecryptConfig::HasPattern() const {
return encryption_pattern_.has_value(); return encryption_pattern_.has_value();
} }
......
...@@ -74,6 +74,12 @@ class MEDIA_EXPORT DecryptConfig { ...@@ -74,6 +74,12 @@ class MEDIA_EXPORT DecryptConfig {
std::unique_ptr<DecryptConfig> Clone() const; 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. // Returns whether this config has EncryptionPattern set or not.
bool HasPattern() const; bool HasPattern() const;
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/numerics/safe_conversions.h" #include "base/numerics/safe_conversions.h"
#include "base/sys_byteorder.h"
#include "media/filters/vp9_compressed_header_parser.h" #include "media/filters/vp9_compressed_header_parser.h"
#include "media/filters/vp9_uncompressed_header_parser.h" #include "media/filters/vp9_uncompressed_header_parser.h"
...@@ -148,6 +149,128 @@ int ClampLf(int lf) { ...@@ -148,6 +149,128 @@ int ClampLf(int lf) {
return std::min(std::max(0, lf), kMaxLoopFilterLevel); return std::min(std::max(0, lf), kMaxLoopFilterLevel);
} }
std::string IncrementIV(const std::string& iv, uint32_t by) {
// What we call the 'IV' value is actually somewhat of a misnomer:
// "IV" = 0xFFFFFFFFFFFFFFFF0000000000000000
// └──actual IV───┘└─block counter┘
// When we want to 'increment' this structure, we treat them both
// as big-endian 64 bit unsigned integers, then increment _only_ the
// block counter, then combine them back into a big-endian bytestring.
// |by| is usually going to be the number of blocks (aka 16 byte chunks)
// of cipher data.
DCHECK_EQ(iv.size(), 16u);
uint64_t integral_data[2];
memcpy(integral_data, reinterpret_cast<const uint8_t*>(iv.data()), 16);
uint64_t block_counter = base::NetToHost64(integral_data[1]) + by;
integral_data[1] = base::HostToNet64(block_counter);
uint8_t new_iv[16];
memcpy(new_iv, integral_data, 16);
return std::string(reinterpret_cast<char*>(new_iv), 16);
}
// |frame_size|: The size of the current frame; this controls how long we
// loop through the subsamples.
// |current_subsample_index|: An index into the |subsamples| vector, we need
// to have this saved between function calls.
// |extra_clear_bytes|: The previous call may have set this variable to show
// that a subsample mey have already started being parsed
// and that only X bytes of free data are left in it.
// |base_decrypt_config|: Not an output parameter, it is just a raw ptr from a
// unique_ptr.
// |subsamples|: A vector of subsamples.
// |iv|: The initialization vector (128bit number stored as std::string). This
// gets incremented by (cipher_bytes % 16) for each frame, and must be
// preserved across function calls.
std::unique_ptr<DecryptConfig> SplitSubsamples(
uint32_t frame_size,
size_t* current_subsample_index,
size_t* extra_clear_subsample_bytes,
DecryptConfig* base_decrypt_config,
const std::vector<SubsampleEntry>& subsamples,
std::string* iv) {
// We copy iv so that we can use the starting value in our
// new config while still incrementing IV for the next frame.
std::string frame_dc_iv = *iv;
std::vector<SubsampleEntry> frame_dc_subsamples;
do {
if (*current_subsample_index >= subsamples.size()) {
DVLOG(1) << "Not enough subsamples in the superframe decrypt config";
return nullptr;
}
uint32_t subsample_clear = subsamples[*current_subsample_index].clear_bytes;
uint32_t subsample_cipher =
subsamples[*current_subsample_index].cypher_bytes;
// if clear+cipher bytes would be over the max of uint32_t, we need to
// quit immediatly, to prevent malicious overflowing.
if (0xFFFFFFFF - subsample_clear < subsample_cipher) {
DVLOG(1) << "Invalid subsample alignment";
return nullptr;
}
// It's possible that the previous frame didn't use all the clear bytes
// in this subsample, in which case we have to start from midway through
// the clear section.
if (*extra_clear_subsample_bytes)
subsample_clear = *extra_clear_subsample_bytes;
if (subsample_clear > frame_size) {
// Support scenario where clear section is larger than our frame:
// The entire length is clear. If |subsample_clear| is the same length,
// we handle it below.
frame_dc_subsamples.push_back(SubsampleEntry(frame_size, 0));
*extra_clear_subsample_bytes = subsample_clear - frame_size;
frame_size = 0;
} else if (subsample_clear + subsample_cipher > frame_size) {
// Only a clear section can cross over a frame boundary, otherwise
// the frame header for the next frame would be encrypted, which is not
// spec compliant.
DVLOG(1) << "Invalid subsample alignment";
return nullptr;
} else if (subsample_clear + subsample_cipher <= frame_size) {
// In this case a subsample is less than or equal to a whole frame
// This is the most likely case for almost all encrypted media.
// note that |subsample_cipher| can be 0.
frame_dc_subsamples.push_back(
SubsampleEntry(subsample_clear, subsample_cipher));
frame_size -= (subsample_clear + subsample_cipher);
*extra_clear_subsample_bytes = 0;
// IV gets incremented by 1 for every 16 bytes of cypher
*iv = IncrementIV(*iv, subsample_cipher >> 4); // uint32 logical shift.
}
// Don't go to the next subsample if there are more clear bytes.
if (!*extra_clear_subsample_bytes)
(*current_subsample_index)++;
// It is possible for there to be more than one subsample associated
// with a single frame, so we need to try again if there are more bytes
// left unaccounted for in this frame.
} while (frame_size);
return base_decrypt_config->CopyNewSubsamplesIV(frame_dc_subsamples,
frame_dc_iv);
}
bool IsByteNEncrypted(off_t byte,
const std::vector<SubsampleEntry>& subsamples) {
off_t original_byte = byte;
for (const SubsampleEntry& subsample : subsamples) {
if (byte < 0)
return false;
if (static_cast<uint32_t>(byte) < subsample.clear_bytes)
return false;
byte -= subsample.clear_bytes;
if (static_cast<uint32_t>(byte) < subsample.cypher_bytes)
return true;
byte -= subsample.cypher_bytes;
}
DVLOG(3) << "Subsamples do not extend to cover offset " << original_byte;
return false;
}
} // namespace } // namespace
bool Vp9FrameHeader::IsKeyframe() const { bool Vp9FrameHeader::IsKeyframe() const {
...@@ -199,9 +322,29 @@ VideoColorSpace Vp9FrameHeader::GetColorSpace() const { ...@@ -199,9 +322,29 @@ VideoColorSpace Vp9FrameHeader::GetColorSpace() const {
return ret; return ret;
} }
Vp9Parser::FrameInfo::FrameInfo() = default;
Vp9Parser::FrameInfo::~FrameInfo() = default;
Vp9Parser::FrameInfo::FrameInfo(const uint8_t* ptr, off_t size) Vp9Parser::FrameInfo::FrameInfo(const uint8_t* ptr, off_t size)
: ptr(ptr), size(size) {} : ptr(ptr), size(size) {}
Vp9Parser::FrameInfo::FrameInfo(const FrameInfo& copy_from)
: ptr(copy_from.ptr),
size(copy_from.size),
decrypt_config(copy_from.decrypt_config
? copy_from.decrypt_config->Clone()
: nullptr) {}
Vp9Parser::FrameInfo& Vp9Parser::FrameInfo::operator=(
const FrameInfo& copy_from) {
this->ptr = copy_from.ptr;
this->size = copy_from.size;
this->decrypt_config =
copy_from.decrypt_config ? copy_from.decrypt_config->Clone() : nullptr;
return *this;
}
bool Vp9FrameContext::IsValid() const { bool Vp9FrameContext::IsValid() const {
// probs should be in [1, 255] range. // probs should be in [1, 255] range.
static_assert(sizeof(Vp9Prob) == 1, static_assert(sizeof(Vp9Prob) == 1,
...@@ -373,11 +516,14 @@ Vp9Parser::Vp9Parser(bool parsing_compressed_header) ...@@ -373,11 +516,14 @@ Vp9Parser::Vp9Parser(bool parsing_compressed_header)
Vp9Parser::~Vp9Parser() = default; Vp9Parser::~Vp9Parser() = default;
void Vp9Parser::SetStream(const uint8_t* stream, off_t stream_size) { void Vp9Parser::SetStream(const uint8_t* stream,
off_t stream_size,
std::unique_ptr<DecryptConfig> stream_config) {
DCHECK(stream); DCHECK(stream);
stream_ = stream; stream_ = stream;
bytes_left_ = stream_size; bytes_left_ = stream_size;
frames_.clear(); frames_.clear();
stream_decrypt_config_ = std::move(stream_config);
} }
void Vp9Parser::Reset() { void Vp9Parser::Reset() {
...@@ -475,7 +621,9 @@ bool Vp9Parser::ParseCompressedHeader(const FrameInfo& frame_info, ...@@ -475,7 +621,9 @@ bool Vp9Parser::ParseCompressedHeader(const FrameInfo& frame_info,
return false; return false;
} }
Vp9Parser::Result Vp9Parser::ParseNextFrame(Vp9FrameHeader* fhdr) { Vp9Parser::Result Vp9Parser::ParseNextFrame(
Vp9FrameHeader* fhdr,
std::unique_ptr<DecryptConfig>* frame_decrypt_config) {
DCHECK(fhdr); DCHECK(fhdr);
DVLOG(2) << "ParseNextFrame"; DVLOG(2) << "ParseNextFrame";
FrameInfo frame_info; FrameInfo frame_info;
...@@ -504,6 +652,11 @@ Vp9Parser::Result Vp9Parser::ParseNextFrame(Vp9FrameHeader* fhdr) { ...@@ -504,6 +652,11 @@ Vp9Parser::Result Vp9Parser::ParseNextFrame(Vp9FrameHeader* fhdr) {
frame_info = frames_.front(); frame_info = frames_.front();
frames_.pop_front(); frames_.pop_front();
if (frame_info.decrypt_config) {
*frame_decrypt_config = frame_info.decrypt_config->Clone();
} else {
*frame_decrypt_config = nullptr;
}
if (ParseUncompressedHeader(frame_info, fhdr, &result)) if (ParseUncompressedHeader(frame_info, fhdr, &result))
return result; return result;
...@@ -534,6 +687,27 @@ Vp9Parser::ContextRefreshCallback Vp9Parser::GetContextRefreshCb( ...@@ -534,6 +687,27 @@ Vp9Parser::ContextRefreshCallback Vp9Parser::GetContextRefreshCb(
return frame_context_manager.GetUpdateCb(); return frame_context_manager.GetUpdateCb();
} }
std::unique_ptr<DecryptConfig> Vp9Parser::NextFrameDecryptContextForTesting() {
if (frames_.empty()) {
// No frames to be decoded, if there is no more stream, request more.
if (!stream_)
return nullptr;
// New stream to be parsed, parse it and fill frames_.
frames_ = ParseSuperframe();
if (frames_.empty())
return nullptr;
}
FrameInfo frame_info = std::move(frames_.front());
frames_.pop_front();
return std::move(frame_info.decrypt_config);
}
std::string Vp9Parser::IncrementIVForTesting(const std::string& iv,
uint32_t by) {
return IncrementIV(iv, by);
}
// Annex B Superframes // Annex B Superframes
base::circular_deque<Vp9Parser::FrameInfo> Vp9Parser::ParseSuperframe() { base::circular_deque<Vp9Parser::FrameInfo> Vp9Parser::ParseSuperframe() {
const uint8_t* stream = stream_; const uint8_t* stream = stream_;
...@@ -543,14 +717,30 @@ base::circular_deque<Vp9Parser::FrameInfo> Vp9Parser::ParseSuperframe() { ...@@ -543,14 +717,30 @@ base::circular_deque<Vp9Parser::FrameInfo> Vp9Parser::ParseSuperframe() {
stream_ = nullptr; stream_ = nullptr;
bytes_left_ = 0; bytes_left_ = 0;
base::circular_deque<FrameInfo> frames;
if (bytes_left < 1) if (bytes_left < 1)
return base::circular_deque<FrameInfo>(); return frames;
// The marker byte might be encrypted, in which case we should treat
// the stream as a single frame.
off_t marker_offset = bytes_left - 1;
if (stream_decrypt_config_) {
if (IsByteNEncrypted(marker_offset, stream_decrypt_config_->subsamples())) {
frames.push_back(FrameInfo(stream, bytes_left));
frames[0].decrypt_config = stream_decrypt_config_->Clone();
return frames;
}
}
// If this is a superframe, the last byte in the stream will contain the // If this is a superframe, the last byte in the stream will contain the
// superframe marker. If not, the whole buffer contains a single frame. // superframe marker. If not, the whole buffer contains a single frame.
uint8_t marker = *(stream + bytes_left - 1); uint8_t marker = *(stream + marker_offset);
if ((marker & 0xe0) != 0xc0) { if ((marker & 0xe0) != 0xc0) {
return {FrameInfo(stream, bytes_left)}; frames.push_back(FrameInfo(stream, bytes_left));
if (stream_decrypt_config_)
frames[0].decrypt_config = stream_decrypt_config_->Clone();
return frames;
} }
DVLOG(1) << "Parsing a superframe"; DVLOG(1) << "Parsing a superframe";
...@@ -574,7 +764,17 @@ base::circular_deque<Vp9Parser::FrameInfo> Vp9Parser::ParseSuperframe() { ...@@ -574,7 +764,17 @@ base::circular_deque<Vp9Parser::FrameInfo> Vp9Parser::ParseSuperframe() {
// Parse frame information contained in the index and add a pointer to and // Parse frame information contained in the index and add a pointer to and
// size of each frame to frames. // size of each frame to frames.
base::circular_deque<FrameInfo> frames;
// Use this to calculate the per-frame IV value.
std::string iv;
std::vector<SubsampleEntry> subsamples;
size_t current_subsample = 0;
size_t extra_clear_subsample_bytes = 0;
if (stream_decrypt_config_) {
iv = stream_decrypt_config_->iv();
subsamples = stream_decrypt_config_->subsamples();
}
for (size_t i = 0; i < num_frames; ++i) { for (size_t i = 0; i < num_frames; ++i) {
uint32_t size = 0; uint32_t size = 0;
for (size_t j = 0; j < mag; ++j) { for (size_t j = 0; j < mag; ++j) {
...@@ -585,10 +785,25 @@ base::circular_deque<Vp9Parser::FrameInfo> Vp9Parser::ParseSuperframe() { ...@@ -585,10 +785,25 @@ base::circular_deque<Vp9Parser::FrameInfo> Vp9Parser::ParseSuperframe() {
if (!base::IsValueInRangeForNumericType<off_t>(size) || if (!base::IsValueInRangeForNumericType<off_t>(size) ||
static_cast<off_t>(size) > bytes_left) { static_cast<off_t>(size) > bytes_left) {
DVLOG(1) << "Not enough data in the buffer for frame " << i; DVLOG(1) << "Not enough data in the buffer for frame " << i;
return base::circular_deque<FrameInfo>(); frames.clear();
return frames;
}
FrameInfo frame = FrameInfo(stream, size);
if (subsamples.size()) {
std::unique_ptr<DecryptConfig> frame_dc = SplitSubsamples(
size, &current_subsample, &extra_clear_subsample_bytes,
stream_decrypt_config_.get(), subsamples, &iv);
if (!frame_dc) {
DVLOG(1) << "Failed to calculate decrypt config for frame " << i;
frames.clear();
return frames;
}
frame.decrypt_config = std::move(frame_dc);
} }
frames.push_back(FrameInfo(stream, size)); frames.push_back(std::move(frame));
stream += size; stream += size;
bytes_left -= size; bytes_left -= size;
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "base/containers/circular_deque.h" #include "base/containers/circular_deque.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "media/base/decrypt_config.h"
#include "media/base/media_export.h" #include "media/base/media_export.h"
#include "media/base/video_color_space.h" #include "media/base/video_color_space.h"
...@@ -368,16 +369,29 @@ class MEDIA_EXPORT Vp9Parser { ...@@ -368,16 +369,29 @@ class MEDIA_EXPORT Vp9Parser {
// |stream_size| in bytes. |stream| must point to the beginning of a single // |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 // frame or a single superframe, is owned by caller and must remain valid
// until the next call to SetStream(). // 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 // Parse the next frame in the current stream buffer, filling |fhdr| with
// the parsed frame header and updating current segmentation and loop filter // the parsed frame header and updating current segmentation and loop filter
// state. // 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, // Return kOk if a frame has successfully been parsed,
// kEOStream if there is no more data in the current stream buffer, // kEOStream if there is no more data in the current stream buffer,
// kAwaitingRefresh if this frame awaiting frame context update, or // kAwaitingRefresh if this frame awaiting frame context update, or
// kInvalidStream on error. // 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. // Return current parsing context.
const Context& context() const { return context_; } const Context& context() const { return context_; }
...@@ -393,8 +407,12 @@ class MEDIA_EXPORT Vp9Parser { ...@@ -393,8 +407,12 @@ class MEDIA_EXPORT Vp9Parser {
private: private:
// Stores start pointer and size of each frame within the current superframe. // Stores start pointer and size of each frame within the current superframe.
struct FrameInfo { struct FrameInfo {
FrameInfo() = default; FrameInfo();
FrameInfo(const FrameInfo& copy_from);
FrameInfo(const uint8_t* ptr, off_t size); FrameInfo(const uint8_t* ptr, off_t size);
~FrameInfo();
FrameInfo& operator=(const FrameInfo& copy_from);
bool IsValid() const { return ptr != nullptr; } bool IsValid() const { return ptr != nullptr; }
void Reset() { ptr = nullptr; } void Reset() { ptr = nullptr; }
...@@ -403,6 +421,8 @@ class MEDIA_EXPORT Vp9Parser { ...@@ -403,6 +421,8 @@ class MEDIA_EXPORT Vp9Parser {
// Size of the frame in bytes. // Size of the frame in bytes.
off_t size = 0; off_t size = 0;
std::unique_ptr<DecryptConfig> decrypt_config;
}; };
base::circular_deque<FrameInfo> ParseSuperframe(); base::circular_deque<FrameInfo> ParseSuperframe();
...@@ -439,6 +459,9 @@ class MEDIA_EXPORT Vp9Parser { ...@@ -439,6 +459,9 @@ class MEDIA_EXPORT Vp9Parser {
Context context_; Context context_;
// Encrypted stream info.
std::unique_ptr<DecryptConfig> stream_decrypt_config_;
FrameInfo curr_frame_info_; FrameInfo curr_frame_info_;
Vp9FrameHeader curr_frame_header_; Vp9FrameHeader curr_frame_header_;
......
...@@ -36,9 +36,10 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { ...@@ -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. // Parse until the end of stream/unsupported stream/error in stream is found.
while (ivf_parser.ParseNextFrame(&ivf_frame_header, &ivf_payload)) { while (ivf_parser.ParseNextFrame(&ivf_frame_header, &ivf_payload)) {
media::Vp9FrameHeader vp9_frame_header; 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. // 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) { media::Vp9Parser::kOk) {
// Repeat until all frames processed. // Repeat until all frames processed.
} }
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
// If |should_update| is true, it follows by the frame context to update. // If |should_update| is true, it follows by the frame context to update.
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <utility>
#include <vector>
#include "base/files/memory_mapped_file.h" #include "base/files/memory_mapped_file.h"
#include "base/logging.h" #include "base/logging.h"
...@@ -50,6 +52,11 @@ const struct TestParams kTestParams[] = { ...@@ -50,6 +52,11 @@ const struct TestParams kTestParams[] = {
{"test-25fps.vp9_2", 2, 10, 320, 240, true, 8, 79, 115, {"test-25fps.vp9_2", 2, 10, 320, 240, true, 8, 79, 115,
Vp9InterpolationFilter::SWITCHABLE, 46, 10}}; Vp9InterpolationFilter::SWITCHABLE, 46, 10}};
const char kInitialIV[] = "aaaaaaaaaaaaaaaa";
const char kIVIncrementOne[] = "aaaaaaaaaaaaaaab";
const char kIVIncrementTwo[] = "aaaaaaaaaaaaaaac";
const char kKeyID[] = "key-id";
} // anonymous namespace } // anonymous namespace
class Vp9ParserTest : public TestWithParam<TestParams> { class Vp9ParserTest : public TestWithParam<TestParams> {
...@@ -98,6 +105,11 @@ class Vp9ParserTest : public TestWithParam<TestParams> { ...@@ -98,6 +105,11 @@ class Vp9ParserTest : public TestWithParam<TestParams> {
} }
Vp9Parser::Result ParseNextFrame(struct Vp9FrameHeader* frame_hdr); Vp9Parser::Result ParseNextFrame(struct Vp9FrameHeader* frame_hdr);
void CheckSubsampleValues(
const uint8_t* superframe,
size_t framesize,
std::unique_ptr<DecryptConfig> config,
std::vector<std::unique_ptr<DecryptConfig>>& expected_split);
const Vp9SegmentationParams& GetSegmentation() const { const Vp9SegmentationParams& GetSegmentation() const {
return vp9_parser_->context().segmentation(); return vp9_parser_->context().segmentation();
...@@ -121,7 +133,8 @@ class Vp9ParserTest : public TestWithParam<TestParams> { ...@@ -121,7 +133,8 @@ class Vp9ParserTest : public TestWithParam<TestParams> {
Vp9Parser::Result Vp9ParserTest::ParseNextFrame(Vp9FrameHeader* fhdr) { Vp9Parser::Result Vp9ParserTest::ParseNextFrame(Vp9FrameHeader* fhdr) {
while (1) { while (1) {
Vp9Parser::Result res = vp9_parser_->ParseNextFrame(fhdr); std::unique_ptr<DecryptConfig> null_config;
Vp9Parser::Result res = vp9_parser_->ParseNextFrame(fhdr, &null_config);
if (res == Vp9Parser::kEOStream) { if (res == Vp9Parser::kEOStream) {
IvfFrameHeader ivf_frame_header; IvfFrameHeader ivf_frame_header;
const uint8_t* ivf_payload; const uint8_t* ivf_payload;
...@@ -129,7 +142,7 @@ Vp9Parser::Result Vp9ParserTest::ParseNextFrame(Vp9FrameHeader* fhdr) { ...@@ -129,7 +142,7 @@ Vp9Parser::Result Vp9ParserTest::ParseNextFrame(Vp9FrameHeader* fhdr) {
if (!ivf_parser_.ParseNextFrame(&ivf_frame_header, &ivf_payload)) if (!ivf_parser_.ParseNextFrame(&ivf_frame_header, &ivf_payload))
return Vp9Parser::kEOStream; return Vp9Parser::kEOStream;
vp9_parser_->SetStream(ivf_payload, ivf_frame_header.frame_size); vp9_parser_->SetStream(ivf_payload, ivf_frame_header.frame_size, nullptr);
continue; continue;
} }
...@@ -137,6 +150,413 @@ Vp9Parser::Result Vp9ParserTest::ParseNextFrame(Vp9FrameHeader* fhdr) { ...@@ -137,6 +150,413 @@ Vp9Parser::Result Vp9ParserTest::ParseNextFrame(Vp9FrameHeader* fhdr) {
} }
} }
void Vp9ParserTest::CheckSubsampleValues(
const uint8_t* superframe,
size_t framesize,
std::unique_ptr<DecryptConfig> config,
std::vector<std::unique_ptr<DecryptConfig>>& expected_split) {
vp9_parser_->SetStream(superframe, framesize, std::move(config));
for (auto& expected : expected_split) {
std::unique_ptr<DecryptConfig> actual =
vp9_parser_->NextFrameDecryptContextForTesting();
EXPECT_EQ(actual->iv(), expected->iv());
EXPECT_EQ(actual->subsamples().size(), expected->subsamples().size());
}
}
uint8_t make_marker_byte(bool is_superframe, const uint8_t frame_count) {
DCHECK_LE(frame_count, 8);
const uint8_t superframe_marker_byte =
// superframe marker byte
// marker (0b110) encoded at bits 6, 7, 8
// or non-superframe marker (0b111)
(0xE0 & ((is_superframe ? 0x06 : 0x07) << 5)) |
// magnitude - 1 encoded at bits 4, 5
(0x18 & (0x00 << 3)) |
// frame count - 2 encoded at bits 1, 2, 3
(0x07 & (frame_count - 1));
return superframe_marker_byte;
}
// ┌───────────────────┬────────────────────┐
// │ frame 1 │ frame 2 │
// ┝━━━━━━━━━━━━━━━━━━━┿━━━━━━━━━━━━━━━━━━━━┥
// │ clear1 | cipher 1 │ clear 2 | cipher 2 │
// └───────────────────┴────────────────────┘
TEST_F(Vp9ParserTest, AlignedFrameSubsampleParsing) {
vp9_parser_.reset(new Vp9Parser(false));
const uint8_t superframe_marker_byte = make_marker_byte(true, 2);
const uint8_t kSuperframe[] = {
// First frame; 32 bytes.
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Second frame; 32 bytes.
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Superframe marker goes before and after frame index.
superframe_marker_byte,
// First frame length (magnitude 1)
0x20,
// Second frame length (magnigude 1)
0x20,
// marker again.
superframe_marker_byte};
std::vector<std::unique_ptr<DecryptConfig>> expected;
expected.push_back(DecryptConfig::CreateCbcsConfig(
kKeyID, kInitialIV, {SubsampleEntry(16, 16)}, base::nullopt));
expected.push_back(DecryptConfig::CreateCbcsConfig(
kKeyID, kIVIncrementOne, {SubsampleEntry(16, 16)}, base::nullopt));
CheckSubsampleValues(
kSuperframe, sizeof(kSuperframe),
DecryptConfig::CreateCencConfig(
kKeyID, kInitialIV, {SubsampleEntry(16, 16), SubsampleEntry(16, 16)}),
expected);
}
// ┌───────────────────┬────────────────────┐
// │ frame 1 │ frame 2 │
// ┝━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━┥
// │ clear1 | cipher 1 │
// └────────────────────────────────────────┘
TEST_F(Vp9ParserTest, UnalignedFrameSubsampleParsing) {
vp9_parser_.reset(new Vp9Parser(false));
const uint8_t superframe_marker_byte = make_marker_byte(true, 2);
const uint8_t kSuperframe[] = {
// First frame; 32 bytes.
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Second frame; 32 bytes.
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Superframe marker goes before and after frame index.
superframe_marker_byte,
// First frame length (magnitude 1)
0x20,
// Second frame length (magnigude 1)
0x20,
// marker again.
superframe_marker_byte};
std::vector<std::unique_ptr<DecryptConfig>> expected;
expected.push_back(DecryptConfig::CreateCbcsConfig(
kKeyID, kInitialIV, {SubsampleEntry(32, 0)}, base::nullopt));
expected.push_back(DecryptConfig::CreateCbcsConfig(
kKeyID, kInitialIV, {SubsampleEntry(16, 16)}, base::nullopt));
CheckSubsampleValues(kSuperframe, sizeof(kSuperframe),
DecryptConfig::CreateCencConfig(
kKeyID, kInitialIV, {SubsampleEntry(48, 16)}),
expected);
}
// ┌─────────────────────────┬────────────────────┐
// │ frame 1 │ frame 2 │
// ┝━━━━━━━━━━━━━━━━━━━┯━━━━━┷━━━━━━━━━━━━━━━━━━━━┥
// │ clear1 | cipher 1 │ clear 2 | cipher 2 │
// └───────────────────┴──────────────────────────┘
TEST_F(Vp9ParserTest, ClearSectionRollsOverSubsampleParsing) {
vp9_parser_.reset(new Vp9Parser(false));
const uint8_t superframe_marker_byte = make_marker_byte(true, 2);
const uint8_t kSuperframe[] = {
// First frame; 48 bytes.
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Second frame; 32 bytes.
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Superframe marker goes before and after frame index.
superframe_marker_byte,
// First frame length (magnitude 1)
0x30,
// Second frame length (magnigude 1)
0x20,
// marker again.
superframe_marker_byte};
std::vector<std::unique_ptr<DecryptConfig>> expected;
expected.push_back(DecryptConfig::CreateCbcsConfig(
kKeyID, kInitialIV, {SubsampleEntry(16, 16), SubsampleEntry(16, 0)},
base::nullopt));
expected.push_back(DecryptConfig::CreateCbcsConfig(
kKeyID, kIVIncrementOne, {SubsampleEntry(16, 16)}, base::nullopt));
CheckSubsampleValues(
kSuperframe, sizeof(kSuperframe),
DecryptConfig::CreateCencConfig(
kKeyID, kInitialIV, {SubsampleEntry(16, 16), SubsampleEntry(32, 16)}),
expected);
}
// ┌────────────────────────────────────────┬────────────────────┐
// │ frame 1 │ frame 2 │
// ┝━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━┿━━━━━━━━━━━━━━━━━━━━┥
// │ clear1 | cipher 1 │ clear 2 | cipher 2 │ clear 3 | cipher 3 │
// └───────────────────┴────────────────────┴────────────────────┘
TEST_F(Vp9ParserTest, FirstFrame2xSubsampleParsing) {
vp9_parser_.reset(new Vp9Parser(false));
const uint8_t superframe_marker_byte = make_marker_byte(true, 2);
const uint8_t kSuperframe[] = {
// First frame; 64 bytes.
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
// Second frame; 32 bytes.
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Superframe marker goes before and after frame index.
superframe_marker_byte,
// First frame length (magnitude 1)
0x40,
// Second frame length (magnigude 1)
0x20,
// marker again.
superframe_marker_byte};
std::vector<std::unique_ptr<DecryptConfig>> expected;
expected.push_back(DecryptConfig::CreateCbcsConfig(
kKeyID, kInitialIV, {SubsampleEntry(16, 16), SubsampleEntry(16, 16)},
base::nullopt));
expected.push_back(DecryptConfig::CreateCbcsConfig(
kKeyID, kIVIncrementTwo, {SubsampleEntry(16, 16)}, base::nullopt));
CheckSubsampleValues(kSuperframe, sizeof(kSuperframe),
DecryptConfig::CreateCencConfig(
kKeyID, kInitialIV,
{SubsampleEntry(16, 16), SubsampleEntry(16, 16),
SubsampleEntry(16, 16)}),
expected);
}
// ┌─────────────────────────────────────────────┬───────────────┐
// │ frame 1 │ frame 2 │
// ┝━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━┯━━━━┷━━━━━━━━━━━━━━━┥
// │ clear1 | cipher 1 │ clear 2 | cipher 2 │ clear 3 | cipher 3 │
// └───────────────────┴────────────────────┴────────────────────┘
TEST_F(Vp9ParserTest, UnalignedBigFrameSubsampleParsing) {
vp9_parser_.reset(new Vp9Parser(false));
const uint8_t superframe_marker_byte = make_marker_byte(true, 2);
const uint8_t kSuperframe[] = {
// First frame; 72 bytes.
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Second frame; 32 bytes.
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Superframe marker goes before and after frame index.
superframe_marker_byte,
// First frame length (magnitude 1)
0x48,
// Second frame length (magnigude 1)
0x20,
// marker again.
superframe_marker_byte};
std::vector<std::unique_ptr<DecryptConfig>> expected;
expected.push_back(DecryptConfig::CreateCbcsConfig(
kKeyID, kInitialIV,
{SubsampleEntry(16, 16), SubsampleEntry(16, 16), SubsampleEntry(8, 0)},
base::nullopt));
expected.push_back(DecryptConfig::CreateCbcsConfig(
kKeyID, kIVIncrementTwo, {SubsampleEntry(16, 16)}, base::nullopt));
CheckSubsampleValues(
kSuperframe, sizeof(kSuperframe),
DecryptConfig::CreateCencConfig(kKeyID, kInitialIV,
{
SubsampleEntry(16, 16),
SubsampleEntry(16, 16),
SubsampleEntry(24, 16),
}),
expected);
}
// ┌───────────────────┬────────────────────┐
// │ frame 1 │ frame 2 │
// ┝━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━┥
// │ clear1 | cipher 1 │
// └────────────────────────────────────────┘
TEST_F(Vp9ParserTest, UnalignedInvalidSubsampleParsing) {
vp9_parser_.reset(new Vp9Parser(false));
const uint8_t superframe_marker_byte = make_marker_byte(true, 2);
const uint8_t kSuperframe[] = {
// First frame; 32 bytes.
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Second frame; 32 bytes.
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Superframe marker goes before and after frame index.
superframe_marker_byte,
// First frame length (magnitude 1)
0x20,
// Second frame length (magnigude 1)
0x20,
// marker again.
superframe_marker_byte};
vp9_parser_->SetStream(kSuperframe, sizeof(kSuperframe),
DecryptConfig::CreateCencConfig(
kKeyID, kInitialIV, {SubsampleEntry(16, 32)}));
ASSERT_EQ(vp9_parser_->NextFrameDecryptContextForTesting().get(), nullptr);
}
// ┌─────────────────────────────────────┬─────────┐
// │ single frame in superframe │ marker │
// ┝━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━┥
// │ clear1 = 0 | cipher 1 │
// └───────────────────────────────────────────────┘
TEST_F(Vp9ParserTest, CipherBytesCoverSuperframeMarkerSubsampleParsing) {
vp9_parser_.reset(new Vp9Parser(false));
const uint8_t superframe_marker_byte = make_marker_byte(false, 1);
const uint8_t kSuperframe[] = {
// First frame; 44 bytes.
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Superframe marker goes before and after frame index.
superframe_marker_byte,
// First frame length (magnitude 1)
0x20,
// Second frame length (magnigude 1)
0x20,
// marker again.
superframe_marker_byte};
vp9_parser_->SetStream(kSuperframe, sizeof(kSuperframe),
DecryptConfig::CreateCencConfig(
kKeyID, kInitialIV, {SubsampleEntry(0, 48)}));
std::unique_ptr<DecryptConfig> actual =
vp9_parser_->NextFrameDecryptContextForTesting();
EXPECT_EQ(actual->iv(), kInitialIV);
EXPECT_EQ(actual->subsamples().size(), 1lu);
}
// ┌─────────────────────────────────────┬─────────┐
// │ single frame in superframe │ marker │
// ┝━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━┥
// │ clear1 │
// └───────────────────────────────────────────────┘
TEST_F(Vp9ParserTest, ClearBytesCoverSuperframeMarkerSubsampleParsing) {
vp9_parser_.reset(new Vp9Parser(false));
const uint8_t superframe_marker_byte = make_marker_byte(false, 1);
const uint8_t kSuperframe[] = {
// First frame; 44 bytes.
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Superframe marker goes before and after frame index.
superframe_marker_byte,
// First frame length (magnitude 1)
0x20,
// Second frame length (magnigude 1)
0x20,
// marker again.
superframe_marker_byte};
vp9_parser_->SetStream(kSuperframe, sizeof(kSuperframe),
DecryptConfig::CreateCencConfig(
kKeyID, kInitialIV, {SubsampleEntry(48, 0)}));
std::unique_ptr<DecryptConfig> actual =
vp9_parser_->NextFrameDecryptContextForTesting();
EXPECT_EQ(actual->iv(), kInitialIV);
EXPECT_EQ(actual->subsamples().size(), 1lu);
}
// ┌─────────────────────────────────────┬─────────┐
// │ single frame in superframe │ marker │
// ┝━━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━┷━━━━━━━━━┥
// │ clear 1 | cipher 1 │ clear 2 │
// └────────────────────┴──────────────────────────┘
TEST_F(Vp9ParserTest, SecondClearSubsampleSuperframeMarkerSubsampleParsing) {
vp9_parser_.reset(new Vp9Parser(false));
const uint8_t superframe_marker_byte = make_marker_byte(false, 1);
const uint8_t kSuperframe[] = {
// First frame; 44 bytes.
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Superframe marker goes before and after frame index.
superframe_marker_byte,
// First frame length (magnitude 1)
0x20,
// Second frame length (magnigude 1)
0x20,
// marker again.
superframe_marker_byte};
vp9_parser_->SetStream(
kSuperframe, sizeof(kSuperframe),
DecryptConfig::CreateCencConfig(kKeyID, kInitialIV,
{
SubsampleEntry(16, 16),
SubsampleEntry(16, 0),
}));
std::unique_ptr<DecryptConfig> actual =
vp9_parser_->NextFrameDecryptContextForTesting();
EXPECT_EQ(actual->iv(), kInitialIV);
EXPECT_EQ(actual->subsamples().size(), 2lu);
}
TEST_F(Vp9ParserTest, TestIncrementIV) {
std::vector<std::tuple<char const*, uint32_t, char const*>> input_output = {
{"--------aaaaaaaa", 1, "--------aaaaaaab"},
{"--------aaaaaaa\377", 1, "--------aaaaaab\0"},
{"--------aaaaaaa\377", 2, "--------aaaaaab\1"},
{"--------\377\377\377\377\377\377\377\377", 2,
"--------\0\0\0\0\0\0\0\1"}};
for (auto& testcase : input_output) {
EXPECT_EQ(
vp9_parser_->IncrementIVForTesting(
std::string(std::get<0>(testcase), 16), std::get<1>(testcase)),
std::string(std::get<2>(testcase), 16));
}
}
TEST_F(Vp9ParserTest, StreamFileParsingWithoutCompressedHeader) { TEST_F(Vp9ParserTest, StreamFileParsingWithoutCompressedHeader) {
Initialize("test-25fps.vp9", false); Initialize("test-25fps.vp9", false);
......
...@@ -34,15 +34,14 @@ void VP9Decoder::SetStream(int32_t id, ...@@ -34,15 +34,14 @@ void VP9Decoder::SetStream(int32_t id,
const DecryptConfig* decrypt_config) { const DecryptConfig* decrypt_config) {
DCHECK(ptr); DCHECK(ptr);
DCHECK(size); DCHECK(size);
if (decrypt_config) {
NOTIMPLEMENTED();
state_ = kError;
return;
}
DVLOG(4) << "New input stream id: " << id << " at: " << (void*)ptr DVLOG(4) << "New input stream id: " << id << " at: " << (void*)ptr
<< " size: " << size; << " size: " << size;
stream_id_ = id; 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() { bool VP9Decoder::Flush() {
...@@ -65,9 +64,11 @@ void VP9Decoder::Reset() { ...@@ -65,9 +64,11 @@ void VP9Decoder::Reset() {
VP9Decoder::DecodeResult VP9Decoder::Decode() { VP9Decoder::DecodeResult VP9Decoder::Decode() {
while (1) { while (1) {
// Read a new frame header if one is not awaiting decoding already. // Read a new frame header if one is not awaiting decoding already.
std::unique_ptr<DecryptConfig> decrypt_config;
if (!curr_frame_hdr_) { if (!curr_frame_hdr_) {
std::unique_ptr<Vp9FrameHeader> hdr(new Vp9FrameHeader()); 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) { switch (res) {
case Vp9Parser::kOk: case Vp9Parser::kOk:
curr_frame_hdr_ = std::move(hdr); curr_frame_hdr_ = std::move(hdr);
...@@ -179,6 +180,8 @@ VP9Decoder::DecodeResult VP9Decoder::Decode() { ...@@ -179,6 +180,8 @@ VP9Decoder::DecodeResult VP9Decoder::Decode() {
pic->set_visible_rect(new_render_rect); pic->set_visible_rect(new_render_rect);
pic->set_bitstream_id(stream_id_); pic->set_bitstream_id(stream_id_);
pic->set_decrypt_config(std::move(decrypt_config));
// For VP9, container color spaces override video stream color spaces. // For VP9, container color spaces override video stream color spaces.
if (container_color_space_.IsSpecified()) { if (container_color_space_.IsSpecified()) {
pic->set_colorspace(container_color_space_); pic->set_colorspace(container_color_space_);
......
...@@ -619,9 +619,10 @@ class VP9ConfigChangeDetector : public ConfigChangeDetector { ...@@ -619,9 +619,10 @@ class VP9ConfigChangeDetector : public ConfigChangeDetector {
// Detects stream configuration changes. // Detects stream configuration changes.
// Returns false on failure. // Returns false on failure.
bool DetectConfig(const uint8_t* stream, unsigned int size) override { bool DetectConfig(const uint8_t* stream, unsigned int size) override {
parser_.SetStream(stream, size); parser_.SetStream(stream, size, nullptr);
Vp9FrameHeader fhdr; 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); visible_rect_ = gfx::Rect(fhdr.render_width, fhdr.render_height);
color_space_ = fhdr.GetColorSpace(); 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