Only encrypt video slice NAL units.
For non-video slices, the data is not encrypted. This also skips the frame headers for H.264; support for H.265 will be added later. Issue #40 Change-Id: Id0cb0fb9ddb6adedf63ef4aef6b3a26260a21654
This commit is contained in:
parent
74951e3405
commit
96abd90ca2
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -25,6 +25,11 @@ namespace media {
|
|||
/// result, and then copying each byte from the decrypted block over the
|
||||
/// corresponding encrypted byte.
|
||||
struct SubsampleEntry {
|
||||
SubsampleEntry()
|
||||
: clear_bytes(0), cipher_bytes(0) {}
|
||||
SubsampleEntry(uint16_t clear_bytes, uint32_t cipher_bytes)
|
||||
: clear_bytes(clear_bytes), cipher_bytes(cipher_bytes) {}
|
||||
|
||||
uint16_t clear_bytes;
|
||||
uint32_t cipher_bytes;
|
||||
};
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include "packager/media/formats/mp4/encrypting_fragmenter.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "packager/media/base/aes_encryptor.h"
|
||||
#include "packager/media/base/buffer_reader.h"
|
||||
#include "packager/media/base/key_source.h"
|
||||
|
@ -15,33 +17,59 @@
|
|||
#include "packager/media/filters/vp9_parser.h"
|
||||
#include "packager/media/formats/mp4/box_definitions.h"
|
||||
|
||||
namespace {
|
||||
// Generate 64bit IV by default.
|
||||
const size_t kDefaultIvSize = 8u;
|
||||
const size_t kCencBlockSize = 16u;
|
||||
} // namespace
|
||||
|
||||
namespace edash_packager {
|
||||
namespace media {
|
||||
namespace mp4 {
|
||||
|
||||
namespace {
|
||||
// Generate 64bit IV by default.
|
||||
const size_t kDefaultIvSize = 8u;
|
||||
const size_t kCencBlockSize = 16u;
|
||||
|
||||
// Adds one or more subsamples to |*subsamples|. This may add more than one
|
||||
// if one of the values overflows the integer in the subsample.
|
||||
void AddSubsamples(uint64_t clear_bytes,
|
||||
uint64_t cipher_bytes,
|
||||
std::vector<SubsampleEntry>* subsamples) {
|
||||
CHECK_LT(cipher_bytes, std::numeric_limits<uint32_t>::max());
|
||||
const uint64_t kUInt16Max = std::numeric_limits<uint16_t>::max();
|
||||
while (clear_bytes > kUInt16Max) {
|
||||
subsamples->push_back(SubsampleEntry(kUInt16Max, 0));
|
||||
clear_bytes -= kUInt16Max;
|
||||
}
|
||||
|
||||
if (clear_bytes > 0 || cipher_bytes > 0)
|
||||
subsamples->push_back(SubsampleEntry(clear_bytes, cipher_bytes));
|
||||
}
|
||||
|
||||
VideoCodec GetVideoCodec(const StreamInfo& stream_info) {
|
||||
if (stream_info.stream_type() != kStreamVideo)
|
||||
return kUnknownVideoCodec;
|
||||
const VideoStreamInfo& video_stream_info =
|
||||
static_cast<const VideoStreamInfo&>(stream_info);
|
||||
return video_stream_info.codec();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
EncryptingFragmenter::EncryptingFragmenter(
|
||||
scoped_refptr<StreamInfo> info,
|
||||
TrackFragment* traf,
|
||||
scoped_ptr<EncryptionKey> encryption_key,
|
||||
int64_t clear_time,
|
||||
VideoCodec video_codec,
|
||||
uint8_t nalu_length_size)
|
||||
int64_t clear_time)
|
||||
: Fragmenter(traf),
|
||||
info_(info),
|
||||
encryption_key_(encryption_key.Pass()),
|
||||
video_codec_(video_codec),
|
||||
nalu_length_size_(nalu_length_size),
|
||||
clear_time_(clear_time) {
|
||||
DCHECK(encryption_key_);
|
||||
VideoCodec video_codec = GetVideoCodec(*info);
|
||||
if (video_codec == kCodecVP8) {
|
||||
vpx_parser_.reset(new VP8Parser);
|
||||
} else if (video_codec == kCodecVP9) {
|
||||
vpx_parser_.reset(new VP9Parser);
|
||||
} else if (video_codec == kCodecH264) {
|
||||
header_parser_.reset(new H264VideoSliceHeaderParser);
|
||||
}
|
||||
// TODO(modmaker): Support H.265.
|
||||
}
|
||||
|
||||
EncryptingFragmenter::~EncryptingFragmenter() {}
|
||||
|
@ -66,6 +94,9 @@ Status EncryptingFragmenter::InitializeFragment(int64_t first_sample_dts) {
|
|||
if (!status.ok())
|
||||
return status;
|
||||
|
||||
if (header_parser_ && !header_parser_->Initialize(info_->extra_data()))
|
||||
return Status(error::MUXER_FAILURE, "Fail to read SPS and PPS data.");
|
||||
|
||||
traf()->auxiliary_size.sample_info_sizes.clear();
|
||||
traf()->auxiliary_offset.offsets.clear();
|
||||
if (IsSubsampleEncryptionRequired()) {
|
||||
|
@ -182,21 +213,43 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
|
|||
data += frame.frame_size;
|
||||
}
|
||||
} else {
|
||||
NaluReader reader(nalu_length_size_, data, sample->data_size());
|
||||
NaluReader reader(GetNaluLengthSize(), data, sample->data_size());
|
||||
|
||||
// Store the current length of clear data. This is used to squash
|
||||
// multiple unencrypted NAL units into fewer subsample entries.
|
||||
uint64_t accumulated_clear_bytes = 0;
|
||||
|
||||
Nalu nalu;
|
||||
NaluReader::Result result;
|
||||
while ((result = reader.Advance(&nalu)) == NaluReader::kOk) {
|
||||
SubsampleEntry subsample;
|
||||
subsample.clear_bytes = nalu.header_size();
|
||||
subsample.cipher_bytes = nalu.data_size();
|
||||
sample_encryption_entry.subsamples.push_back(subsample);
|
||||
if (nalu.is_video_slice()) {
|
||||
// For video-slice NAL units, encrypt the video slice. This skips
|
||||
// the frame header. If this is an unrecognized codec (e.g. H.265),
|
||||
// the whole NAL unit will be encrypted.
|
||||
const int64_t video_slice_header_size =
|
||||
header_parser_ ? header_parser_->GetHeaderSize(nalu) : 0;
|
||||
if (video_slice_header_size < 0)
|
||||
return Status(error::MUXER_FAILURE, "Failed to read slice header.");
|
||||
|
||||
EncryptBytes(const_cast<uint8_t*>(nalu.data() + nalu.header_size()),
|
||||
subsample.cipher_bytes);
|
||||
const uint64_t current_clear_bytes =
|
||||
nalu.header_size() + video_slice_header_size;
|
||||
const uint64_t cipher_bytes =
|
||||
nalu.data_size() - video_slice_header_size;
|
||||
const uint8_t* nalu_data = nalu.data() + current_clear_bytes;
|
||||
EncryptBytes(const_cast<uint8_t*>(nalu_data), cipher_bytes);
|
||||
|
||||
AddSubsamples(accumulated_clear_bytes + current_clear_bytes,
|
||||
cipher_bytes, &sample_encryption_entry.subsamples);
|
||||
accumulated_clear_bytes = 0;
|
||||
} else {
|
||||
// For non-video-slice NAL units, don't encrypt.
|
||||
accumulated_clear_bytes += nalu.header_size() + nalu.data_size();
|
||||
}
|
||||
}
|
||||
if (result != NaluReader::kEOStream)
|
||||
return Status(error::MUXER_FAILURE, "Failed to parse NAL units.");
|
||||
AddSubsamples(accumulated_clear_bytes, 0,
|
||||
&sample_encryption_entry.subsamples);
|
||||
}
|
||||
|
||||
// The length of per-sample auxiliary datum, defined in CENC ch. 7.
|
||||
|
@ -212,6 +265,19 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
|
|||
return Status::OK;
|
||||
}
|
||||
|
||||
uint8_t EncryptingFragmenter::GetNaluLengthSize() {
|
||||
if (info_->stream_type() != kStreamVideo)
|
||||
return 0;
|
||||
|
||||
const VideoStreamInfo& video_stream_info =
|
||||
static_cast<const VideoStreamInfo&>(*info_);
|
||||
return video_stream_info.nalu_length_size();
|
||||
}
|
||||
|
||||
bool EncryptingFragmenter::IsSubsampleEncryptionRequired() {
|
||||
return vpx_parser_ || GetNaluLengthSize() != 0;
|
||||
}
|
||||
|
||||
} // namespace mp4
|
||||
} // namespace media
|
||||
} // namespace edash_packager
|
||||
|
|
|
@ -7,14 +7,17 @@
|
|||
#ifndef MEDIA_FORMATS_MP4_ENCRYPTING_FRAGMENTER_H_
|
||||
#define MEDIA_FORMATS_MP4_ENCRYPTING_FRAGMENTER_H_
|
||||
|
||||
#include "packager/base/memory/ref_counted.h"
|
||||
#include "packager/base/memory/scoped_ptr.h"
|
||||
#include "packager/media/filters/vpx_parser.h"
|
||||
#include "packager/media/formats/mp4/fragmenter.h"
|
||||
#include "packager/media/formats/mp4/video_slice_header_parser.h"
|
||||
|
||||
namespace edash_packager {
|
||||
namespace media {
|
||||
|
||||
class AesCtrEncryptor;
|
||||
class StreamInfo;
|
||||
struct EncryptionKey;
|
||||
|
||||
namespace mp4 {
|
||||
|
@ -26,16 +29,10 @@ class EncryptingFragmenter : public Fragmenter {
|
|||
/// @param encryption_key contains the encryption parameters.
|
||||
/// @param clear_time specifies clear lead duration in units of the current
|
||||
/// track's timescale.
|
||||
/// @param video_codec specifies the codec if input is a video stream; it
|
||||
/// should be set to kUnknownVideoCodec for audio stream. This
|
||||
/// parameter is used for proper subsample encryption.
|
||||
/// @param nalu_length_size specifies the size of NAL unit length, in bytes,
|
||||
/// for subsample encryption.
|
||||
EncryptingFragmenter(TrackFragment* traf,
|
||||
EncryptingFragmenter(scoped_refptr<StreamInfo> info,
|
||||
TrackFragment* traf,
|
||||
scoped_ptr<EncryptionKey> encryption_key,
|
||||
int64_t clear_time,
|
||||
VideoCodec video_codec,
|
||||
uint8_t nalu_length_size);
|
||||
int64_t clear_time);
|
||||
|
||||
~EncryptingFragmenter() override;
|
||||
|
||||
|
@ -69,23 +66,20 @@ class EncryptingFragmenter : public Fragmenter {
|
|||
void EncryptBytes(uint8_t* data, uint32_t size);
|
||||
Status EncryptSample(scoped_refptr<MediaSample> sample);
|
||||
|
||||
// If this stream contains AVC, subsample encryption specifies that the size
|
||||
// and type of NAL units remain unencrypted. This function returns the size of
|
||||
// the size field in bytes. Can be 1, 2 or 4 bytes.
|
||||
uint8_t GetNaluLengthSize();
|
||||
// Should we enable subsample encryption?
|
||||
bool IsSubsampleEncryptionRequired() {
|
||||
return vpx_parser_ || nalu_length_size_ != 0;
|
||||
}
|
||||
bool IsSubsampleEncryptionRequired();
|
||||
|
||||
scoped_refptr<StreamInfo> info_;
|
||||
scoped_ptr<EncryptionKey> encryption_key_;
|
||||
scoped_ptr<AesCtrEncryptor> encryptor_;
|
||||
// For VP8/VP9, uncompressed_header should not be encrypted; for AVC/HEVC,
|
||||
// the size and type NAL units should not be encrypted.
|
||||
VideoCodec video_codec_;
|
||||
// If this stream contains AVC, subsample encryption specifies that the size
|
||||
// and type of NAL units remain unencrypted. This field specifies the size of
|
||||
// the size field. Can be 1, 2 or 4 bytes.
|
||||
const uint8_t nalu_length_size_;
|
||||
int64_t clear_time_;
|
||||
|
||||
scoped_ptr<VPxParser> vpx_parser_;
|
||||
scoped_ptr<VideoSliceHeaderParser> header_parser_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(EncryptingFragmenter);
|
||||
};
|
||||
|
|
|
@ -18,19 +18,17 @@ const bool kInitialEncryptionInfo = false;
|
|||
} // namespace
|
||||
|
||||
KeyRotationFragmenter::KeyRotationFragmenter(MovieFragment* moof,
|
||||
scoped_refptr<StreamInfo> info,
|
||||
TrackFragment* traf,
|
||||
KeySource* encryption_key_source,
|
||||
KeySource::TrackType track_type,
|
||||
int64_t crypto_period_duration,
|
||||
int64_t clear_time,
|
||||
VideoCodec video_codec,
|
||||
uint8_t nalu_length_size,
|
||||
MuxerListener* muxer_listener)
|
||||
: EncryptingFragmenter(traf,
|
||||
: EncryptingFragmenter(info,
|
||||
traf,
|
||||
scoped_ptr<EncryptionKey>(new EncryptionKey()),
|
||||
clear_time,
|
||||
video_codec,
|
||||
nalu_length_size),
|
||||
clear_time),
|
||||
moof_(moof),
|
||||
encryption_key_source_(encryption_key_source),
|
||||
track_type_(track_type),
|
||||
|
|
|
@ -31,21 +31,15 @@ class KeyRotationFragmenter : public EncryptingFragmenter {
|
|||
/// of the current track's timescale.
|
||||
/// @param clear_time specifies clear lead duration in units of the current
|
||||
/// track's timescale.
|
||||
/// @param video_codec specifies the codec if input is a video stream; it
|
||||
/// should be set to kUnknownVideoCodec for audio stream. This
|
||||
/// parameter is used for proper subsample encryption.
|
||||
/// @param nalu_length_size NAL unit length size, in bytes, for subsample
|
||||
/// encryption.
|
||||
/// @param muxer_listener is a pointer to MuxerListener for notifying
|
||||
/// muxer related events. This may be null.
|
||||
KeyRotationFragmenter(MovieFragment* moof,
|
||||
scoped_refptr<StreamInfo> info,
|
||||
TrackFragment* traf,
|
||||
KeySource* encryption_key_source,
|
||||
KeySource::TrackType track_type,
|
||||
int64_t crypto_period_duration,
|
||||
int64_t clear_time,
|
||||
VideoCodec video_codec,
|
||||
uint8_t nalu_length_size,
|
||||
MuxerListener* muxer_listener);
|
||||
~KeyRotationFragmenter() override;
|
||||
|
||||
|
|
|
@ -52,6 +52,8 @@
|
|||
'sync_sample_iterator.h',
|
||||
'track_run_iterator.cc',
|
||||
'track_run_iterator.h',
|
||||
'video_slice_header_parser.cc',
|
||||
'video_slice_header_parser.h',
|
||||
],
|
||||
'dependencies': [
|
||||
'../../../third_party/boringssl/boringssl.gyp:boringssl',
|
||||
|
@ -74,6 +76,7 @@
|
|||
'mp4_media_parser_unittest.cc',
|
||||
'sync_sample_iterator_unittest.cc',
|
||||
'track_run_iterator_unittest.cc',
|
||||
'video_slice_header_parser_unittest.cc',
|
||||
],
|
||||
'dependencies': [
|
||||
'../../../testing/gtest.gyp:gtest',
|
||||
|
|
|
@ -89,22 +89,6 @@ void GenerateEncryptedSampleEntry(const EncryptionKey& encryption_key,
|
|||
}
|
||||
}
|
||||
|
||||
VideoCodec GetVideoCodec(const StreamInfo& stream_info) {
|
||||
if (stream_info.stream_type() != kStreamVideo)
|
||||
return kUnknownVideoCodec;
|
||||
const VideoStreamInfo& video_stream_info =
|
||||
static_cast<const VideoStreamInfo&>(stream_info);
|
||||
return video_stream_info.codec();
|
||||
}
|
||||
|
||||
uint8_t GetNaluLengthSize(const StreamInfo& stream_info) {
|
||||
if (stream_info.stream_type() != kStreamVideo)
|
||||
return 0;
|
||||
const VideoStreamInfo& video_stream_info =
|
||||
static_cast<const VideoStreamInfo&>(stream_info);
|
||||
return video_stream_info.nalu_length_size();
|
||||
}
|
||||
|
||||
KeySource::TrackType GetTrackTypeForEncryption(const StreamInfo& stream_info,
|
||||
uint32_t max_sd_pixels) {
|
||||
if (stream_info.stream_type() == kStreamAudio)
|
||||
|
@ -168,8 +152,6 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
|||
continue;
|
||||
}
|
||||
|
||||
VideoCodec video_codec = GetVideoCodec(*streams[i]->info());
|
||||
uint8_t nalu_length_size = GetNaluLengthSize(*streams[i]->info());
|
||||
KeySource::TrackType track_type =
|
||||
GetTrackTypeForEncryption(*streams[i]->info(), max_sd_pixels);
|
||||
SampleDescription& description =
|
||||
|
@ -191,10 +173,11 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
|||
}
|
||||
|
||||
fragmenters_[i] = new KeyRotationFragmenter(
|
||||
moof_.get(), &moof_->tracks[i], encryption_key_source, track_type,
|
||||
moof_.get(), streams[i]->info(), &moof_->tracks[i],
|
||||
encryption_key_source, track_type,
|
||||
crypto_period_duration_in_seconds * streams[i]->info()->time_scale(),
|
||||
clear_lead_in_seconds * streams[i]->info()->time_scale(), video_codec,
|
||||
nalu_length_size, muxer_listener_);
|
||||
clear_lead_in_seconds * streams[i]->info()->time_scale(),
|
||||
muxer_listener_);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -222,9 +205,8 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
|||
}
|
||||
|
||||
fragmenters_[i] = new EncryptingFragmenter(
|
||||
&moof_->tracks[i], encryption_key.Pass(),
|
||||
clear_lead_in_seconds * streams[i]->info()->time_scale(), video_codec,
|
||||
nalu_length_size);
|
||||
streams[i]->info(), &moof_->tracks[i], encryption_key.Pass(),
|
||||
clear_lead_in_seconds * streams[i]->info()->time_scale());
|
||||
}
|
||||
|
||||
// Choose the first stream if there is no VIDEO.
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
#include "packager/media/formats/mp4/video_slice_header_parser.h"
|
||||
|
||||
#include "packager/media/formats/mp4/rcheck.h"
|
||||
#include "packager/media/base/buffer_reader.h"
|
||||
|
||||
namespace edash_packager {
|
||||
namespace media {
|
||||
namespace mp4 {
|
||||
|
||||
namespace {
|
||||
const uint8_t kStartCodeSize = 0;
|
||||
}
|
||||
|
||||
H264VideoSliceHeaderParser::H264VideoSliceHeaderParser() {}
|
||||
H264VideoSliceHeaderParser::~H264VideoSliceHeaderParser() {}
|
||||
|
||||
bool H264VideoSliceHeaderParser::Initialize(
|
||||
const std::vector<uint8_t>& decoder_configuration) {
|
||||
// See ISO 14496-15 sec 5.3.3.1.2
|
||||
BufferReader reader(decoder_configuration.data(),
|
||||
decoder_configuration.size());
|
||||
RCHECK(reader.SkipBytes(5));
|
||||
|
||||
uint8_t sps_count;
|
||||
RCHECK(reader.Read1(&sps_count));
|
||||
sps_count = sps_count & 0x1f;
|
||||
|
||||
for (size_t i = 0; i < sps_count; i++) {
|
||||
uint16_t size;
|
||||
RCHECK(reader.Read2(&size));
|
||||
const uint8_t* data = reader.data() + reader.pos();
|
||||
RCHECK(reader.SkipBytes(size));
|
||||
|
||||
int id;
|
||||
Nalu nalu;
|
||||
RCHECK(nalu.InitializeFromH264(data, size, kStartCodeSize));
|
||||
RCHECK(parser_.ParseSPS(nalu, &id) == H264Parser::kOk);
|
||||
}
|
||||
|
||||
uint8_t pps_count;
|
||||
RCHECK(reader.Read1(&pps_count));
|
||||
for (size_t i = 0; i < pps_count; i++) {
|
||||
uint16_t size;
|
||||
RCHECK(reader.Read2(&size));
|
||||
const uint8_t* data = reader.data() + reader.pos();
|
||||
RCHECK(reader.SkipBytes(size));
|
||||
|
||||
int id;
|
||||
Nalu nalu;
|
||||
RCHECK(nalu.InitializeFromH264(data, size, kStartCodeSize));
|
||||
RCHECK(parser_.ParsePPS(nalu, &id) == H264Parser::kOk);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t H264VideoSliceHeaderParser::GetHeaderSize(const Nalu& nalu) {
|
||||
DCHECK(nalu.is_video_slice());
|
||||
H264SliceHeader slice_header;
|
||||
if (parser_.ParseSliceHeader(nalu, &slice_header) != H264Parser::kOk)
|
||||
return -1;
|
||||
|
||||
// Round-up to bytes.
|
||||
return (slice_header.header_bit_size - 1) / 8 + 1;
|
||||
}
|
||||
|
||||
} // namespace mp4
|
||||
} // namespace media
|
||||
} // namespace edash_packager
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
#ifndef MEDIA_FORMATS_MP4_VIDEO_SLICE_HEADER_PARSER_H_
|
||||
#define MEDIA_FORMATS_MP4_VIDEO_SLICE_HEADER_PARSER_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "packager/media/base/macros.h"
|
||||
#include "packager/media/filters/h264_parser.h"
|
||||
|
||||
namespace edash_packager {
|
||||
namespace media {
|
||||
namespace mp4 {
|
||||
|
||||
class VideoSliceHeaderParser {
|
||||
public:
|
||||
VideoSliceHeaderParser() {}
|
||||
virtual ~VideoSliceHeaderParser() {}
|
||||
|
||||
/// Adds decoder configuration from the given data. This must be called
|
||||
/// once before any calls to GetHeaderSize.
|
||||
virtual bool Initialize(
|
||||
const std::vector<uint8_t>& decoder_configuration) = 0;
|
||||
|
||||
/// Gets the header size of the given NALU. Returns < 0 on error.
|
||||
virtual int64_t GetHeaderSize(const Nalu& nalu) = 0;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(VideoSliceHeaderParser);
|
||||
};
|
||||
|
||||
class H264VideoSliceHeaderParser : public VideoSliceHeaderParser {
|
||||
public:
|
||||
H264VideoSliceHeaderParser();
|
||||
~H264VideoSliceHeaderParser() override;
|
||||
|
||||
/// @name VideoSliceHeaderParser implementation overrides.
|
||||
/// @{
|
||||
bool Initialize(const std::vector<uint8_t>& decoder_configuration) override;
|
||||
int64_t GetHeaderSize(const Nalu& nalu) override;
|
||||
/// @}
|
||||
|
||||
private:
|
||||
H264Parser parser_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(H264VideoSliceHeaderParser);
|
||||
};
|
||||
|
||||
// TODO(modmaker): Add H.265 parser.
|
||||
|
||||
} // namespace mp4
|
||||
} // namespace media
|
||||
} // namespace edash_packager
|
||||
|
||||
#endif // MEDIA_FORMATS_MP4_VIDEO_SLICE_HEADER_PARSER_H_
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "packager/media/formats/mp4/video_slice_header_parser.h"
|
||||
|
||||
namespace edash_packager {
|
||||
namespace media {
|
||||
namespace mp4 {
|
||||
|
||||
TEST(H264VideoSliceHeaderParserTest, BasicSupport) {
|
||||
// Taken from bear-640x360.mp4 (video)
|
||||
const uint8_t kExtraData[] = {
|
||||
// Header (ignored)
|
||||
0x01, 0x64, 0x00, 0x1e, 0xff,
|
||||
// SPS count (ignore top three bits)
|
||||
0xe1,
|
||||
// SPS
|
||||
0x00, 0x19, // Size
|
||||
0x67, 0x64, 0x00, 0x1e, 0xac, 0xd9, 0x40, 0xa0,
|
||||
0x2f, 0xf9, 0x70, 0x11, 0x00, 0x00, 0x03, 0x03,
|
||||
0xe9, 0x00, 0x00, 0xea, 0x60, 0x0f, 0x16, 0x2d,
|
||||
0x96,
|
||||
// PPS count
|
||||
0x01,
|
||||
// PPS
|
||||
0x00, 0x06, // Size
|
||||
0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0
|
||||
};
|
||||
const uint8_t kStartCodeSize = 0;
|
||||
const uint8_t kData[] = {
|
||||
// Incomplete data, but we only care about the header size.
|
||||
0x65, 0x88, 0x84, 0x00, 0x21, 0xff, 0xcf, 0x73, 0xc7, 0x24,
|
||||
0xc8, 0xc3, 0xa5, 0xcb, 0x77, 0x60, 0x50, 0x85, 0xd9, 0xfc
|
||||
};
|
||||
const std::vector<uint8_t> extra_data(kExtraData,
|
||||
kExtraData + arraysize(kExtraData));
|
||||
|
||||
H264VideoSliceHeaderParser parser;
|
||||
ASSERT_TRUE(parser.Initialize(extra_data));
|
||||
|
||||
Nalu nalu;
|
||||
ASSERT_TRUE(nalu.InitializeFromH264(kData, arraysize(kData), kStartCodeSize));
|
||||
// Real header size is 34 bits, but we round up to 5 bytes.
|
||||
EXPECT_EQ(5, parser.GetHeaderSize(nalu));
|
||||
}
|
||||
|
||||
TEST(H264VideoSliceHeaderParserTest, SupportsMultipleEntriesInExtraData) {
|
||||
const uint8_t kExtraData[] = {
|
||||
// Header (ignored)
|
||||
0xfe, 0xed, 0xf0, 0x0d, 0x00,
|
||||
// SPS count (ignore top three bits)
|
||||
0xe3,
|
||||
// SPS
|
||||
0x00, 0x19, // Size
|
||||
0x67, 0x64, 0x00, 0x1e, 0xac, 0xd9, 0x40, 0xa0,
|
||||
0x2f, 0xf9, 0x70, 0x11, 0x00, 0x00, 0x03, 0x03,
|
||||
0xe9, 0x00, 0x00, 0xea, 0x60, 0x0f, 0x16, 0x2d,
|
||||
0x96,
|
||||
// SPS
|
||||
0x00, 0x19, // Size
|
||||
0x67, 0x64, 0x00, 0x1e, 0xac, 0xd9, 0x40, 0xa0,
|
||||
0x2f, 0xf9, 0x70, 0x11, 0x00, 0x00, 0x03, 0x03,
|
||||
0xe9, 0x00, 0x00, 0xea, 0x60, 0x0f, 0x16, 0x2d,
|
||||
0x96,
|
||||
// SPS
|
||||
0x00, 0x19, // Size
|
||||
0x67, 0x64, 0x00, 0x1e, 0xac, 0xd9, 0x40, 0xa0,
|
||||
0x2f, 0xf9, 0x70, 0x11, 0x00, 0x00, 0x03, 0x03,
|
||||
0xe9, 0x00, 0x00, 0xea, 0x60, 0x0f, 0x16, 0x2d,
|
||||
0x96,
|
||||
// PPS count
|
||||
0x03,
|
||||
// PPS
|
||||
0x00, 0x06, // Size
|
||||
0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0,
|
||||
// PPS
|
||||
0x00, 0x06, // Size
|
||||
0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0,
|
||||
// PPS
|
||||
0x00, 0x06, // Size
|
||||
0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0
|
||||
};
|
||||
const std::vector<uint8_t> extra_data(kExtraData,
|
||||
kExtraData + arraysize(kExtraData));
|
||||
|
||||
H264VideoSliceHeaderParser parser;
|
||||
EXPECT_TRUE(parser.Initialize(extra_data));
|
||||
}
|
||||
|
||||
TEST(H264VideoSliceHeaderParserTest, IgnoresExtraDataAtEnd) {
|
||||
const uint8_t kExtraData[] = {
|
||||
// Header (ignored)
|
||||
0xfe, 0xed, 0xf0, 0x0d, 0x00,
|
||||
// SPS count
|
||||
0x00,
|
||||
// PPS count
|
||||
0x00,
|
||||
// Extra data
|
||||
0x00, 0x19, 0x67, 0x64, 0x00
|
||||
};
|
||||
const std::vector<uint8_t> extra_data(kExtraData,
|
||||
kExtraData + arraysize(kExtraData));
|
||||
|
||||
H264VideoSliceHeaderParser parser;
|
||||
EXPECT_TRUE(parser.Initialize(extra_data));
|
||||
}
|
||||
|
||||
TEST(H264VideoSliceHeaderParserTest, ErrorsForEOSAfterEntry) {
|
||||
const uint8_t kExtraData[] = {
|
||||
// Header (ignored)
|
||||
0xfe, 0xed, 0xf0, 0x0d, 0x00,
|
||||
// SPS count (ignore top three bits)
|
||||
0xe3,
|
||||
// SPS
|
||||
0x00, 0x19, // Size
|
||||
0x67, 0x64, 0x00, 0x1e, 0xac, 0xd9, 0x40, 0xa0,
|
||||
0x2f, 0xf9, 0x70, 0x11, 0x00, 0x00, 0x03, 0x03,
|
||||
0xe9, 0x00, 0x00, 0xea, 0x60, 0x0f, 0x16, 0x2d,
|
||||
0x96,
|
||||
};
|
||||
const std::vector<uint8_t> extra_data(kExtraData,
|
||||
kExtraData + arraysize(kExtraData));
|
||||
|
||||
H264VideoSliceHeaderParser parser;
|
||||
EXPECT_FALSE(parser.Initialize(extra_data));
|
||||
}
|
||||
|
||||
TEST(H264VideoSliceHeaderParserTest, ErrorsForEOSWithinEntry) {
|
||||
const uint8_t kExtraData[] = {
|
||||
// Header (ignored)
|
||||
0xfe, 0xed, 0xf0, 0x0d, 0x00,
|
||||
// SPS count (ignore top three bits)
|
||||
0xe3,
|
||||
// SPS
|
||||
0x00, 0x19, // Size
|
||||
0x67, 0x64, 0x00, 0x1e, 0xac, 0xd9, 0x40, 0xa0,
|
||||
};
|
||||
const std::vector<uint8_t> extra_data(kExtraData,
|
||||
kExtraData + arraysize(kExtraData));
|
||||
|
||||
H264VideoSliceHeaderParser parser;
|
||||
EXPECT_FALSE(parser.Initialize(extra_data));
|
||||
}
|
||||
|
||||
} // namespace mp4
|
||||
} // namespace media
|
||||
} // namespace edash_packager
|
Loading…
Reference in New Issue