Support subsample encryption in AV1

Closes #453.

Change-Id: I68e46fb83cbf7e62a19fa83698cb66bfc0acd98d
This commit is contained in:
KongQun Yang 2018-10-09 10:41:18 -07:00
parent 5c4d930465
commit 4b19905bc2
17 changed files with 290 additions and 42 deletions

View File

@ -1198,6 +1198,18 @@ class PackagerFunctionalTest(PackagerAppTest):
self.assertPackageSuccess(streams, flags)
self._CheckTestResults('flac-with-encryption', verify_decryption=True)
def testAv1Mp4WithEncryption(self):
self.assertPackageSuccess(
self._GetStreams(['video'], test_files=['bear-av1.mp4']),
self._GetFlags(encryption=True, output_dash=True, output_hls=True))
self._CheckTestResults('av1-mp4-with-encryption', verify_decryption=True)
def testAv1WebMWithEncryption(self):
self.assertPackageSuccess(
self._GetStreams(['video'], test_files=['bear-av1.webm']),
self._GetFlags(encryption=True, output_dash=True, output_hls=True))
self._CheckTestResults('av1-webm-with-encryption', verify_decryption=True)
def testWvmInput(self):
self.encryption_key = '9248d245390e0a49d483ba9b43fc69c3'
self.assertPackageSuccess(

View File

@ -0,0 +1,5 @@
#EXTM3U
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
#EXT-X-STREAM-INF:BANDWIDTH=69777,AVERAGE-BANDWIDTH=69777,CODECS="av01.0.00M.08.0.110",RESOLUTION=320x240
stream_0.m3u8

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT2.7360665798187256S">
<Period id="0">
<AdaptationSet id="0" contentType="video" width="320" height="240" frameRate="30000/1001" subsegmentAlignment="true" par="4:3">
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b">
<cenc:pssh>AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==</cenc:pssh>
</ContentProtection>
<Representation id="0" bandwidth="69777" codecs="av01.0.00M.08.0.110" mimeType="video/mp4" sar="1:1">
<BaseURL>bear-av1-video.mp4</BaseURL>
<SegmentBase indexRange="1041-1084" timescale="30000">
<Initialization range="0-1040"/>
</SegmentBase>
</Representation>
</AdaptationSet>
</Period>
</MPD>

View File

@ -0,0 +1,10 @@
#EXTM3U
#EXT-X-VERSION:6
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
#EXT-X-TARGETDURATION:3
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-MAP:URI="bear-av1-video.mp4",BYTERANGE="1041@0"
#EXTINF:2.736,
#EXT-X-BYTERANGE:23864@1085
bear-av1-video.mp4
#EXT-X-ENDLIST

View File

@ -0,0 +1,5 @@
#EXTM3U
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
#EXT-X-STREAM-INF:BANDWIDTH=69426,AVERAGE-BANDWIDTH=69426,CODECS="av01.0.00M.08.0.110",RESOLUTION=320x240
stream_0.m3u8

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT2.734999895095825S">
<Period id="0">
<AdaptationSet id="0" contentType="video" width="320" height="240" frameRate="1000000/33000" subsegmentAlignment="true" par="4:3">
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b" cenc:default_KID="31323334-3536-3738-3930-313233343536">
<cenc:pssh>AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==</cenc:pssh>
</ContentProtection>
<Representation id="0" bandwidth="69426" codecs="av01.0.00M.08.0.110" mimeType="video/webm" sar="1:1">
<BaseURL>bear-av1-video.webm</BaseURL>
<SegmentBase indexRange="358-376" timescale="1000000">
<Initialization range="0-357"/>
</SegmentBase>
</Representation>
</AdaptationSet>
</Period>
</MPD>

View File

@ -0,0 +1,10 @@
#EXTM3U
#EXT-X-VERSION:6
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
#EXT-X-TARGETDURATION:3
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-MAP:URI="bear-av1-video.webm",BYTERANGE="358@0"
#EXTINF:2.735,
#EXT-X-BYTERANGE:23735@377
bear-av1-video.webm
#EXT-X-ENDLIST

View File

@ -252,17 +252,22 @@ int FindLatestForward(int shifted_order_hints[],
AV1Parser::AV1Parser() = default;
AV1Parser::~AV1Parser() = default;
bool AV1Parser::Parse(const uint8_t* data, size_t data_size) {
bool AV1Parser::Parse(const uint8_t* data,
size_t data_size,
std::vector<Tile>* tiles) {
tiles->clear();
BitReader reader(data, data_size);
while (reader.bits_available() > 0) {
if (!ParseOpenBitstreamUnit(&reader))
if (!ParseOpenBitstreamUnit(&reader, tiles))
return false;
}
return true;
}
// 5.3.1. General OBU syntax.
bool AV1Parser::ParseOpenBitstreamUnit(BitReader* reader) {
bool AV1Parser::ParseOpenBitstreamUnit(BitReader* reader,
std::vector<Tile>* tiles) {
ObuHeader obu_header;
RCHECK(ParseObuHeader(reader, &obu_header));
@ -284,10 +289,10 @@ bool AV1Parser::ParseOpenBitstreamUnit(BitReader* reader) {
RCHECK(ParseFrameHeaderObu(obu_header, reader));
break;
case OBU_TILE_GROUP:
RCHECK(ParseTileGroupObu(obu_size, reader));
RCHECK(ParseTileGroupObu(obu_size, reader, tiles));
break;
case OBU_FRAME:
RCHECK(ParseFrameObu(obu_header, obu_size, reader));
RCHECK(ParseFrameObu(obu_header, obu_size, reader, tiles));
break;
default:
// Skip all OBUs we are not interested.
@ -1638,18 +1643,21 @@ bool AV1Parser::SkipTemporalPointInfo(BitReader* reader) {
// 5.10. Frame OBU syntax.
bool AV1Parser::ParseFrameObu(const ObuHeader& obu_header,
size_t size,
BitReader* reader) {
BitReader* reader,
std::vector<Tile>* tiles) {
const size_t start_bit_pos = reader->bit_position();
RCHECK(ParseFrameHeaderObu(obu_header, reader));
RCHECK(ByteAlignment(reader));
const size_t end_bit_pos = reader->bit_position();
const size_t header_bytes = (end_bit_pos - start_bit_pos) / 8;
RCHECK(ParseTileGroupObu(size - header_bytes, reader));
RCHECK(ParseTileGroupObu(size - header_bytes, reader, tiles));
return true;
}
// 5.11.1. General tile group OBU syntax.
bool AV1Parser::ParseTileGroupObu(size_t size, BitReader* reader) {
bool AV1Parser::ParseTileGroupObu(size_t size,
BitReader* reader,
std::vector<Tile>* tiles) {
const TileInfo& tile_info = frame_header_.tile_info;
const size_t start_bit_pos = reader->bit_position();
@ -1680,6 +1688,7 @@ bool AV1Parser::ParseTileGroupObu(size_t size, BitReader* reader) {
tile_size = tile_size_minus_1 + 1;
size -= tile_size + tile_info.tile_size_bytes;
}
tiles->push_back({reader->bit_position() / 8, tile_size});
RCHECK(reader->SkipBits(tile_size * 8)); // Skip the tile.
}

View File

@ -10,6 +10,8 @@
#include <stdint.h>
#include <stdlib.h>
#include <vector>
namespace shaka {
namespace media {
@ -19,15 +21,25 @@ class BitReader;
/// https://aomediacodec.github.io/av1-spec/.
class AV1Parser {
public:
struct Tile {
size_t start_offset_in_bytes;
size_t size_in_bytes;
};
AV1Parser();
~AV1Parser();
virtual ~AV1Parser();
/// Parse an AV1 sample. Note that the sample data SHALL be a sequence of OBUs
/// forming a Temporal Unit, with each OBU SHALL follow the
/// open_bitstream_unit Low Overhead Bitstream Format syntax. See
/// https://aomediacodec.github.io/av1-isobmff/#sampleformat for details.
/// @param[out] on success, tiles will be filled with the tile information if
/// @a data contains Frame OBU or TileGroup OBU; It will be empty
/// otherwise.
/// @return true on success, false otherwise.
bool Parse(const uint8_t* data, size_t data_size);
virtual bool Parse(const uint8_t* data,
size_t data_size,
std::vector<Tile>* tiles);
private:
AV1Parser(const AV1Parser&) = delete;
@ -183,7 +195,7 @@ class AV1Parser {
bool subsampling_y = false;
};
bool ParseOpenBitstreamUnit(BitReader* reader);
bool ParseOpenBitstreamUnit(BitReader* reader, std::vector<Tile>* tiles);
bool ParseObuHeader(BitReader* reader, ObuHeader* obu_header);
bool ParseObuExtensionHeader(BitReader* reader,
ObuExtensionHeader* obu_extension_header);
@ -248,10 +260,13 @@ class AV1Parser {
// Frame OBU.
bool ParseFrameObu(const ObuHeader& obu_header,
size_t size,
BitReader* reader);
BitReader* reader,
std::vector<Tile>* tiles);
// TileGroup OBU.
bool ParseTileGroupObu(size_t size, BitReader* reader);
bool ParseTileGroupObu(size_t size,
BitReader* reader,
std::vector<Tile>* tiles);
bool SegFeatureActiveIdx(int idx, int feature);
// Decoding process related helper functions.

View File

@ -6,18 +6,28 @@
#include "packager/media/codecs/av1_parser.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "packager/media/test/test_data_util.h"
using ::testing::ElementsAre;
namespace shaka {
namespace media {
inline bool operator==(const AV1Parser::Tile& lhs, const AV1Parser::Tile& rhs) {
return lhs.start_offset_in_bytes == rhs.start_offset_in_bytes &&
lhs.size_in_bytes == rhs.size_in_bytes;
}
TEST(AV1ParserTest, ParseIFrameSuccess) {
const std::vector<uint8_t> buffer = ReadTestDataFile("av1-I-frame-320x240");
AV1Parser parser;
ASSERT_TRUE(parser.Parse(buffer.data(), buffer.size()));
std::vector<AV1Parser::Tile> tiles;
ASSERT_TRUE(parser.Parse(buffer.data(), buffer.size(), &tiles));
EXPECT_THAT(tiles, ElementsAre(AV1Parser::Tile{0x1d, 0x4e1}));
}
} // namespace media

View File

@ -11,6 +11,7 @@
#include "packager/media/base/decrypt_config.h"
#include "packager/media/base/video_stream_info.h"
#include "packager/media/codecs/av1_parser.h"
#include "packager/media/codecs/video_slice_header_parser.h"
#include "packager/media/codecs/vp8_parser.h"
#include "packager/media/codecs/vp9_parser.h"
@ -30,6 +31,39 @@ uint8_t GetNaluLengthSize(const StreamInfo& stream_info) {
return video_stream_info.nalu_length_size();
}
bool ShouldAlignProtectedData(Codec codec,
FourCC protection_scheme,
bool vp9_subsample_encryption) {
switch (codec) {
case kCodecAV1:
// Per AV1 in ISO-BMFF spec [1], BytesOfProtectedData SHALL be a multiple
// of 16 bytes.
// [1] https://aomediacodec.github.io/av1-isobmff/#subsample-encryption
return true;
case kCodecVP9:
// "VP Codec ISO Media File Format Binding" document requires that the
// encrypted bytes of each frame within the superframe must be block
// aligned so that the counter state can be computed for each frame
// within the superframe.
// ISO/IEC 23001-7:2016 10.2 'cbc1' 10.3 'cens'
// The BytesOfProtectedData size SHALL be a multiple of 16 bytes to
// avoid partial blocks in Subsamples.
// For consistency, apply block alignment to all frames when VP9 subsample
// encryption is enabled.
return vp9_subsample_encryption;
default:
// ISO/IEC 23001-7:2016 10.2 'cbc1' 10.3 'cens'
// The BytesOfProtectedData size SHALL be a multiple of 16 bytes to avoid
// partial blocks in Subsamples.
// CMAF requires 'cenc' scheme BytesOfProtectedData SHALL be a multiple of
// 16 bytes; while 'cbcs' scheme BytesOfProtectedData SHALL start on the
// first byte of video data following the slice header.
return protection_scheme == FOURCC_cbc1 ||
protection_scheme == FOURCC_cens ||
protection_scheme == FOURCC_cenc;
}
}
// A convenient util class to organize subsamples, e.g. combine consecutive
// subsamples with only clear bytes, split subsamples if the clear bytes exceeds
// 2^16 etc.
@ -97,6 +131,9 @@ Status SubsampleGenerator::Initialize(FourCC protection_scheme,
nalu_length_size_ = GetNaluLengthSize(stream_info);
switch (codec_) {
case kCodecAV1:
av1_parser_.reset(new AV1Parser);
break;
case kCodecVP9:
if (vp9_subsample_encryption_)
vpx_parser_.reset(new VP9Parser);
@ -114,39 +151,33 @@ Status SubsampleGenerator::Initialize(FourCC protection_scheme,
return Status(error::ENCRYPTION_FAILURE, "Unknown video codec.");
}
}
if (av1_parser_) {
// Parse configOBUs in AV1CodecConfigurationRecord if exists.
// https://aomediacodec.github.io/av1-isobmff/#av1codecconfigurationbox-syntax.
const size_t kConfigOBUsOffset = 4;
const bool has_config_obus =
stream_info.codec_config().size() > kConfigOBUsOffset;
std::vector<AV1Parser::Tile> tiles;
if (has_config_obus &&
!av1_parser_->Parse(
&stream_info.codec_config()[kConfigOBUsOffset],
stream_info.codec_config().size() - kConfigOBUsOffset, &tiles)) {
return Status(
error::ENCRYPTION_FAILURE,
"Failed to parse configOBUs in AV1CodecConfigurationRecord.");
}
DCHECK(tiles.empty());
}
if (header_parser_) {
CHECK_NE(nalu_length_size_, 0u) << "AnnexB stream is not supported yet";
if (!header_parser_->Initialize(stream_info.codec_config())) {
return Status(error::ENCRYPTION_FAILURE,
"Fail to read SPS and PPS data.");
"Failed to read SPS and PPS data.");
}
}
switch (codec_) {
case kCodecVP9:
// "VP Codec ISO Media File Format Binding" document requires that the
// encrypted bytes of each frame within the superframe must be block
// aligned so that the counter state can be computed for each frame
// within the superframe.
// ISO/IEC 23001-7:2016 10.2 'cbc1' 10.3 'cens'
// The BytesOfProtectedData size SHALL be a multiple of 16 bytes to
// avoid partial blocks in Subsamples.
// For consistency, apply block alignment to all frames when VP9 subsample
// encryption is enabled.
align_protected_data_ = vp9_subsample_encryption_;
break;
default:
// ISO/IEC 23001-7:2016 10.2 'cbc1' 10.3 'cens'
// The BytesOfProtectedData size SHALL be a multiple of 16 bytes to avoid
// partial blocks in Subsamples.
// CMAF requires 'cenc' scheme BytesOfProtectedData SHALL be a multiple of
// 16 bytes; while 'cbcs' scheme BytesOfProtectedData SHALL start on the
// first byte of video data following the slice header.
align_protected_data_ = protection_scheme == FOURCC_cbc1 ||
protection_scheme == FOURCC_cens ||
protection_scheme == FOURCC_cenc;
break;
}
align_protected_data_ = ShouldAlignProtectedData(codec_, protection_scheme,
vp9_subsample_encryption_);
if (protection_scheme == kAppleSampleAesProtectionScheme) {
const size_t kH264LeadingClearBytesSize = 32u;
@ -184,6 +215,8 @@ Status SubsampleGenerator::GenerateSubsamples(
std::vector<SubsampleEntry>* subsamples) {
subsamples->clear();
switch (codec_) {
case kCodecAV1:
return GenerateSubsamplesFromAV1Frame(frame, frame_size, subsamples);
case kCodecH264:
FALLTHROUGH_INTENDED;
case kCodecH265:
@ -221,6 +254,11 @@ void SubsampleGenerator::InjectVideoSliceHeaderParserForTesting(
header_parser_ = std::move(header_parser);
}
void SubsampleGenerator::InjectAV1ParserForTesting(
std::unique_ptr<AV1Parser> av1_parser) {
av1_parser_ = std::move(av1_parser);
}
Status SubsampleGenerator::GenerateSubsamplesFromVPxFrame(
const uint8_t* frame,
size_t frame_size,
@ -299,5 +337,31 @@ Status SubsampleGenerator::GenerateSubsamplesFromH26xFrame(
return Status::OK;
}
Status SubsampleGenerator::GenerateSubsamplesFromAV1Frame(
const uint8_t* frame,
size_t frame_size,
std::vector<SubsampleEntry>* subsamples) {
DCHECK(av1_parser_);
std::vector<AV1Parser::Tile> av1_tiles;
if (!av1_parser_->Parse(frame, frame_size, &av1_tiles))
return Status(error::ENCRYPTION_FAILURE, "Failed to parse AV1 frame.");
SubsampleOrganizer subsample_organizer(align_protected_data_, subsamples);
size_t last_tile_end_offset = 0;
for (const AV1Parser::Tile& tile : av1_tiles) {
DCHECK_LE(last_tile_end_offset, tile.start_offset_in_bytes);
// Per AV1 in ISO-BMFF spec [1], only decode_tile is encrypted.
// [1] https://aomediacodec.github.io/av1-isobmff/#subsample-encryption
subsample_organizer.AddSubsample(
tile.start_offset_in_bytes - last_tile_end_offset, tile.size_in_bytes);
last_tile_end_offset = tile.start_offset_in_bytes + tile.size_in_bytes;
}
DCHECK_LE(last_tile_end_offset, frame_size);
if (last_tile_end_offset < frame_size)
subsample_organizer.AddSubsample(frame_size - last_tile_end_offset, 0);
return Status::OK;
}
} // namespace media
} // namespace shaka

View File

@ -17,6 +17,7 @@
namespace shaka {
namespace media {
class AV1Parser;
class VideoSliceHeaderParser;
class VPxParser;
struct SubsampleEntry;
@ -63,6 +64,7 @@ class SubsampleGenerator {
void InjectVpxParserForTesting(std::unique_ptr<VPxParser> vpx_parser);
void InjectVideoSliceHeaderParserForTesting(
std::unique_ptr<VideoSliceHeaderParser> header_parser);
void InjectAV1ParserForTesting(std::unique_ptr<AV1Parser> av1_parser);
private:
SubsampleGenerator(const SubsampleGenerator&) = delete;
@ -76,6 +78,10 @@ class SubsampleGenerator {
const uint8_t* frame,
size_t frame_size,
std::vector<SubsampleEntry>* subsamples);
Status GenerateSubsamplesFromAV1Frame(
const uint8_t* frame,
size_t frame_size,
std::vector<SubsampleEntry>* subsamples);
const bool vp9_subsample_encryption_ = false;
// Whether the protected portion should be AES block (16 bytes) aligned.
@ -95,6 +101,8 @@ class SubsampleGenerator {
std::unique_ptr<VPxParser> vpx_parser_;
// Video slice header parser for NAL strucutred streams.
std::unique_ptr<VideoSliceHeaderParser> header_parser_;
// AV1 parser for AV1 streams.
std::unique_ptr<AV1Parser> av1_parser_;
};
} // namespace media

View File

@ -11,6 +11,7 @@
#include "packager/media/base/audio_stream_info.h"
#include "packager/media/base/video_stream_info.h"
#include "packager/media/codecs/av1_parser.h"
#include "packager/media/codecs/video_slice_header_parser.h"
#include "packager/media/codecs/vpx_parser.h"
#include "packager/status_test_util.h"
@ -30,8 +31,7 @@ using ::testing::Values;
using ::testing::WithParamInterface;
const bool kVP9SubsampleEncryption = true;
// Use H264 code config.
const uint8_t kH264CodecConfig[]{
const uint8_t kH264CodecConfig[] = {
// clang-format off
// Header
0x01, 0x64, 0x00, 0x1e, 0xff,
@ -49,6 +49,7 @@ const uint8_t kH264CodecConfig[]{
0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0,
// clang-format on
};
const uint8_t kAV1CodecConfig[] = {0x00, 0x01, 0x02, 0x03};
const int kTrackId = 1;
const uint32_t kTimeScale = 1000;
const uint64_t kDuration = 10000;
@ -71,6 +72,10 @@ VideoStreamInfo GetVideoStreamInfo(Codec codec) {
codec_config = kH264CodecConfig;
codec_config_size = sizeof(kH264CodecConfig);
break;
case kCodecAV1:
codec_config = kAV1CodecConfig;
codec_config_size = sizeof(kAV1CodecConfig);
break;
default:
// We do not care about the codec configs for other codecs in this file.
break;
@ -121,6 +126,14 @@ class MockVideoSliceHeaderParser : public VideoSliceHeaderParser {
MOCK_METHOD1(GetHeaderSize, int64_t(const Nalu& nalu));
};
class MockAV1Parser : public AV1Parser {
public:
MOCK_METHOD3(Parse,
bool(const uint8_t* data,
size_t data_size,
std::vector<Tile>* tiles));
};
class SubsampleGeneratorTest : public Test, public WithParamInterface<FourCC> {
public:
SubsampleGeneratorTest() : protection_scheme_(GetParam()) {}
@ -345,6 +358,58 @@ TEST_P(SubsampleGeneratorTest, H264SubsampleEncryption) {
EXPECT_THAT(subsamples, ElementsAreArray(kExpectedAlignedSubsamples));
}
TEST_P(SubsampleGeneratorTest, AV1ParserFailed) {
SubsampleGenerator generator(kVP9SubsampleEncryption);
ASSERT_OK(
generator.Initialize(protection_scheme_, GetVideoStreamInfo(kCodecAV1)));
constexpr size_t kFrameSize = 50;
constexpr uint8_t kFrame[kFrameSize] = {};
std::unique_ptr<MockAV1Parser> mock_av1_parser(new MockAV1Parser);
EXPECT_CALL(*mock_av1_parser, Parse(kFrame, kFrameSize, _))
.WillOnce(Return(false));
generator.InjectAV1ParserForTesting(std::move(mock_av1_parser));
std::vector<SubsampleEntry> subsamples;
ASSERT_NOT_OK(generator.GenerateSubsamples(kFrame, kFrameSize, &subsamples));
}
TEST_P(SubsampleGeneratorTest, AV1SubsampleEncryption) {
SubsampleGenerator generator(kVP9SubsampleEncryption);
ASSERT_OK(
generator.Initialize(protection_scheme_, GetVideoStreamInfo(kCodecAV1)));
constexpr size_t kFrameSize = 50;
constexpr uint8_t kFrame[kFrameSize] = {};
constexpr size_t kTileOffsets[] = {4, 11};
constexpr size_t kTileSizes[] = {6, 33};
// AV1 block align protected data for all protection schemes.
const SubsampleEntry kExpectedSubsamples[] = {
// {4,6},{11-4-6,33},{50-11-33,0} block aligned => {10,0},{2,32},{6,0}.
// Then merge consecutive clear-only subsamples.
{12, 32},
{6, 0},
};
std::vector<AV1Parser::Tile> tiles(2);
for (int i = 0; i < 2; i++) {
tiles[i].start_offset_in_bytes = kTileOffsets[i];
tiles[i].size_in_bytes = kTileSizes[i];
}
std::unique_ptr<MockAV1Parser> mock_av1_parser(new MockAV1Parser);
EXPECT_CALL(*mock_av1_parser, Parse(kFrame, kFrameSize, _))
.WillOnce(DoAll(SetArgPointee<2>(tiles), Return(true)));
generator.InjectAV1ParserForTesting(std::move(mock_av1_parser));
std::vector<SubsampleEntry> subsamples;
ASSERT_OK(generator.GenerateSubsamples(kFrame, kFrameSize, &subsamples));
EXPECT_THAT(subsamples, ElementsAreArray(kExpectedSubsamples));
}
TEST_P(SubsampleGeneratorTest, AACIsFullSampleEncrypted) {
SubsampleGenerator generator(kVP9SubsampleEncryption);
ASSERT_OK(