diff --git a/packager/app/test/packager_test.py b/packager/app/test/packager_test.py
index 9098f04941..479b1fd820 100755
--- a/packager/app/test/packager_test.py
+++ b/packager/app/test/packager_test.py
@@ -84,6 +84,13 @@ class PackagerAppTest(unittest.TestCase):
self._DiffGold(self.output[1], 'bear-640x360-v-cenc-golden.mp4')
self._DiffGold(self.mpd_output, 'bear-640x360-av-cenc-golden.mpd')
+ def testPackageHevcWithEncryption(self):
+ self.packager.Package(self._GetStreams(['video'],
+ test_file='bear-640x360-hevc.mp4'),
+ self._GetFlags(encryption=True))
+ self._DiffGold(self.output[0], 'bear-640x360-hevc-v-cenc-golden.mp4')
+ self._DiffGold(self.mpd_output, 'bear-640x360-hevc-v-cenc-golden.mpd')
+
def testPackageWithEncryptionAndRandomIv(self):
self.packager.Package(self._GetStreams(['audio', 'video']),
self._GetFlags(encryption=True, random_iv=True))
@@ -198,13 +205,14 @@ class PackagerAppTest(unittest.TestCase):
self._AssertStreamInfo(self.output[0], 'is_encrypted: true')
self._AssertStreamInfo(self.output[1], 'is_encrypted: true')
- def _GetStreams(self, stream_descriptors, live = False):
+ def _GetStreams(self, stream_descriptors, live = False,
+ test_file = 'bear-640x360.mp4'):
streams = []
self.output = []
test_data_dir = os.path.join(
test_env.SRC_DIR, 'packager', 'media', 'test', 'data')
- input = os.path.join(test_data_dir, 'bear-640x360.mp4')
+ input = os.path.join(test_data_dir, test_file)
for stream_descriptor in stream_descriptors:
if live:
# This is still output prefix actually.
diff --git a/packager/app/test/testdata/bear-640x360-hevc-v-cenc-golden.mp4 b/packager/app/test/testdata/bear-640x360-hevc-v-cenc-golden.mp4
new file mode 100644
index 0000000000..5213040a3e
Binary files /dev/null and b/packager/app/test/testdata/bear-640x360-hevc-v-cenc-golden.mp4 differ
diff --git a/packager/app/test/testdata/bear-640x360-hevc-v-cenc-golden.mpd b/packager/app/test/testdata/bear-640x360-hevc-v-cenc-golden.mpd
new file mode 100644
index 0000000000..391d6e2850
--- /dev/null
+++ b/packager/app/test/testdata/bear-640x360-hevc-v-cenc-golden.mpd
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+ AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2
+
+ output_video.mp4
+
+
+
+
+
+
+
diff --git a/packager/media/base/video_stream_info.cc b/packager/media/base/video_stream_info.cc
index fad76d10c4..2ec7fa51a2 100644
--- a/packager/media/base/video_stream_info.cc
+++ b/packager/media/base/video_stream_info.cc
@@ -21,6 +21,10 @@ std::string VideoCodecToString(VideoCodec video_codec) {
switch (video_codec) {
case kCodecH264:
return "H264";
+ case kCodecHEV1:
+ return "HEV1";
+ case kCodecHVC1:
+ return "HVC1";
case kCodecVC1:
return "VC1";
case kCodecMPEG2:
diff --git a/packager/media/base/video_stream_info.h b/packager/media/base/video_stream_info.h
index 2992e65b59..6786d5457f 100644
--- a/packager/media/base/video_stream_info.h
+++ b/packager/media/base/video_stream_info.h
@@ -15,6 +15,8 @@ namespace media {
enum VideoCodec {
kUnknownVideoCodec = 0,
kCodecH264,
+ kCodecHEV1,
+ kCodecHVC1,
kCodecVC1,
kCodecMPEG2,
kCodecMPEG4,
diff --git a/packager/media/filters/filters.gyp b/packager/media/filters/filters.gyp
index f836dec71f..dc36a9a225 100644
--- a/packager/media/filters/filters.gyp
+++ b/packager/media/filters/filters.gyp
@@ -15,6 +15,8 @@
'sources': [
'avc_decoder_configuration.cc',
'avc_decoder_configuration.h',
+ 'hevc_decoder_configuration.cc',
+ 'hevc_decoder_configuration.h',
'h264_bit_reader.cc',
'h264_bit_reader.h',
'h264_byte_to_unit_stream_converter.cc',
@@ -36,6 +38,7 @@
'h264_bit_reader_unittest.cc',
'h264_byte_to_unit_stream_converter_unittest.cc',
'h264_parser_unittest.cc',
+ 'hevc_decoder_configuration_unittest.cc',
'vp_codec_configuration_unittest.cc',
],
'dependencies': [
diff --git a/packager/media/filters/hevc_decoder_configuration.cc b/packager/media/filters/hevc_decoder_configuration.cc
new file mode 100644
index 0000000000..b265a020c8
--- /dev/null
+++ b/packager/media/filters/hevc_decoder_configuration.cc
@@ -0,0 +1,139 @@
+// Copyright 2015 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/filters/hevc_decoder_configuration.h"
+
+#include "packager/base/stl_util.h"
+#include "packager/base/strings/string_number_conversions.h"
+#include "packager/base/strings/string_util.h"
+#include "packager/media/base/buffer_reader.h"
+#include "packager/media/formats/mp4/rcheck.h"
+
+namespace edash_packager {
+namespace media {
+
+namespace {
+
+// ISO/IEC 14496-15:2014 Annex E.
+std::string GeneralProfileSpaceAsString(uint8_t general_profile_space) {
+ switch (general_profile_space) {
+ case 0:
+ return "";
+ case 1:
+ return "A";
+ case 2:
+ return "B";
+ case 3:
+ return "C";
+ default:
+ LOG(WARNING) << "Unexpected general_profile_space "
+ << general_profile_space;
+ return "";
+ }
+}
+
+std::string TrimLeadingZeros(const std::string& str) {
+ DCHECK_GT(str.size(), 0u);
+ for (size_t i = 0; i < str.size(); ++i) {
+ if (str[i] == '0') continue;
+ return str.substr(i);
+ }
+ return "0";
+}
+
+// Encode the 32 bits input, but in reverse bit order, i.e. bit [31] as the most
+// significant bit, followed by, bit [30], and down to bit [0] as the least
+// significant bit, where bits [i] for i in the range of 0 to 31, inclusive, are
+// specified in ISO/IEC 23008‐2, encoded in hexadecimal (leading zeroes may be
+// omitted).
+std::string ReverseBitsAndHexEncode(uint32_t x) {
+ x = ((x & 0x55555555) << 1) | ((x & 0xAAAAAAAA) >> 1);
+ x = ((x & 0x33333333) << 2) | ((x & 0xCCCCCCCC) >> 2);
+ x = ((x & 0x0F0F0F0F) << 4) | ((x & 0xF0F0F0F0) >> 4);
+ const uint8_t bytes[] = {static_cast(x & 0xFF),
+ static_cast((x >> 8) & 0xFF),
+ static_cast((x >> 16) & 0xFF),
+ static_cast((x >> 24) & 0xFF)};
+ return TrimLeadingZeros(base::HexEncode(bytes, arraysize(bytes)));
+}
+
+std::string CodecAsString(VideoCodec codec) {
+ switch (codec) {
+ case kCodecHEV1:
+ return "hev1";
+ case kCodecHVC1:
+ return "hvc1";
+ default:
+ LOG(WARNING) << "Unknown codec: " << codec;
+ return std::string();
+ }
+}
+
+} // namespace
+
+HEVCDecoderConfiguration::HEVCDecoderConfiguration()
+ : version_(0),
+ general_profile_space_(0),
+ general_tier_flag_(false),
+ general_profile_idc_(0),
+ general_profile_compatibility_flags_(0),
+ general_level_idc_(0),
+ length_size_(0) {}
+
+HEVCDecoderConfiguration::~HEVCDecoderConfiguration() {}
+
+bool HEVCDecoderConfiguration::Parse(const std::vector& data) {
+ BufferReader reader(vector_as_array(&data), data.size());
+
+ uint8_t profile_indication = 0;
+ uint8_t length_size_minus_one = 0;
+ uint8_t num_of_arrays = 0;
+ RCHECK(reader.Read1(&version_) && version_ == 1 &&
+ reader.Read1(&profile_indication) &&
+ reader.Read4(&general_profile_compatibility_flags_) &&
+ reader.ReadToVector(&general_constraint_indicator_flags_, 6) &&
+ reader.Read1(&general_level_idc_) &&
+ reader.SkipBytes(8) && // Skip uninterested fields.
+ reader.Read1(&length_size_minus_one) &&
+ reader.Read1(&num_of_arrays));
+
+ general_profile_space_ = profile_indication >> 6;
+ RCHECK(general_profile_space_ <= 3u);
+ general_tier_flag_ = ((profile_indication >> 5) & 1) == 1;
+ general_profile_idc_ = profile_indication & 0x1f;
+
+ length_size_ = (length_size_minus_one & 0x3) + 1;
+
+ // TODO(kqyang): Parse SPS to get resolutions.
+ return true;
+}
+
+std::string HEVCDecoderConfiguration::GetCodecString(VideoCodec codec) const {
+ // ISO/IEC 14496-15:2014 Annex E.
+ std::vector fields;
+ fields.push_back(CodecAsString(codec));
+ fields.push_back(GeneralProfileSpaceAsString(general_profile_space_) +
+ base::IntToString(general_profile_idc_));
+ fields.push_back(
+ ReverseBitsAndHexEncode(general_profile_compatibility_flags_));
+ fields.push_back((general_tier_flag_ ? "H" : "L") +
+ base::IntToString(general_level_idc_));
+
+ // Remove trailing bytes that are zero.
+ std::vector constraints = general_constraint_indicator_flags_;
+ size_t size = constraints.size();
+ for (; size > 0; --size) {
+ if (constraints[size - 1] != 0) break;
+ }
+ constraints.resize(size);
+ for (uint8_t constraint : constraints)
+ fields.push_back(TrimLeadingZeros(base::HexEncode(&constraint, 1)));
+
+ return base::JoinString(fields, ".");
+}
+
+} // namespace media
+} // namespace edash_packager
diff --git a/packager/media/filters/hevc_decoder_configuration.h b/packager/media/filters/hevc_decoder_configuration.h
new file mode 100644
index 0000000000..2783c86f2e
--- /dev/null
+++ b/packager/media/filters/hevc_decoder_configuration.h
@@ -0,0 +1,52 @@
+// Copyright 2015 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_FILTERS_HEVC_DECODER_CONFIGURATION_H_
+#define MEDIA_FILTERS_HEVC_DECODER_CONFIGURATION_H_
+
+#include
+#include
+#include
+
+#include "packager/base/macros.h"
+#include "packager/media/base/video_stream_info.h"
+
+namespace edash_packager {
+namespace media {
+
+/// Class for parsing HEVC decoder configuration.
+class HEVCDecoderConfiguration {
+ public:
+ HEVCDecoderConfiguration();
+ ~HEVCDecoderConfiguration();
+
+ /// Parses input to extract HEVC decoder configuration data.
+ /// @return false if there is parsing errors.
+ bool Parse(const std::vector& data);
+
+ /// @return The codec string.
+ std::string GetCodecString(VideoCodec codec) const;
+
+ /// @return The size of the NAL unit length field.
+ uint8_t length_size() { return length_size_; }
+
+ private:
+ uint8_t version_;
+ uint8_t general_profile_space_;
+ bool general_tier_flag_;
+ uint8_t general_profile_idc_;
+ uint32_t general_profile_compatibility_flags_;
+ std::vector general_constraint_indicator_flags_;
+ uint8_t general_level_idc_;
+ uint8_t length_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(HEVCDecoderConfiguration);
+};
+
+} // namespace media
+} // namespace edash_packager
+
+#endif // MEDIA_FILTERS_HEVC_DECODER_CONFIGURATION_H_
diff --git a/packager/media/filters/hevc_decoder_configuration_unittest.cc b/packager/media/filters/hevc_decoder_configuration_unittest.cc
new file mode 100644
index 0000000000..d0f2810644
--- /dev/null
+++ b/packager/media/filters/hevc_decoder_configuration_unittest.cc
@@ -0,0 +1,47 @@
+// Copyright 2015 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/filters/hevc_decoder_configuration.h"
+
+#include
+
+namespace edash_packager {
+namespace media {
+
+TEST(HEVCDecoderConfigurationTest, Success) {
+ const uint8_t kHevcDecoderConfigurationData[] = {
+ 0x01, 0x02, 0x20, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3F, 0xF0, 0x00, 0xFC, 0xFD, 0xFA, 0xFA, 0x00, 0x00, 0x0F, 0x04, 0x20,
+ 0x00, 0x01, 0x00, 0x18, 0x40, 0x01, 0x0C, 0x01, 0xFF, 0xFF, 0x02, 0x20,
+ 0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
+ 0x3F, 0x99, 0x98, 0x09, 0x21, 0x00, 0x01, 0x00, 0x29, 0x42, 0x01, 0x01,
+ 0x02, 0x20, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00,
+ };
+
+ HEVCDecoderConfiguration hevc_config;
+ ASSERT_TRUE(hevc_config.Parse(
+ std::vector(kHevcDecoderConfigurationData,
+ kHevcDecoderConfigurationData +
+ arraysize(kHevcDecoderConfigurationData))));
+
+ EXPECT_EQ(4u, hevc_config.length_size());
+
+ EXPECT_EQ("hev1.2.4.L63.90", hevc_config.GetCodecString(kCodecHEV1));
+ EXPECT_EQ("hvc1.2.4.L63.90", hevc_config.GetCodecString(kCodecHVC1));
+}
+
+TEST(HEVCDecoderConfigurationTest, FailOnInsufficientData) {
+ const uint8_t kHevcDecoderConfigurationData[] = {0x01, 0x02, 0x20, 0x00};
+
+ HEVCDecoderConfiguration hevc_config;
+ ASSERT_FALSE(hevc_config.Parse(
+ std::vector(kHevcDecoderConfigurationData,
+ kHevcDecoderConfigurationData +
+ arraysize(kHevcDecoderConfigurationData))));
+}
+
+} // namespace media
+} // namespace edash_packager
diff --git a/packager/media/formats/mp4/box_definitions.cc b/packager/media/formats/mp4/box_definitions.cc
index ea3ba169dc..62acd32148 100644
--- a/packager/media/formats/mp4/box_definitions.cc
+++ b/packager/media/formats/mp4/box_definitions.cc
@@ -37,6 +37,7 @@ const uint16_t kVideoDepth = 0x0018;
const uint32_t kCompressorNameSize = 32u;
const char kAvcCompressorName[] = "\012AVC Coding";
+const char kHevcCompressorName[] = "\013HEVC Coding";
const char kVpcCompressorName[] = "\012VPC Coding";
// Utility functions to check if the 64bit integers can fit in 32bit integer.
@@ -950,6 +951,12 @@ bool VideoSampleEntry::ReadWrite(BoxBuffer* buffer) {
kAvcCompressorName,
kAvcCompressorName + arraysize(kAvcCompressorName));
break;
+ case FOURCC_HEV1:
+ case FOURCC_HVC1:
+ compressor_name.assign(
+ kHevcCompressorName,
+ kHevcCompressorName + arraysize(kHevcCompressorName));
+ break;
case FOURCC_VP08:
case FOURCC_VP09:
case FOURCC_VP10:
@@ -1001,6 +1008,10 @@ bool VideoSampleEntry::ReadWrite(BoxBuffer* buffer) {
case FOURCC_AVC1:
codec_config_record.box_type = FOURCC_AVCC;
break;
+ case FOURCC_HEV1:
+ case FOURCC_HVC1:
+ codec_config_record.box_type = FOURCC_HVCC;
+ break;
case FOURCC_VP08:
case FOURCC_VP09:
case FOURCC_VP10:
@@ -1680,7 +1691,9 @@ bool SampleToGroup::ReadWrite(BoxBuffer* buffer) {
if (grouping_type != FOURCC_SEIG) {
DCHECK(buffer->Reading());
- DLOG(WARNING) << "Sample group '" << grouping_type << "' is not supported.";
+ DLOG(WARNING) << "Sample group "
+ << FourCCToString(static_cast(grouping_type))
+ << " is not supported.";
return true;
}
diff --git a/packager/media/formats/mp4/fourccs.h b/packager/media/formats/mp4/fourccs.h
index e472f870a9..19b603cd57 100644
--- a/packager/media/formats/mp4/fourccs.h
+++ b/packager/media/formats/mp4/fourccs.h
@@ -32,7 +32,10 @@ enum FourCC {
FOURCC_FRMA = 0x66726d61,
FOURCC_FTYP = 0x66747970,
FOURCC_HDLR = 0x68646c72,
+ FOURCC_HEV1 = 0x68657631,
FOURCC_HINT = 0x68696e74,
+ FOURCC_HVC1 = 0x68766331,
+ FOURCC_HVCC = 0x68766343,
FOURCC_ISO6 = 0x69736f36,
FOURCC_IODS = 0x696f6473,
FOURCC_MDAT = 0x6d646174,
diff --git a/packager/media/formats/mp4/mp4_media_parser.cc b/packager/media/formats/mp4/mp4_media_parser.cc
index ecefe7da52..98d85f1f2f 100644
--- a/packager/media/formats/mp4/mp4_media_parser.cc
+++ b/packager/media/formats/mp4/mp4_media_parser.cc
@@ -21,6 +21,7 @@
#include "packager/media/file/file.h"
#include "packager/media/file/file_closer.h"
#include "packager/media/filters/avc_decoder_configuration.h"
+#include "packager/media/filters/hevc_decoder_configuration.h"
#include "packager/media/filters/vp_codec_configuration.h"
#include "packager/media/formats/mp4/box_definitions.h"
#include "packager/media/formats/mp4/box_reader.h"
@@ -43,6 +44,10 @@ VideoCodec FourCCToCodec(FourCC fourcc) {
switch (fourcc) {
case FOURCC_AVC1:
return kCodecH264;
+ case FOURCC_HEV1:
+ return kCodecHEV1;
+ case FOURCC_HVC1:
+ return kCodecHVC1;
case FOURCC_VP08:
return kCodecVP8;
case FOURCC_VP09:
@@ -419,6 +424,17 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
}
break;
}
+ case FOURCC_HEV1:
+ case FOURCC_HVC1: {
+ HEVCDecoderConfiguration hevc_config;
+ if (!hevc_config.Parse(entry.codec_config_record.data)) {
+ LOG(ERROR) << "Failed to parse hevc.";
+ return false;
+ }
+ codec_string = hevc_config.GetCodecString(video_codec);
+ nalu_length_size = hevc_config.length_size();
+ break;
+ }
case FOURCC_VP08:
case FOURCC_VP09:
case FOURCC_VP10: {
diff --git a/packager/media/formats/mp4/mp4_muxer.cc b/packager/media/formats/mp4/mp4_muxer.cc
index 1901aec947..762bc69a28 100644
--- a/packager/media/formats/mp4/mp4_muxer.cc
+++ b/packager/media/formats/mp4/mp4_muxer.cc
@@ -44,6 +44,10 @@ FourCC CodecToFourCC(VideoCodec codec) {
switch (codec) {
case kCodecH264:
return FOURCC_AVC1;
+ case kCodecHEV1:
+ return FOURCC_HEV1;
+ case kCodecHVC1:
+ return FOURCC_HVC1;
case kCodecVP8:
return FOURCC_VP08;
case kCodecVP9:
diff --git a/packager/media/test/data/README b/packager/media/test/data/README
index 14bcf67f53..79dc67b7aa 100644
--- a/packager/media/test/data/README
+++ b/packager/media/test/data/README
@@ -31,6 +31,7 @@ bear-1280x720_ptswraparound.ts - Same as bear-1280x720.ts, with a timestamp wrap
// ISO-BMFF streams.
bear-1280x720.mp4 - AVC + AAC encode, mulitplexed into an ISOBMFF container.
bear-640x360.mp4 - Same as above, but in a different resolution.
+bear-640x360-hevc.mp4 - Same content, but encoded with HEVC.
bear-320x180.mp4 - Same as above, but in a different resolution.
bear-640x360-trailing-moov.mp4 - Same content, but with moov box in the end.
bear-640x360-av_frag.mp4 - Same content, but in fragmented mp4.
diff --git a/packager/media/test/data/bear-640x360-hevc.mp4 b/packager/media/test/data/bear-640x360-hevc.mp4
new file mode 100644
index 0000000000..b543c9a48f
Binary files /dev/null and b/packager/media/test/data/bear-640x360-hevc.mp4 differ