Implement initial support for vpx in iso-bmff

Also update the code to generate CompressorName in VideoSampleEntry
for both AVC1 and various VP codecs.

Change-Id: I2355f8008a72806e852aa1ec6c80e9141b97d963
This commit is contained in:
KongQun Yang 2015-10-26 13:50:22 -07:00
parent 749571fc07
commit c577e6132f
19 changed files with 379 additions and 25 deletions

View File

@ -17,6 +17,7 @@
#include "packager/media/file/file.h"
#include "packager/media/formats/mp2t/mp2t_media_parser.h"
#include "packager/media/formats/mp4/mp4_media_parser.h"
#include "packager/media/formats/webm/webm_media_parser.h"
#include "packager/media/formats/wvm/wvm_media_parser.h"
namespace {
@ -83,6 +84,9 @@ Status Demuxer::Initialize() {
case CONTAINER_MPEG2PS:
parser_.reset(new wvm::WvmMediaParser());
break;
case CONTAINER_WEBM:
parser_.reset(new WebMMediaParser());
break;
default:
NOTIMPLEMENTED();
return Status(error::UNIMPLEMENTED, "Container not supported.");

View File

@ -33,6 +33,8 @@ std::string VideoCodecToString(VideoCodec video_codec) {
return "VP8";
case kCodecVP9:
return "VP9";
case kCodecVP10:
return "VP10";
default:
NOTIMPLEMENTED() << "Unknown Video Codec: " << video_codec;
return "UnknownVideoCodec";

View File

@ -21,6 +21,7 @@ enum VideoCodec {
kCodecTheora,
kCodecVP8,
kCodecVP9,
kCodecVP10,
kNumVideoCodec
};

View File

@ -21,6 +21,8 @@
'h264_byte_to_unit_stream_converter.h',
'h264_parser.cc',
'h264_parser.h',
'vp_codec_configuration.cc',
'vp_codec_configuration.h',
],
'dependencies': [
'../../base/base.gyp:base',
@ -34,6 +36,7 @@
'h264_bit_reader_unittest.cc',
'h264_byte_to_unit_stream_converter_unittest.cc',
'h264_parser_unittest.cc',
'vp_codec_configuration_unittest.cc',
],
'dependencies': [
'../../media/base/media_base.gyp:base',

View File

@ -0,0 +1,121 @@
// 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/vp_codec_configuration.h"
#include "packager/base/strings/string_number_conversions.h"
#include "packager/base/strings/string_util.h"
#include "packager/media/base/bit_reader.h"
#include "packager/media/base/buffer_writer.h"
#include "packager/base/strings/stringprintf.h"
#include "packager/media/formats/mp4/rcheck.h"
namespace edash_packager {
namespace media {
namespace {
std::string VPCodecAsString(VideoCodec codec) {
switch (codec) {
case kCodecVP8:
return "vp08";
case kCodecVP9:
return "vp09";
case kCodecVP10:
return "vp10";
default:
LOG(WARNING) << "Unknown VP codec: " << codec;
return std::string();
}
}
} // namespace
VPCodecConfiguration::VPCodecConfiguration()
: profile_(0),
level_(0),
bit_depth_(0),
color_space_(0),
chroma_subsampling_(0),
transfer_function_(0),
video_full_range_flag_(false) {}
VPCodecConfiguration::VPCodecConfiguration(
uint8_t profile,
uint8_t level,
uint8_t bit_depth,
uint8_t color_space,
uint8_t chroma_subsampling,
uint8_t transfer_function,
bool video_full_range_flag,
const std::vector<uint8_t>& codec_initialization_data)
: profile_(profile),
level_(level),
bit_depth_(bit_depth),
color_space_(color_space),
chroma_subsampling_(chroma_subsampling),
transfer_function_(transfer_function),
video_full_range_flag_(video_full_range_flag),
codec_initialization_data_(codec_initialization_data) {}
VPCodecConfiguration::~VPCodecConfiguration(){};
bool VPCodecConfiguration::Parse(const std::vector<uint8_t>& data) {
BitReader reader(vector_as_array(&data), data.size());
RCHECK(reader.ReadBits(8, &profile_));
RCHECK(reader.ReadBits(8, &level_));
RCHECK(reader.ReadBits(4, &bit_depth_));
RCHECK(reader.ReadBits(4, &color_space_));
RCHECK(reader.ReadBits(4, &chroma_subsampling_));
RCHECK(reader.ReadBits(3, &transfer_function_));
RCHECK(reader.ReadBits(1, &video_full_range_flag_));
uint16_t codec_initialization_data_size = 0;
RCHECK(reader.ReadBits(16, &codec_initialization_data_size));
RCHECK(reader.bits_available() >= codec_initialization_data_size * 8);
const size_t kHeaderSize = 6u; // Size of bytes read so far.
codec_initialization_data_.assign(
data.begin() + kHeaderSize,
data.begin() + kHeaderSize + codec_initialization_data_size);
return true;
}
void VPCodecConfiguration::Write(std::vector<uint8_t>* data) const {
BufferWriter writer;
writer.AppendInt(profile_);
writer.AppendInt(level_);
uint8_t bit_depth_color_space = (bit_depth_ << 4) | color_space_;
writer.AppendInt(bit_depth_color_space);
uint8_t chroma = (chroma_subsampling_ << 4) | (transfer_function_ << 1) |
(video_full_range_flag_ ? 1 : 0);
writer.AppendInt(chroma);
uint16_t codec_initialization_data_size = codec_initialization_data_.size();
writer.AppendInt(codec_initialization_data_size);
writer.AppendVector(codec_initialization_data_);
writer.SwapBuffer(data);
}
std::string VPCodecConfiguration::GetCodecString(VideoCodec codec) const {
const std::string fields[] = {
base::IntToString(profile_),
base::IntToString(level_),
base::IntToString(bit_depth_),
base::IntToString(color_space_),
base::IntToString(chroma_subsampling_),
base::IntToString(transfer_function_),
(video_full_range_flag_ ? "01" : "00"),
};
std::string codec_string = VPCodecAsString(codec);
for (const std::string& field : fields) {
// Make sure every field is at least 2-chars wide. The space will be
// replaced with '0' afterwards.
base::StringAppendF(&codec_string, ".%2s", field.c_str());
}
base::ReplaceChars(codec_string, " ", "0", &codec_string);
return codec_string;
}
} // namespace media
} // namespace edash_packager

View File

@ -0,0 +1,69 @@
// 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_VP_CODEC_CONFIGURATION_H_
#define MEDIA_FILTERS_VP_CODEC_CONFIGURATION_H_
#include <stdint.h>
#include <string>
#include <vector>
#include "packager/base/macros.h"
#include "packager/media/base/video_stream_info.h"
namespace edash_packager {
namespace media {
/// Class for parsing or writing VP codec configuration data.
class VPCodecConfiguration {
public:
VPCodecConfiguration();
VPCodecConfiguration(uint8_t profile,
uint8_t level,
uint8_t bit_depth,
uint8_t color_space,
uint8_t chroma_subsampling,
uint8_t transfer_function,
bool video_full_range_flag,
const std::vector<uint8_t>& codec_initialization_data);
~VPCodecConfiguration();
/// Parses input to extract VP codec configuration data.
/// @return false if there is parsing errors.
bool Parse(const std::vector<uint8_t>& data);
/// @param data should not be null.
/// Writes VP codec configuration data to buffer.
void Write(std::vector<uint8_t>* data) const;
/// @return The codec string.
std::string GetCodecString(VideoCodec codec) const;
uint8_t profile() const { return profile_; }
uint8_t level() const { return level_; }
uint8_t bit_depth() const { return bit_depth_; }
uint8_t color_space() const { return color_space_; }
uint8_t chroma_subsampling() const { return chroma_subsampling_; }
uint8_t transfer_function() const { return transfer_function_; }
bool video_full_range_flag() const { return video_full_range_flag_; }
private:
uint8_t profile_;
uint8_t level_;
uint8_t bit_depth_;
uint8_t color_space_;
uint8_t chroma_subsampling_;
uint8_t transfer_function_;
bool video_full_range_flag_;
std::vector<uint8_t> codec_initialization_data_;
DISALLOW_COPY_AND_ASSIGN(VPCodecConfiguration);
};
} // namespace media
} // namespace edash_packager
#endif // MEDIA_FILTERS_VP_CODEC_CONFIGURATION_H_

View File

@ -0,0 +1,63 @@
// 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/vp_codec_configuration.h"
#include <gtest/gtest.h>
namespace edash_packager {
namespace media {
TEST(VPCodecConfigurationTest, Parse) {
const uint8_t kVpCodecConfigurationData[] = {
0x01, 0x00, 0xA2, 0x14, 0x00, 0x01, 0x00,
};
VPCodecConfiguration vp_config;
ASSERT_TRUE(vp_config.Parse(std::vector<uint8_t>(
kVpCodecConfigurationData,
kVpCodecConfigurationData + arraysize(kVpCodecConfigurationData))));
EXPECT_EQ(1u, vp_config.profile());
EXPECT_EQ(0u, vp_config.level());
EXPECT_EQ(10u, vp_config.bit_depth());
EXPECT_EQ(2u, vp_config.color_space());
EXPECT_EQ(1u, vp_config.chroma_subsampling());
EXPECT_EQ(2u, vp_config.transfer_function());
EXPECT_FALSE(vp_config.video_full_range_flag());
EXPECT_EQ("vp09.01.00.10.02.01.02.00", vp_config.GetCodecString(kCodecVP9));
}
TEST(VPCodecConfigurationTest, ParseWithInsufficientData) {
const uint8_t kVpCodecConfigurationData[] = {
0x01, 0x00, 0xA2, 0x14,
};
VPCodecConfiguration vp_config;
ASSERT_FALSE(vp_config.Parse(std::vector<uint8_t>(
kVpCodecConfigurationData,
kVpCodecConfigurationData + arraysize(kVpCodecConfigurationData))));
}
TEST(VPCodecConfigurationTest, Write) {
const uint8_t kExpectedVpCodecConfigurationData[] = {
0x02, 0x01, 0x80, 0x21, 0x00, 0x00,
};
VPCodecConfiguration vp_config(0x02, 0x01, 0x08, 0x00, 0x02, 0x00, true,
std::vector<uint8_t>());
std::vector<uint8_t> data;
vp_config.Write(&data);
EXPECT_EQ(
std::vector<uint8_t>(kExpectedVpCodecConfigurationData,
kExpectedVpCodecConfigurationData +
arraysize(kExpectedVpCodecConfigurationData)),
data);
}
} // namespace media
} // namespace edash_packager

View File

@ -36,6 +36,8 @@ const uint16_t kVideoFrameCount = 1;
const uint16_t kVideoDepth = 0x0018;
const uint32_t kCompressorNameSize = 32u;
const char kAvcCompressorName[] = "\012AVC Coding";
const char kVpcCompressorName[] = "\012VPC Coding";
// Utility functions to check if the 64bit integers can fit in 32bit integer.
bool IsFitIn32Bits(uint64_t a) {
@ -933,12 +935,33 @@ FourCC VideoSampleEntry::BoxType() const {
}
bool VideoSampleEntry::ReadWrite(BoxBuffer* buffer) {
std::vector<uint8_t> compressor_name;
if (buffer->Reading()) {
DCHECK(buffer->reader());
format = buffer->reader()->type();
} else {
RCHECK(buffer->ReadWriteUInt32(&atom_size) &&
buffer->ReadWriteFourCC(&format));
const FourCC actual_format = GetActualFormat();
switch (actual_format) {
case FOURCC_AVC1:
compressor_name.assign(
kAvcCompressorName,
kAvcCompressorName + arraysize(kAvcCompressorName));
break;
case FOURCC_VP08:
case FOURCC_VP09:
case FOURCC_VP10:
compressor_name.assign(
kVpcCompressorName,
kVpcCompressorName + arraysize(kVpcCompressorName));
break;
default:
LOG(ERROR) << FourCCToString(actual_format) << " is not supported.";
return false;
}
compressor_name.resize(kCompressorNameSize);
}
uint32_t video_resolution = kVideoResolution;
@ -954,7 +977,7 @@ bool VideoSampleEntry::ReadWrite(BoxBuffer* buffer) {
buffer->ReadWriteUInt32(&video_resolution) &&
buffer->IgnoreBytes(4) && // reserved.
buffer->ReadWriteUInt16(&video_frame_count) &&
buffer->IgnoreBytes(32) && // comparessor_name.
buffer->ReadWriteVector(&compressor_name, kCompressorNameSize) &&
buffer->ReadWriteUInt16(&video_depth) &&
buffer->ReadWriteInt16(&predefined));
@ -978,6 +1001,11 @@ bool VideoSampleEntry::ReadWrite(BoxBuffer* buffer) {
case FOURCC_AVC1:
codec_config_record.box_type = FOURCC_AVCC;
break;
case FOURCC_VP08:
case FOURCC_VP09:
case FOURCC_VP10:
codec_config_record.box_type = FOURCC_VPCC;
break;
default:
LOG(ERROR) << FourCCToString(actual_format) << " is not supported.";
return false;

View File

@ -92,6 +92,10 @@ enum FourCC {
FOURCC_UUID = 0x75756964,
FOURCC_VIDE = 0x76696465,
FOURCC_VMHD = 0x766d6864,
FOURCC_VP08 = 0x76703038,
FOURCC_VP09 = 0x76703039,
FOURCC_VP10 = 0x76703130,
FOURCC_VPCC = 0x76706343,
FOURCC_WIDE = 0x77696465,
};

View File

@ -21,12 +21,16 @@
#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/vp_codec_configuration.h"
#include "packager/media/formats/mp4/box_definitions.h"
#include "packager/media/formats/mp4/box_reader.h"
#include "packager/media/formats/mp4/es_descriptor.h"
#include "packager/media/formats/mp4/rcheck.h"
#include "packager/media/formats/mp4/track_run_iterator.h"
namespace edash_packager {
namespace media {
namespace mp4 {
namespace {
uint64_t Rescale(uint64_t time_in_old_scale,
@ -35,15 +39,25 @@ uint64_t Rescale(uint64_t time_in_old_scale,
return (static_cast<double>(time_in_old_scale) / old_scale) * new_scale;
}
VideoCodec FourCCToCodec(FourCC fourcc) {
switch (fourcc) {
case FOURCC_AVC1:
return kCodecH264;
case FOURCC_VP08:
return kCodecVP8;
case FOURCC_VP09:
return kCodecVP9;
case FOURCC_VP10:
return kCodecVP10;
default:
return kUnknownVideoCodec;
}
}
const char kWidevineKeySystemId[] = "edef8ba979d64acea3c827dcd51d21ed";
} // namespace
namespace edash_packager {
namespace media {
namespace mp4 {
MP4MediaParser::MP4MediaParser()
: state_(kWaitingForInit), moof_head_(0), mdat_tail_(0) {}
@ -367,6 +381,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
uint8_t nalu_length_size = 0;
const FourCC actual_format = entry.GetActualFormat();
const VideoCodec video_codec = FourCCToCodec(actual_format);
switch (actual_format) {
case FOURCC_AVC1: {
AVCDecoderConfiguration avc_config;
@ -404,6 +419,17 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
}
break;
}
case FOURCC_VP08:
case FOURCC_VP09:
case FOURCC_VP10: {
VPCodecConfiguration vp_config;
if (!vp_config.Parse(entry.codec_config_record.data)) {
LOG(ERROR) << "Failed to parse vpcc.";
return false;
}
codec_string = vp_config.GetCodecString(video_codec);
break;
}
default:
LOG(ERROR) << "Unsupported video format "
<< FourCCToString(actual_format) << " in stsd box.";
@ -413,7 +439,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
bool is_encrypted = entry.sinf.info.track_encryption.is_encrypted;
DVLOG(1) << "is_video_track_encrypted_: " << is_encrypted;
streams.push_back(new VideoStreamInfo(
track->header.track_id, timescale, duration, kCodecH264,
track->header.track_id, timescale, duration, video_codec,
codec_string, track->media.header.language, coded_width, coded_height,
pixel_width, pixel_height,
0, // trick_play_rate

View File

@ -22,7 +22,12 @@
#include "packager/media/formats/mp4/multi_segment_segmenter.h"
#include "packager/media/formats/mp4/single_segment_segmenter.h"
namespace edash_packager {
namespace media {
namespace mp4 {
namespace {
// Sets the range start and end value from offset and size.
// |start| and |end| are for byte-range-spec specified in RFC2616.
void SetStartAndEndFromOffsetAndSize(size_t offset,
@ -34,11 +39,23 @@ void SetStartAndEndFromOffsetAndSize(size_t offset,
// Note that ranges are inclusive. So we need - 1.
*end = *start + static_cast<uint32_t>(size) - 1;
}
} // namespace
namespace edash_packager {
namespace media {
namespace mp4 {
FourCC CodecToFourCC(VideoCodec codec) {
switch (codec) {
case kCodecH264:
return FOURCC_AVC1;
case kCodecVP8:
return FOURCC_VP08;
case kCodecVP9:
return FOURCC_VP09;
case kCodecVP10:
return FOURCC_VP10;
default:
return FOURCC_NULL;
}
}
} // namespace
MP4Muxer::MP4Muxer(const MuxerOptions& options) : Muxer(options) {}
MP4Muxer::~MP4Muxer() {}
@ -53,8 +70,12 @@ Status MP4Muxer::Initialize() {
ftyp->compatible_brands.push_back(FOURCC_ISO6);
ftyp->compatible_brands.push_back(FOURCC_MP41);
if (streams().size() == 1 &&
streams()[0]->info()->stream_type() == kStreamVideo)
ftyp->compatible_brands.push_back(FOURCC_AVC1);
streams()[0]->info()->stream_type() == kStreamVideo) {
const FourCC codec_fourcc = CodecToFourCC(
static_cast<VideoStreamInfo*>(streams()[0]->info().get())->codec());
if (codec_fourcc != FOURCC_NULL)
ftyp->compatible_brands.push_back(codec_fourcc);
}
moov->header.creation_time = IsoTimeNow();
moov->header.modification_time = IsoTimeNow();
@ -176,7 +197,7 @@ void MP4Muxer::GenerateVideoTrak(const VideoStreamInfo* video_info,
trak->media.handler.type = kVideo;
VideoSampleEntry video;
video.format = FOURCC_AVC1;
video.format = CodecToFourCC(video_info->codec());
video.width = video_info->width();
video.height = video_info->height();
video.codec_config_record.data = video_info->extra_data();

View File

@ -41,6 +41,7 @@
'dependencies': [
'../../../third_party/libwebm/libwebm.gyp:libwebm',
'../../base/media_base.gyp:base',
'../../filters/filters.gyp:filters'
],
},
{

View File

@ -5,6 +5,8 @@
#include "packager/media/formats/webm/webm_video_client.h"
#include "packager/base/logging.h"
#include "packager/base/stl_util.h"
#include "packager/media/filters/vp_codec_configuration.h"
#include "packager/media/formats/webm/webm_constants.h"
namespace {
@ -56,6 +58,8 @@ scoped_refptr<VideoStreamInfo> WebMVideoClient::GetVideoStreamInfo(
video_codec = kCodecVP8;
} else if (codec_id == "V_VP9") {
video_codec = kCodecVP9;
} else if (codec_id == "V_VP10") {
video_codec = kCodecVP10;
} else {
LOG(ERROR) << "Unsupported video codec_id " << codec_id;
return scoped_refptr<VideoStreamInfo>();
@ -102,20 +106,25 @@ scoped_refptr<VideoStreamInfo> WebMVideoClient::GetVideoStreamInfo(
sar_x /= gcd;
sar_y /= gcd;
const uint8_t* extra_data = NULL;
size_t extra_data_size = 0;
if (codec_private.size() > 0) {
extra_data = &codec_private[0];
extra_data_size = codec_private.size();
}
// TODO(kqyang): Generate codec string.
std::string codec_string;
// TODO(kqyang): Fill in the values for vp codec configuration.
const uint8_t profile = 0;
const uint8_t level = 0;
const uint8_t bit_depth = 8;
const uint8_t color_space = 0;
const uint8_t chroma_subsampling = 0;
const uint8_t transfer_function = 0;
const bool video_full_range_flag = false;
VPCodecConfiguration vp_config(profile, level, bit_depth, color_space,
chroma_subsampling, transfer_function,
video_full_range_flag, codec_private);
std::vector<uint8_t> extra_data;
vp_config.Write(&extra_data);
return scoped_refptr<VideoStreamInfo>(new VideoStreamInfo(
track_num, kWebMTimeScale, 0, video_codec, codec_string, std::string(),
width_after_crop, height_after_crop, sar_x, sar_y, 0, 0, extra_data,
extra_data_size, is_encrypted));
track_num, kWebMTimeScale, 0, video_codec,
vp_config.GetCodecString(video_codec), std::string(), width_after_crop,
height_after_crop, sar_x, sar_y, 0, 0, vector_as_array(&extra_data),
extra_data.size(), is_encrypted));
}
bool WebMVideoClient::OnUInt(int id, int64_t val) {

View File

@ -74,6 +74,7 @@
'media/formats/mp2t/mp2t.gyp:mp2t',
'media/formats/mp4/mp4.gyp:mp4',
'media/formats/mpeg/mpeg.gyp:mpeg',
'media/formats/webm/webm.gyp:webm',
'media/formats/wvm/wvm.gyp:wvm',
'media/test/media_test.gyp:media_test_support',
'testing/gtest.gyp:gtest',
@ -88,6 +89,7 @@
'media/file/file.gyp:*',
'media/formats/mp2t/mp2t.gyp:*',
'media/formats/mp4/mp4.gyp:*',
'media/formats/webm/webm.gyp:*',
'media/formats/wvm/wvm.gyp:*',
'mpd/mpd.gyp:*',
],