Implement vp8 bit stream parser.

Change-Id: I8b7997ceb8e7484399442f7c1072f4d5aa33ff4e
This commit is contained in:
KongQun Yang 2015-11-20 11:28:45 -08:00
parent 94401d750a
commit 814d2414e3
15 changed files with 624 additions and 162 deletions

View File

@ -53,6 +53,21 @@ bool BitReader::SkipBits(int num_bits) {
return ReadBitsInternal(num_bits, &not_needed); return ReadBitsInternal(num_bits, &not_needed);
} }
bool BitReader::SkipBytes(int num_bytes) {
if (num_remaining_bits_in_curr_byte_ != 8)
return false;
if (num_bytes == 0)
return true;
data_ += num_bytes - 1; // One additional byte in curr_byte_.
if (num_bytes > bytes_left_ + 1)
return false;
bytes_left_ -= num_bytes - 1;
num_remaining_bits_in_curr_byte_ = 0;
UpdateCurrByte();
return true;
}
bool BitReader::ReadBitsInternal(int num_bits, uint64_t* out) { bool BitReader::ReadBitsInternal(int num_bits, uint64_t* out) {
DCHECK_LE(num_bits, 64); DCHECK_LE(num_bits, 64);

View File

@ -44,10 +44,34 @@ class BitReader {
/// @param num_bits specifies the number of bits to be skipped. /// @param num_bits specifies the number of bits to be skipped.
/// @return false if the given number of bits cannot be skipped (not enough /// @return false if the given number of bits cannot be skipped (not enough
/// bits in the stream), true otherwise. When false is returned, the /// bits in the stream), true otherwise. When false is returned, the
/// stream will enter a state where further ReadBits/SkipBits /// stream will enter a state where further ReadXXX/SkipXXX
/// operations will always return false unless |num_bits| is 0. /// operations will always return false unless |num_bits/bytes| is 0.
bool SkipBits(int num_bits); bool SkipBits(int num_bits);
/// Read one bit then skip the number of bits specified if that bit matches @a
/// condition.
/// @param condition indicates when the number of bits should be skipped.
/// @param num_bits specifies the number of bits to be skipped.
/// @return false if the one bit cannot be read (not enough bits in the
/// stream) or if the bit is set but the given number of bits cannot
/// be skipped (not enough bits in the stream), true otherwise. When
/// false is returned, the stream will enter a state where further
/// ReadXXX/SkipXXX operations will always return false.
bool SkipBitsConditional(bool condition, int num_bits) {
bool condition_read = true;
if (!ReadBits(1, &condition_read))
return false;
return condition_read == condition ? SkipBits(num_bits) : true;
}
/// Skip a number of bytes from stream. The current posision should be byte
/// aligned, otherwise a false is returned and bytes are not skipped.
/// @param num_bytes specifies the number of bytes to be skipped.
/// @return false if the current position is not byte aligned or if the given
/// number of bytes cannot be skipped (not enough bytes in the
/// stream), true otherwise.
bool SkipBytes(int num_bytes);
/// @return The number of bits available for reading. /// @return The number of bits available for reading.
int bits_available() const { int bits_available() const {
return 8 * bytes_left_ + num_remaining_bits_in_curr_byte_; return 8 * bytes_left_ + num_remaining_bits_in_curr_byte_;

View File

@ -17,20 +17,22 @@ TEST(BitReaderTest, NormalOperationTest) {
BitReader reader1(buffer, 6); // Initialize with 6 bytes only BitReader reader1(buffer, 6); // Initialize with 6 bytes only
EXPECT_TRUE(reader1.ReadBits(1, &value8)); EXPECT_TRUE(reader1.ReadBits(1, &value8));
EXPECT_EQ(value8, 0); EXPECT_EQ(0, value8);
EXPECT_TRUE(reader1.ReadBits(8, &value8)); EXPECT_TRUE(reader1.ReadBits(8, &value8));
EXPECT_EQ(value8, 0xab); // 1010 1011 EXPECT_EQ(0xab, value8); // 1010 1011
EXPECT_EQ(39, reader1.bits_available());
EXPECT_EQ(9, reader1.bit_position());
EXPECT_TRUE(reader1.ReadBits(7, &value64)); EXPECT_TRUE(reader1.ReadBits(7, &value64));
EXPECT_TRUE(reader1.ReadBits(32, &value64)); EXPECT_TRUE(reader1.ReadBits(32, &value64));
EXPECT_EQ(value64, 0x55995599u); EXPECT_EQ(0x55995599u, value64);
EXPECT_FALSE(reader1.ReadBits(1, &value8)); EXPECT_FALSE(reader1.ReadBits(1, &value8));
value8 = 0xff; value8 = 0xff;
EXPECT_TRUE(reader1.ReadBits(0, &value8)); EXPECT_TRUE(reader1.ReadBits(0, &value8));
EXPECT_EQ(value8, 0); EXPECT_EQ(0, value8);
BitReader reader2(buffer, 8); BitReader reader2(buffer, 8);
EXPECT_TRUE(reader2.ReadBits(64, &value64)); EXPECT_TRUE(reader2.ReadBits(64, &value64));
EXPECT_EQ(value64, 0x5599559955995599ull); EXPECT_EQ(0x5599559955995599ull, value64);
EXPECT_FALSE(reader2.ReadBits(1, &value8)); EXPECT_FALSE(reader2.ReadBits(1, &value8));
EXPECT_TRUE(reader2.ReadBits(0, &value8)); EXPECT_TRUE(reader2.ReadBits(0, &value8));
} }
@ -53,17 +55,38 @@ TEST(BitReaderTest, SkipBitsTest) {
EXPECT_TRUE(reader1.SkipBits(2)); EXPECT_TRUE(reader1.SkipBits(2));
EXPECT_TRUE(reader1.ReadBits(3, &value8)); EXPECT_TRUE(reader1.ReadBits(3, &value8));
EXPECT_EQ(value8, 1); EXPECT_EQ(1, value8);
EXPECT_FALSE(reader1.SkipBytes(1)); // not aligned.
EXPECT_TRUE(reader1.SkipBits(11)); EXPECT_TRUE(reader1.SkipBits(11));
EXPECT_TRUE(reader1.ReadBits(8, &value8)); EXPECT_TRUE(reader1.ReadBits(8, &value8));
EXPECT_EQ(value8, 3); EXPECT_EQ(3, value8);
EXPECT_TRUE(reader1.SkipBits(76)); EXPECT_TRUE(reader1.SkipBytes(2));
EXPECT_TRUE(reader1.SkipBytes(0));
EXPECT_TRUE(reader1.SkipBytes(1));
EXPECT_TRUE(reader1.SkipBits(52));
EXPECT_EQ(20, reader1.bits_available());
EXPECT_EQ(100, reader1.bit_position());
EXPECT_TRUE(reader1.ReadBits(4, &value8)); EXPECT_TRUE(reader1.ReadBits(4, &value8));
EXPECT_EQ(value8, 13); EXPECT_EQ(13, value8);
EXPECT_FALSE(reader1.SkipBits(100)); EXPECT_FALSE(reader1.SkipBits(100));
EXPECT_TRUE(reader1.SkipBits(0)); EXPECT_TRUE(reader1.SkipBits(0));
EXPECT_FALSE(reader1.SkipBits(1)); EXPECT_FALSE(reader1.SkipBits(1));
} }
TEST(BitReaderTest, SkipBitsConditionalTest) {
uint8_t buffer[] = {0x8a, 0x12};
BitReader reader(buffer, sizeof(buffer));
EXPECT_TRUE(reader.SkipBitsConditional(false, 2));
EXPECT_EQ(1, reader.bit_position()); // Not skipped.
EXPECT_TRUE(reader.SkipBitsConditional(false, 3));
EXPECT_EQ(5, reader.bit_position()); // Skipped.
EXPECT_TRUE(reader.SkipBitsConditional(true, 2));
EXPECT_EQ(6, reader.bit_position()); // Not skipped.
EXPECT_TRUE(reader.SkipBitsConditional(true, 5));
EXPECT_EQ(12, reader.bit_position()); // Skipped.
EXPECT_TRUE(reader.SkipBits(4));
EXPECT_FALSE(reader.SkipBits(1));
}
} // namespace media } // namespace media
} // namespace edash_packager } // namespace edash_packager

View File

@ -25,8 +25,11 @@
'h264_parser.h', 'h264_parser.h',
'vp_codec_configuration.cc', 'vp_codec_configuration.cc',
'vp_codec_configuration.h', 'vp_codec_configuration.h',
'vp8_parser.cc',
'vp8_parser.h',
'vp9_parser.cc', 'vp9_parser.cc',
'vp9_parser.h', 'vp9_parser.h',
'vpx_parser.h',
], ],
'dependencies': [ 'dependencies': [
'../../base/base.gyp:base', '../../base/base.gyp:base',
@ -42,6 +45,7 @@
'h264_parser_unittest.cc', 'h264_parser_unittest.cc',
'hevc_decoder_configuration_unittest.cc', 'hevc_decoder_configuration_unittest.cc',
'vp_codec_configuration_unittest.cc', 'vp_codec_configuration_unittest.cc',
'vp8_parser_unittest.cc',
'vp9_parser_unittest.cc', 'vp9_parser_unittest.cc',
], ],
'dependencies': [ 'dependencies': [

View File

@ -0,0 +1,195 @@
// 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/vp8_parser.h"
#include "packager/base/logging.h"
#include "packager/media/base/bit_reader.h"
#include "packager/media/formats/mp4/rcheck.h"
namespace edash_packager {
namespace media {
namespace {
const uint32_t MB_FEATURE_TREE_PROBS = 3;
const uint32_t MAX_MB_SEGMENTS = 4;
const uint32_t MAX_REF_LF_DELTAS = 4;
const uint32_t MAX_MODE_LF_DELTAS = 4;
const uint32_t MB_LVL_MAX = 2;
const uint32_t MB_FEATURE_DATA_BITS[MB_LVL_MAX] = {7, 6};
bool VerifySyncCode(const uint8_t* data) {
return data[0] == 0x9d && data[1] == 0x01 && data[2] == 0x2a;
}
bool ReadSegmentation(BitReader* reader) {
bool enabled;
RCHECK(reader->ReadBits(1, &enabled));
if (!enabled)
return true;
bool update_map;
RCHECK(reader->ReadBits(1, &update_map));
bool update_data;
RCHECK(reader->ReadBits(1, &update_data));
if (update_data) {
RCHECK(reader->SkipBits(1)); // abs_delta
for (uint32_t i = 0; i < MAX_MB_SEGMENTS; ++i)
for (uint32_t j = 0; j < MB_LVL_MAX; ++j) {
RCHECK(reader->SkipBitsConditional(true, MB_FEATURE_DATA_BITS[j] + 1));
}
}
if (update_map) {
for (uint32_t i = 0; i < MB_FEATURE_TREE_PROBS; ++i)
RCHECK(reader->SkipBitsConditional(true, 8));
}
return true;
}
bool ReadLoopFilter(BitReader* reader) {
RCHECK(reader->SkipBits(10)); // filter_type, filter_evel, sharness_level
bool mode_ref_delta_enabled;
RCHECK(reader->ReadBits(1, &mode_ref_delta_enabled));
if (!mode_ref_delta_enabled)
return true;
bool mode_ref_delta_update;
RCHECK(reader->ReadBits(1, &mode_ref_delta_update));
if (!mode_ref_delta_update)
return true;
for (uint32_t i = 0; i < MAX_REF_LF_DELTAS + MAX_MODE_LF_DELTAS; ++i)
RCHECK(reader->SkipBitsConditional(true, 6 + 1));
return true;
}
bool ReadQuantization(BitReader* reader) {
uint32_t yac_index;
RCHECK(reader->ReadBits(7, &yac_index));
VLOG(4) << "yac_index: " << yac_index;
RCHECK(reader->SkipBitsConditional(true, 4 + 1)); // y dc delta
RCHECK(reader->SkipBitsConditional(true, 4 + 1)); // y2 dc delta
RCHECK(reader->SkipBitsConditional(true, 4 + 1)); // y2 ac delta
RCHECK(reader->SkipBitsConditional(true, 4 + 1)); // chroma dc delta
RCHECK(reader->SkipBitsConditional(true, 4 + 1)); // chroma ac delta
return true;
}
bool ReadRefreshFrame(BitReader* reader) {
bool refresh_golden_frame;
RCHECK(reader->ReadBits(1, &refresh_golden_frame));
bool refresh_altref_frame;
RCHECK(reader->ReadBits(1, &refresh_altref_frame));
if (!refresh_golden_frame)
RCHECK(reader->SkipBits(2)); // buffer copy flag
if (!refresh_altref_frame)
RCHECK(reader->SkipBits(2)); // buffer copy flag
RCHECK(reader->SkipBits(2)); // sign bias flags
return true;
}
} // namespace
VP8Parser::VP8Parser() : width_(0), height_(0) {}
VP8Parser::~VP8Parser() {}
bool VP8Parser::Parse(const uint8_t* data,
size_t data_size,
std::vector<VPxFrameInfo>* vpx_frames) {
DCHECK(data);
DCHECK(vpx_frames);
BitReader reader(data, data_size);
// The following 3 bytes are read directly from |data|.
RCHECK(reader.SkipBytes(3));
// One bit for frame type.
bool is_interframe = data[0] & 1;
// 3-bit version number with 2 bits for profile and the other bit reserved for
// future variants.
uint8_t profile = (data[0] >> 1) & 3;
// One bit for show frame flag.
// Then 19 bits (the remaining 3 bits in the first byte + next two bytes) for
// header size.
uint32_t header_size = (data[0] | (data[1] << 8) | (data[2] << 16)) >> 5;
RCHECK(header_size <= data_size);
if (!is_interframe) {
// The following 7 bytes are read directly from |data|.
RCHECK(reader.SkipBytes(7));
RCHECK(VerifySyncCode(&data[3]));
// Bits 0b11000000 for data[7] and data[9] are scaling.
width_ = data[6] | ((data[7] & 0x3f) << 8);
height_ = data[8] | ((data[9] & 0x3f) << 8);
RCHECK(reader.SkipBits(2)); // colorspace and pixel value clamping.
}
RCHECK(ReadSegmentation(&reader));
RCHECK(ReadLoopFilter(&reader));
RCHECK(reader.SkipBits(2)); // partitions bits
RCHECK(ReadQuantization(&reader));
if (is_interframe) {
RCHECK(ReadRefreshFrame(&reader));
RCHECK(reader.SkipBits(1)); // refresh_entropy_probs
RCHECK(reader.SkipBits(1)); // refresh last frame flag
} else {
RCHECK(reader.SkipBits(1)); // refresh_entropy_probs
}
// The next field is entropy header (coef probability tree), which is encoded
// using bool entropy encoder, i.e. compressed. We don't consider it as part
// of uncompressed header.
writable_codec_config()->set_profile(profile);
// VP8 uses an 8-bit YUV 4:2:0 format.
// http://tools.ietf.org/html/rfc6386 Section 2.
writable_codec_config()->set_bit_depth(8);
writable_codec_config()->set_chroma_subsampling(
VPCodecConfiguration::CHROMA_420_COLLOCATED_WITH_LUMA);
// VP8 uses YCrCb color space defined in ITU-R_BT.601.
// http://tools.ietf.org/html/rfc6386 Section 9.2.
writable_codec_config()->set_color_space(
VPCodecConfiguration::COLOR_SPACE_BT_601);
VPxFrameInfo vpx_frame;
vpx_frame.frame_size = data_size;
vpx_frame.uncompressed_header_size =
vpx_frame.frame_size - reader.bits_available() / 8;
vpx_frame.is_keyframe = !is_interframe;
vpx_frame.width = width_;
vpx_frame.height = height_;
vpx_frames->clear();
vpx_frames->push_back(vpx_frame);
VLOG(3) << "\n frame_size: " << vpx_frame.frame_size
<< "\n uncompressed_header_size: "
<< vpx_frame.uncompressed_header_size
<< "\n bits read: " << reader.bit_position()
<< "\n header_size: " << header_size
<< "\n width: " << vpx_frame.width
<< "\n height: " << vpx_frame.height;
return true;
}
bool VP8Parser::IsKeyframe(const uint8_t* data, size_t data_size) {
// Make sure the block is big enough for the minimal keyframe header size.
if (data_size < 10)
return false;
// The LSb of the first byte must be a 0 for a keyframe.
if ((data[0] & 0x01) != 0)
return false;
return VerifySyncCode(&data[3]);
}
} // namespace media
} // namespace edash_packager

View File

@ -0,0 +1,55 @@
// 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_VP8_PARSER_H_
#define MEDIA_FILTERS_VP8_PARSER_H_
#include <stdint.h>
#include <stdlib.h>
#include "packager/base/macros.h"
#include "packager/media/filters/vpx_parser.h"
namespace edash_packager {
namespace media {
/// Class to parse a vp8 bit stream. Implemented according to
/// https://tools.ietf.org/html/rfc6386.
class VP8Parser : public VPxParser {
public:
VP8Parser();
~VP8Parser() override;
/// Parse @a data with size @a data_size.
/// @param data_size Size of the sample in bytes. Note that it should be a
/// full sample.
/// @param[out] vpx_frames points to the list of VPx frames for the current
/// sample on success. Cannot be NULL.
/// @return true on success, false otherwise.
bool Parse(const uint8_t* data,
size_t data_size,
std::vector<VPxFrameInfo>* vpx_frames) override;
/// A convenient utility function to check whether the frame is a keyframe.
/// Note that this function does not do a full parse of the frame header, so
/// should be more efficient than Parse().
/// @param data_size Size of the sample in bytes.
/// @return true if it is, false if it is not or if there is parsing error.
static bool IsKeyframe(const uint8_t* data, size_t data_size);
private:
// Keep track of the current width and height. Note that they may change from
// frame to frame.
uint32_t width_;
uint32_t height_;
DISALLOW_COPY_AND_ASSIGN(VP8Parser);
};
} // namespace media
} // namespace edash_packager
#endif // MEDIA_FILTERS_VP8_PARSER_H_

View File

@ -0,0 +1,105 @@
// 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/vp8_parser.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using ::testing::ElementsAre;
namespace edash_packager {
namespace media {
namespace {
MATCHER_P5(EqualVPxFrame,
frame_size,
uncompressed_header_size,
is_keyframe,
width,
height,
"") {
*result_listener << "which is (" << arg.frame_size << ", "
<< arg.uncompressed_header_size << ", " << arg.is_keyframe
<< ", " << arg.width << ", " << arg.height << ").";
return arg.frame_size == frame_size &&
arg.uncompressed_header_size == uncompressed_header_size &&
arg.is_keyframe == is_keyframe && arg.width == width &&
arg.height == height;
}
} // namespace
TEST(VP8ParserTest, Keyframe) {
const uint8_t kData[] = {
0x54, 0x04, 0x00, 0x9d, 0x01, 0x2a, 0x40, 0x01, 0xf0, 0x00, 0x00, 0x47,
0x08, 0x85, 0x85, 0x88, 0x85, 0x84, 0x88, 0x01, 0x24, 0x10, 0x17, 0x67,
0x63, 0x3f, 0xbb, 0xe5, 0xcf, 0x9b, 0x7d, 0x53, 0xec, 0x67, 0xa2, 0xcf,
};
EXPECT_TRUE(VP8Parser::IsKeyframe(kData, arraysize(kData)));
VP8Parser parser;
std::vector<VPxFrameInfo> frames;
ASSERT_TRUE(parser.Parse(kData, arraysize(kData), &frames));
EXPECT_EQ("vp08.02.00.08.01.01.00.00",
parser.codec_config().GetCodecString(kCodecVP8));
EXPECT_THAT(frames, ElementsAre(EqualVPxFrame(arraysize(kData), 22u, true,
320u, 240u)));
}
TEST(VP8ParserTest, NonKeyframe) {
const uint8_t kData[] = {
0x31, 0x03, 0x00, 0x11, 0x10, 0xa4, 0x00, 0x1a, 0xea, 0xd8, 0xaf, 0x40,
0xcf, 0x80, 0x2f, 0xdc, 0x9d, 0x42, 0x4b, 0x19, 0xc8, 0x04, 0x97, 0x28,
0x34, 0x7b, 0x47, 0xfc, 0x2d, 0xaa, 0x0b, 0xbb, 0xc6, 0xc3, 0xc1, 0x12,
};
EXPECT_FALSE(VP8Parser::IsKeyframe(kData, arraysize(kData)));
VP8Parser parser;
std::vector<VPxFrameInfo> frames;
ASSERT_TRUE(parser.Parse(kData, arraysize(kData), &frames));
EXPECT_THAT(frames,
ElementsAre(EqualVPxFrame(arraysize(kData), 8u, false, 0u, 0u)));
}
TEST(VP8ParserTest, InsufficientData) {
const uint8_t kData[] = {0x00, 0x0a};
EXPECT_FALSE(VP8Parser::IsKeyframe(kData, arraysize(kData)));
VP8Parser parser;
std::vector<VPxFrameInfo> frames;
ASSERT_FALSE(parser.Parse(kData, arraysize(kData), &frames));
}
TEST(VP8ParserTest, CorruptedSynccode) {
const uint8_t kData[] = {
0x54, 0x04, 0x00, 0x9d, 0x21, 0x2a, 0x40, 0x01, 0xf0, 0x00, 0x00, 0x47,
0x08, 0x85, 0x85, 0x88, 0x85, 0x84, 0x88, 0x01, 0x24, 0x10, 0x17, 0x67,
0x63, 0x3f, 0xbb, 0xe5, 0xcf, 0x9b, 0x7d, 0x53, 0xec, 0x67, 0xa2, 0xcf,
};
EXPECT_FALSE(VP8Parser::IsKeyframe(kData, arraysize(kData)));
VP8Parser parser;
std::vector<VPxFrameInfo> frames;
ASSERT_FALSE(parser.Parse(kData, arraysize(kData), &frames));
}
TEST(VP8ParserTest, NotEnoughBytesForHeaderSize) {
const uint8_t kData[] = {
0x54, 0x06, 0x00, 0x9d, 0x01, 0x2a, 0x40, 0x01, 0xf0, 0x00, 0x00, 0x47,
0x08, 0x85, 0x85, 0x88, 0x85, 0x84, 0x88, 0x01, 0x24, 0x10, 0x17, 0x67,
0x63, 0x3f, 0xbb, 0xe5, 0xcf, 0x9b, 0x7d, 0x53, 0xec, 0x67, 0xa2, 0xcf,
};
// IsKeyframe only parses the bytes that is necessary to determine whether it
// is a keyframe.
EXPECT_TRUE(VP8Parser::IsKeyframe(kData, arraysize(kData)));
VP8Parser parser;
std::vector<VPxFrameInfo> frames;
EXPECT_FALSE(parser.Parse(kData, arraysize(kData), &frames));
}
} // namespace media
} // namespace edash_packager

View File

@ -46,22 +46,6 @@ enum VpxColorSpace {
VPX_COLOR_SPACE_SRGB = 7, VPX_COLOR_SPACE_SRGB = 7,
}; };
class VP9BitReader : public BitReader {
public:
VP9BitReader(const uint8_t* data, off_t size) : BitReader(data, size) {}
~VP9BitReader() {}
bool SkipBitsConditional(uint32_t num_bits) {
bool condition;
if (!ReadBits(1, &condition))
return false;
return condition ? SkipBits(num_bits) : true;
}
private:
DISALLOW_COPY_AND_ASSIGN(VP9BitReader);
};
uint32_t RoundupShift(uint32_t value, uint32_t n) { uint32_t RoundupShift(uint32_t value, uint32_t n) {
return (value + (1 << n) - 1) >> n; return (value + (1 << n) - 1) >> n;
} }
@ -156,7 +140,7 @@ bool ParseIfSuperframeIndex(const uint8_t* data,
return true; return true;
} }
bool ReadProfile(VP9BitReader* reader, VPCodecConfiguration* codec_config) { bool ReadProfile(BitReader* reader, VPCodecConfiguration* codec_config) {
uint8_t bit[2]; uint8_t bit[2];
RCHECK(reader->ReadBits(1, &bit[0])); RCHECK(reader->ReadBits(1, &bit[0]));
RCHECK(reader->ReadBits(1, &bit[1])); RCHECK(reader->ReadBits(1, &bit[1]));
@ -170,7 +154,7 @@ bool ReadProfile(VP9BitReader* reader, VPCodecConfiguration* codec_config) {
return true; return true;
} }
bool ReadSyncCode(VP9BitReader* reader) { bool ReadSyncCode(BitReader* reader) {
uint32_t sync_code; uint32_t sync_code;
RCHECK(reader->ReadBits(24, &sync_code)); RCHECK(reader->ReadBits(24, &sync_code));
return sync_code == VP9_SYNC_CODE; return sync_code == VP9_SYNC_CODE;
@ -222,7 +206,7 @@ VPCodecConfiguration::ChromaSubsampling GetChromaSubsampling(
} }
} }
bool ReadBitDepthAndColorSpace(VP9BitReader* reader, bool ReadBitDepthAndColorSpace(BitReader* reader,
VPCodecConfiguration* codec_config) { VPCodecConfiguration* codec_config) {
uint8_t bit_depth = 8; uint8_t bit_depth = 8;
if (codec_config->profile() >= 2) { if (codec_config->profile() >= 2) {
@ -284,7 +268,7 @@ bool ReadBitDepthAndColorSpace(VP9BitReader* reader,
return true; return true;
} }
bool ReadFrameSize(VP9BitReader* reader, uint32_t* width, uint32_t* height) { bool ReadFrameSize(BitReader* reader, uint32_t* width, uint32_t* height) {
RCHECK(reader->ReadBits(16, width)); RCHECK(reader->ReadBits(16, width));
*width += 1; // Off by 1. *width += 1; // Off by 1.
RCHECK(reader->ReadBits(16, height)); RCHECK(reader->ReadBits(16, height));
@ -292,7 +276,7 @@ bool ReadFrameSize(VP9BitReader* reader, uint32_t* width, uint32_t* height) {
return true; return true;
} }
bool ReadDisplayFrameSize(VP9BitReader* reader, bool ReadDisplayFrameSize(BitReader* reader,
uint32_t* display_width, uint32_t* display_width,
uint32_t* display_height) { uint32_t* display_height) {
bool has_display_size; bool has_display_size;
@ -302,7 +286,7 @@ bool ReadDisplayFrameSize(VP9BitReader* reader,
return true; return true;
} }
bool ReadFrameSizes(VP9BitReader* reader, uint32_t* width, uint32_t* height) { bool ReadFrameSizes(BitReader* reader, uint32_t* width, uint32_t* height) {
uint32_t new_width; uint32_t new_width;
uint32_t new_height; uint32_t new_height;
RCHECK(ReadFrameSize(reader, &new_width, &new_height)); RCHECK(ReadFrameSize(reader, &new_width, &new_height));
@ -321,7 +305,7 @@ bool ReadFrameSizes(VP9BitReader* reader, uint32_t* width, uint32_t* height) {
return true; return true;
} }
bool ReadFrameSizesWithRefs(VP9BitReader* reader, bool ReadFrameSizesWithRefs(BitReader* reader,
uint32_t* width, uint32_t* width,
uint32_t* height) { uint32_t* height) {
bool found = false; bool found = false;
@ -340,7 +324,7 @@ bool ReadFrameSizesWithRefs(VP9BitReader* reader,
return true; return true;
} }
bool ReadLoopFilter(VP9BitReader* reader) { bool ReadLoopFilter(BitReader* reader) {
RCHECK(reader->SkipBits(9)); // filter_evel, sharness_level RCHECK(reader->SkipBits(9)); // filter_evel, sharness_level
bool mode_ref_delta_enabled; bool mode_ref_delta_enabled;
RCHECK(reader->ReadBits(1, &mode_ref_delta_enabled)); RCHECK(reader->ReadBits(1, &mode_ref_delta_enabled));
@ -348,22 +332,23 @@ bool ReadLoopFilter(VP9BitReader* reader) {
return true; return true;
bool mode_ref_delta_update; bool mode_ref_delta_update;
RCHECK(reader->ReadBits(1, &mode_ref_delta_update)); RCHECK(reader->ReadBits(1, &mode_ref_delta_update));
if (!mode_ref_delta_update) return true; if (!mode_ref_delta_update)
return true;
for (uint32_t i = 0; i < MAX_REF_LF_DELTAS + MAX_MODE_LF_DELTAS; ++i) for (uint32_t i = 0; i < MAX_REF_LF_DELTAS + MAX_MODE_LF_DELTAS; ++i)
RCHECK(reader->SkipBitsConditional(6 + 1)); RCHECK(reader->SkipBitsConditional(true, 6 + 1));
return true; return true;
} }
bool ReadQuantization(VP9BitReader* reader) { bool ReadQuantization(BitReader* reader) {
RCHECK(reader->SkipBits(QINDEX_BITS)); RCHECK(reader->SkipBits(QINDEX_BITS));
// Skip delta_q bits. // Skip delta_q bits.
for (uint32_t i = 0; i < 3; ++i) for (uint32_t i = 0; i < 3; ++i)
RCHECK(reader->SkipBitsConditional(4 + 1)); RCHECK(reader->SkipBitsConditional(true, 4 + 1));
return true; return true;
} }
bool ReadSegmentation(VP9BitReader* reader) { bool ReadSegmentation(BitReader* reader) {
bool enabled; bool enabled;
RCHECK(reader->ReadBits(1, &enabled)); RCHECK(reader->ReadBits(1, &enabled));
if (!enabled) if (!enabled)
@ -373,13 +358,13 @@ bool ReadSegmentation(VP9BitReader* reader) {
RCHECK(reader->ReadBits(1, &update_map)); RCHECK(reader->ReadBits(1, &update_map));
if (update_map) { if (update_map) {
for (uint32_t i = 0; i < SEG_TREE_PROBS; ++i) for (uint32_t i = 0; i < SEG_TREE_PROBS; ++i)
RCHECK(reader->SkipBitsConditional(8)); RCHECK(reader->SkipBitsConditional(true, 8));
bool temporal_update; bool temporal_update;
RCHECK(reader->ReadBits(1, &temporal_update)); RCHECK(reader->ReadBits(1, &temporal_update));
if (temporal_update) { if (temporal_update) {
for (uint32_t j = 0; j < PREDICTION_PROBS; ++j) for (uint32_t j = 0; j < PREDICTION_PROBS; ++j)
RCHECK(reader->SkipBitsConditional(8)); RCHECK(reader->SkipBitsConditional(true, 8));
} }
} }
@ -402,7 +387,7 @@ bool ReadSegmentation(VP9BitReader* reader) {
return true; return true;
} }
bool ReadTileInfo(uint32_t width, VP9BitReader* reader) { bool ReadTileInfo(uint32_t width, BitReader* reader) {
uint32_t mi_cols = GetNumMiUnits(width); uint32_t mi_cols = GetNumMiUnits(width);
uint32_t min_log2_tile_cols; uint32_t min_log2_tile_cols;
@ -420,7 +405,7 @@ bool ReadTileInfo(uint32_t width, VP9BitReader* reader) {
} }
RCHECK(log2_tile_cols <= 6); RCHECK(log2_tile_cols <= 6);
RCHECK(reader->SkipBitsConditional(1)); // log2_tile_rows RCHECK(reader->SkipBitsConditional(true, 1)); // log2_tile_rows
return true; return true;
} }
@ -438,12 +423,12 @@ bool VP9Parser::Parse(const uint8_t* data,
for (auto& vpx_frame : *vpx_frames) { for (auto& vpx_frame : *vpx_frames) {
VLOG(4) << "process frame with size " << vpx_frame.frame_size; VLOG(4) << "process frame with size " << vpx_frame.frame_size;
VP9BitReader reader(data, vpx_frame.frame_size); BitReader reader(data, vpx_frame.frame_size);
uint8_t frame_marker; uint8_t frame_marker;
RCHECK(reader.ReadBits(2, &frame_marker)); RCHECK(reader.ReadBits(2, &frame_marker));
RCHECK(frame_marker == VP9_FRAME_MARKER); RCHECK(frame_marker == VP9_FRAME_MARKER);
RCHECK(ReadProfile(&reader, &codec_config_)); RCHECK(ReadProfile(&reader, writable_codec_config()));
bool show_existing_frame; bool show_existing_frame;
RCHECK(reader.ReadBits(1, &show_existing_frame)); RCHECK(reader.ReadBits(1, &show_existing_frame));
@ -470,7 +455,7 @@ bool VP9Parser::Parse(const uint8_t* data,
if (vpx_frame.is_keyframe) { if (vpx_frame.is_keyframe) {
RCHECK(ReadSyncCode(&reader)); RCHECK(ReadSyncCode(&reader));
RCHECK(ReadBitDepthAndColorSpace(&reader, &codec_config_)); RCHECK(ReadBitDepthAndColorSpace(&reader, writable_codec_config()));
RCHECK(ReadFrameSizes(&reader, &width_, &height_)); RCHECK(ReadFrameSizes(&reader, &width_, &height_));
} else { } else {
bool intra_only = false; bool intra_only = false;
@ -481,16 +466,16 @@ bool VP9Parser::Parse(const uint8_t* data,
if (intra_only) { if (intra_only) {
RCHECK(ReadSyncCode(&reader)); RCHECK(ReadSyncCode(&reader));
if (codec_config_.profile() > 0) { if (codec_config().profile() > 0) {
RCHECK(ReadBitDepthAndColorSpace(&reader, &codec_config_)); RCHECK(ReadBitDepthAndColorSpace(&reader, writable_codec_config()));
} else { } else {
// NOTE: The intra-only frame header does not include the // NOTE: The intra-only frame header does not include the
// specification of either the color format or color sub-sampling in // specification of either the color format or color sub-sampling in
// profile 0. VP9 specifies that the default color format should be // profile 0. VP9 specifies that the default color format should be
// YUV 4:2:0 in this case (normative). // YUV 4:2:0 in this case (normative).
codec_config_.set_chroma_subsampling( writable_codec_config()->set_chroma_subsampling(
VPCodecConfiguration::CHROMA_420_COLLOCATED_WITH_LUMA); VPCodecConfiguration::CHROMA_420_COLLOCATED_WITH_LUMA);
codec_config_.set_bit_depth(8); writable_codec_config()->set_bit_depth(8);
} }
RCHECK(reader.SkipBits(REF_FRAMES)); // refresh_frame_flags RCHECK(reader.SkipBits(REF_FRAMES)); // refresh_frame_flags
@ -519,26 +504,27 @@ bool VP9Parser::Parse(const uint8_t* data,
} }
RCHECK(reader.SkipBits(FRAME_CONTEXTS_LOG2)); // frame_context_idx RCHECK(reader.SkipBits(FRAME_CONTEXTS_LOG2)); // frame_context_idx
VLOG(4) << "Bits read before ReadLoopFilter: " << reader.bit_position(); VLOG(4) << "bits read before ReadLoopFilter: " << reader.bit_position();
RCHECK(ReadLoopFilter(&reader)); RCHECK(ReadLoopFilter(&reader));
RCHECK(ReadQuantization(&reader)); RCHECK(ReadQuantization(&reader));
RCHECK(ReadSegmentation(&reader)); RCHECK(ReadSegmentation(&reader));
RCHECK(ReadTileInfo(width_, &reader)); RCHECK(ReadTileInfo(width_, &reader));
uint16_t first_partition_size; uint16_t header_size;
RCHECK(reader.ReadBits(16, &first_partition_size)); RCHECK(reader.ReadBits(16, &header_size));
vpx_frame.uncompressed_header_size = vpx_frame.uncompressed_header_size =
vpx_frame.frame_size - reader.bits_available() / 8; vpx_frame.frame_size - reader.bits_available() / 8;
vpx_frame.width = width_; vpx_frame.width = width_;
vpx_frame.height = height_; vpx_frame.height = height_;
VLOG(3) << "\n frame_size: " << vpx_frame.frame_size VLOG(3) << "\n frame_size: " << vpx_frame.frame_size
<< "\n header_size: " << vpx_frame.uncompressed_header_size << "\n uncompressed_header_size: "
<< "\n Bits read: " << reader.bit_position() << vpx_frame.uncompressed_header_size
<< "\n first_partition_size: " << first_partition_size; << "\n bits read: " << reader.bit_position()
<< "\n header_size: " << header_size;
RCHECK(first_partition_size > 0); RCHECK(header_size > 0);
RCHECK(first_partition_size * 8 <= reader.bits_available()); RCHECK(header_size * 8 <= reader.bits_available());
data += vpx_frame.frame_size; data += vpx_frame.frame_size;
} }
@ -546,7 +532,7 @@ bool VP9Parser::Parse(const uint8_t* data,
} }
bool VP9Parser::IsKeyframe(const uint8_t* data, size_t data_size) { bool VP9Parser::IsKeyframe(const uint8_t* data, size_t data_size) {
VP9BitReader reader(data, data_size); BitReader reader(data, data_size);
uint8_t frame_marker; uint8_t frame_marker;
RCHECK(reader.ReadBits(2, &frame_marker)); RCHECK(reader.ReadBits(2, &frame_marker));
RCHECK(frame_marker == VP9_FRAME_MARKER); RCHECK(frame_marker == VP9_FRAME_MARKER);

View File

@ -11,24 +11,16 @@
#include <stdlib.h> #include <stdlib.h>
#include "packager/base/macros.h" #include "packager/base/macros.h"
#include "packager/media/filters/vp_codec_configuration.h" #include "packager/media/filters/vpx_parser.h"
namespace edash_packager { namespace edash_packager {
namespace media { namespace media {
struct VPxFrameInfo {
size_t frame_size;
size_t uncompressed_header_size;
bool is_keyframe;
uint32_t width;
uint32_t height;
};
/// Class to parse a vp9 bit stream. /// Class to parse a vp9 bit stream.
class VP9Parser { class VP9Parser : public VPxParser {
public: public:
VP9Parser(); VP9Parser();
~VP9Parser(); ~VP9Parser() override;
/// Parse @a data with size @a data_size. /// Parse @a data with size @a data_size.
/// @param data_size Size of the sample in bytes. Note that it should be a /// @param data_size Size of the sample in bytes. Note that it should be a
@ -38,11 +30,7 @@ class VP9Parser {
/// @return true on success, false otherwise. /// @return true on success, false otherwise.
bool Parse(const uint8_t* data, bool Parse(const uint8_t* data,
size_t data_size, size_t data_size,
std::vector<VPxFrameInfo>* vpx_frames); std::vector<VPxFrameInfo>* vpx_frames) override;
/// @return VPx codec configuration extracted. Note that it is only valid
/// after parsing a keyframe or intra frame successfully.
const VPCodecConfiguration& codec_config() { return codec_config_; }
/// A convenient utility function to check whether the frame is a keyframe. /// A convenient utility function to check whether the frame is a keyframe.
/// Note that this function does not do a full parse of the frame header, so /// Note that this function does not do a full parse of the frame header, so
@ -57,8 +45,6 @@ class VP9Parser {
uint32_t width_; uint32_t width_;
uint32_t height_; uint32_t height_;
VPCodecConfiguration codec_config_;
DISALLOW_COPY_AND_ASSIGN(VP9Parser); DISALLOW_COPY_AND_ASSIGN(VP9Parser);
}; };

View File

@ -226,7 +226,7 @@ TEST(VP9ParserTest, CorruptedSynccode) {
ASSERT_FALSE(parser.Parse(kData, arraysize(kData), &frames)); ASSERT_FALSE(parser.Parse(kData, arraysize(kData), &frames));
} }
TEST(VP9ParserTest, NotEnoughBytesForFirstPartitionSize) { TEST(VP9ParserTest, NotEnoughBytesForHeaderSize) {
const uint8_t kData[] = { const uint8_t kData[] = {
0x82, 0x49, 0x83, 0x42, 0x04, 0xaf, 0xf0, 0x06, 0xbb, 0xdd, 0xf8, 0x03, 0x82, 0x49, 0x83, 0x42, 0x04, 0xaf, 0xf0, 0x06, 0xbb, 0xdd, 0xf8, 0x03,
0xfc, 0x00, 0x38, 0x24, 0x1c, 0x18, 0x00, 0x00, 0x03, 0x38, 0x7f, 0x8f, 0xfc, 0x00, 0x38, 0x24, 0x1c, 0x18, 0x00, 0x00, 0x03, 0x38, 0x7f, 0x8f,

View File

@ -0,0 +1,58 @@
// 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_VPX_PARSER_H_
#define MEDIA_FILTERS_VPX_PARSER_H_
#include <stdint.h>
#include <stdlib.h>
#include "packager/base/macros.h"
#include "packager/media/filters/vp_codec_configuration.h"
namespace edash_packager {
namespace media {
struct VPxFrameInfo {
size_t frame_size;
size_t uncompressed_header_size;
bool is_keyframe;
uint32_t width;
uint32_t height;
};
class VPxParser {
public:
VPxParser() {}
virtual ~VPxParser() {}
/// Parse @a data with size @a data_size.
/// @param data_size Size of the sample in bytes. Note that it should be a
/// full sample.
/// @param[out] vpx_frames points to the list of VPx frames for the current
/// sample on success. Cannot be NULL.
/// @return true on success, false otherwise.
virtual bool Parse(const uint8_t* data,
size_t data_size,
std::vector<VPxFrameInfo>* vpx_frames) = 0;
/// @return VPx codec configuration extracted. Note that it is only valid
/// after parsing a keyframe or intra frame successfully.
const VPCodecConfiguration& codec_config() const { return codec_config_; }
protected:
VPCodecConfiguration* writable_codec_config() { return &codec_config_; }
private:
VPCodecConfiguration codec_config_;
DISALLOW_COPY_AND_ASSIGN(VPxParser);
};
} // namespace media
} // namespace edash_packager
#endif // MEDIA_FILTERS_VPX_PARSER_H_

View File

@ -10,6 +10,7 @@
#include "packager/media/base/buffer_reader.h" #include "packager/media/base/buffer_reader.h"
#include "packager/media/base/key_source.h" #include "packager/media/base/key_source.h"
#include "packager/media/base/media_sample.h" #include "packager/media/base/media_sample.h"
#include "packager/media/filters/vp8_parser.h"
#include "packager/media/filters/vp9_parser.h" #include "packager/media/filters/vp9_parser.h"
#include "packager/media/formats/mp4/box_definitions.h" #include "packager/media/formats/mp4/box_definitions.h"
#include "packager/media/formats/mp4/cenc.h" #include "packager/media/formats/mp4/cenc.h"
@ -35,8 +36,11 @@ EncryptingFragmenter::EncryptingFragmenter(
nalu_length_size_(nalu_length_size), nalu_length_size_(nalu_length_size),
clear_time_(clear_time) { clear_time_(clear_time) {
DCHECK(encryption_key_); DCHECK(encryption_key_);
if (video_codec == kCodecVP9) if (video_codec == kCodecVP8) {
vp9_parser_.reset(new VP9Parser); vpx_parser_.reset(new VP8Parser);
} else if (video_codec == kCodecVP9) {
vpx_parser_.reset(new VP9Parser);
}
} }
EncryptingFragmenter::~EncryptingFragmenter() {} EncryptingFragmenter::~EncryptingFragmenter() {}
@ -140,11 +144,11 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
FrameCENCInfo cenc_info(encryptor_->iv()); FrameCENCInfo cenc_info(encryptor_->iv());
uint8_t* data = sample->writable_data(); uint8_t* data = sample->writable_data();
if (IsSubsampleEncryptionRequired()) { if (IsSubsampleEncryptionRequired()) {
if (video_codec_ == kCodecVP9) { if (vpx_parser_) {
std::vector<VPxFrameInfo> vpx_frames; std::vector<VPxFrameInfo> vpx_frames;
if (!vp9_parser_->Parse(sample->data(), sample->data_size(), if (!vpx_parser_->Parse(sample->data(), sample->data_size(),
&vpx_frames)) { &vpx_frames)) {
return Status(error::MUXER_FAILURE, "Failed to parse vp9 frame."); return Status(error::MUXER_FAILURE, "Failed to parse vpx frame.");
} }
for (const VPxFrameInfo& frame : vpx_frames) { for (const VPxFrameInfo& frame : vpx_frames) {
SubsampleEntry subsample; SubsampleEntry subsample;

View File

@ -8,7 +8,7 @@
#define MEDIA_FORMATS_MP4_ENCRYPTING_FRAGMENTER_H_ #define MEDIA_FORMATS_MP4_ENCRYPTING_FRAGMENTER_H_
#include "packager/base/memory/scoped_ptr.h" #include "packager/base/memory/scoped_ptr.h"
#include "packager/media/filters/vp9_parser.h" #include "packager/media/filters/vpx_parser.h"
#include "packager/media/formats/mp4/fragmenter.h" #include "packager/media/formats/mp4/fragmenter.h"
namespace edash_packager { namespace edash_packager {
@ -71,7 +71,7 @@ class EncryptingFragmenter : public Fragmenter {
// Should we enable subsample encryption? // Should we enable subsample encryption?
bool IsSubsampleEncryptionRequired() { bool IsSubsampleEncryptionRequired() {
return video_codec_ == kCodecVP9 || nalu_length_size_ != 0; return vpx_parser_ || nalu_length_size_ != 0;
} }
scoped_ptr<EncryptionKey> encryption_key_; scoped_ptr<EncryptionKey> encryption_key_;
@ -85,7 +85,7 @@ class EncryptingFragmenter : public Fragmenter {
const uint8_t nalu_length_size_; const uint8_t nalu_length_size_;
int64_t clear_time_; int64_t clear_time_;
scoped_ptr<VP9Parser> vp9_parser_; scoped_ptr<VPxParser> vpx_parser_;
DISALLOW_COPY_AND_ASSIGN(EncryptingFragmenter); DISALLOW_COPY_AND_ASSIGN(EncryptingFragmenter);
}; };

View File

@ -10,6 +10,7 @@
#include "packager/base/sys_byteorder.h" #include "packager/base/sys_byteorder.h"
#include "packager/media/base/decrypt_config.h" #include "packager/media/base/decrypt_config.h"
#include "packager/media/base/timestamp.h" #include "packager/media/base/timestamp.h"
#include "packager/media/filters/vp8_parser.h"
#include "packager/media/filters/vp9_parser.h" #include "packager/media/filters/vp9_parser.h"
#include "packager/media/filters/webvtt_util.h" #include "packager/media/filters/webvtt_util.h"
#include "packager/media/formats/webm/webm_constants.h" #include "packager/media/formats/webm/webm_constants.h"
@ -59,26 +60,15 @@ bool IsKeyframe(bool is_video,
if (!is_video) if (!is_video)
return true; return true;
if (codec == kCodecVP9) switch (codec) {
case kCodecVP8:
return VP8Parser::IsKeyframe(data, size);
case kCodecVP9:
return VP9Parser::IsKeyframe(data, size); return VP9Parser::IsKeyframe(data, size);
default:
CHECK_EQ(kCodecVP8, codec); NOTIMPLEMENTED() << "Unsupported codec " << codec;
// Make sure the block is big enough for the minimal keyframe header size.
if (size < 7)
return false; return false;
}
// The LSb of the first byte must be a 0 for a keyframe.
// http://tools.ietf.org/html/rfc6386 Section 19.1
if ((data[0] & 0x01) != 0)
return false;
// Verify VP8 keyframe startcode.
// http://tools.ietf.org/html/rfc6386 Section 19.1
if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a)
return false;
return true;
} }
} // namespace } // namespace
@ -583,33 +573,41 @@ bool WebMClusterParser::OnBlock(bool is_simple_block,
buffer->set_duration(track->default_duration()); buffer->set_duration(track->default_duration());
} }
if (!initialized_) { if (!init_cb_.is_null() && !initialized_) {
std::vector<scoped_refptr<StreamInfo>> streams; std::vector<scoped_refptr<StreamInfo>> streams;
if (audio_stream_info_) if (audio_stream_info_)
streams.push_back(audio_stream_info_); streams.push_back(audio_stream_info_);
if (video_stream_info_) { if (video_stream_info_) {
if (stream_type == kStreamVideo) { if (stream_type == kStreamVideo) {
VPCodecConfiguration codec_config; scoped_ptr<VPxParser> vpx_parser;
if (video_stream_info_->codec() == kCodecVP9) { switch (video_stream_info_->codec()) {
VP9Parser vp9_parser; case kCodecVP8:
vpx_parser.reset(new VP8Parser);
break;
case kCodecVP9:
vpx_parser.reset(new VP9Parser);
break;
default:
NOTIMPLEMENTED() << "Unsupported codec "
<< video_stream_info_->codec();
return false;
}
std::vector<VPxFrameInfo> vpx_frames; std::vector<VPxFrameInfo> vpx_frames;
if (!vp9_parser.Parse(buffer->data(), buffer->data_size(), if (!vpx_parser->Parse(buffer->data(), buffer->data_size(),
&vpx_frames)) { &vpx_frames)) {
LOG(ERROR) << "Failed to parse vp9 frame."; LOG(ERROR) << "Failed to parse vpx frame.";
return false; return false;
} }
if (vpx_frames.size() != 1u || !vpx_frames[0].is_keyframe) { if (vpx_frames.size() != 1u || !vpx_frames[0].is_keyframe) {
LOG(ERROR) << "The first frame should be a key frame."; LOG(ERROR) << "The first frame should be a key frame.";
return false; return false;
} }
codec_config = vp9_parser.codec_config();
}
// TODO(kqyang): Support VP8.
const VPCodecConfiguration* codec_config = &vpx_parser->codec_config();
video_stream_info_->set_codec_string( video_stream_info_->set_codec_string(
codec_config.GetCodecString(video_stream_info_->codec())); codec_config->GetCodecString(video_stream_info_->codec()));
std::vector<uint8_t> extra_data; std::vector<uint8_t> extra_data;
codec_config.Write(&extra_data); codec_config->Write(&extra_data);
video_stream_info_->set_extra_data(extra_data); video_stream_info_->set_extra_data(extra_data);
streams.push_back(video_stream_info_); streams.push_back(video_stream_info_);
init_cb_.Run(streams); init_cb_.Run(streams);

View File

@ -139,6 +139,11 @@ const uint8_t kEncryptedFrame[] = {
0x01, 0x01,
}; };
const uint8_t kVP8Frame[] = {
0x52, 0x04, 0x00, 0x9d, 0x01, 0x2a, 0x40, 0x01, 0xf0, 0x00, 0x00, 0x47,
0x08, 0x85, 0x85, 0x88, 0x85, 0x84, 0x88, 0x01, 0x24, 0x10, 0x17, 0x67,
0x63, 0x3f, 0xbb, 0xe5, 0xcf, 0x9b, 0x7d, 0x53, 0xec, 0x67, 0xa2, 0xcf,
};
const uint8_t kVP9Frame[] = { const uint8_t kVP9Frame[] = {
0xb1, 0x24, 0xc1, 0xa1, 0x40, 0x00, 0x4f, 0x80, 0x2c, 0xa0, 0x41, 0xc1, 0xb1, 0x24, 0xc1, 0xa1, 0x40, 0x00, 0x4f, 0x80, 0x2c, 0xa0, 0x41, 0xc1,
0x20, 0xe0, 0xc3, 0xf0, 0x00, 0x09, 0x00, 0x7c, 0x57, 0x77, 0x3f, 0x67, 0x20, 0xe0, 0xc3, 0xf0, 0x00, 0x09, 0x00, 0x7c, 0x57, 0x77, 0x3f, 0x67,
@ -166,7 +171,7 @@ scoped_ptr<Cluster> CreateCluster(int timecode,
data_length = block_info[i].data_length; data_length = block_info[i].data_length;
} else { } else {
data = kDefaultBlockData; data = kDefaultBlockData;
data_length = sizeof(kDefaultBlockData); data_length = arraysize(kDefaultBlockData);
} }
if (block_info[i].use_simple_block) { if (block_info[i].use_simple_block) {
@ -190,23 +195,11 @@ scoped_ptr<Cluster> CreateCluster(int timecode,
return cb.Finish(); return cb.Finish();
} }
// Creates a Cluster with one encrypted Block. |bytes_to_write| is number of // Creates a Cluster with one block.
// bytes of the encrypted frame to write. scoped_ptr<Cluster> CreateCluster(const uint8_t* data, size_t data_size) {
scoped_ptr<Cluster> CreateEncryptedCluster(int bytes_to_write) {
CHECK_GT(bytes_to_write, 0);
CHECK_LE(bytes_to_write, static_cast<int>(sizeof(kEncryptedFrame)));
ClusterBuilder cb; ClusterBuilder cb;
cb.SetClusterTimecode(0); cb.SetClusterTimecode(0);
cb.AddSimpleBlock(kVideoTrackNum, 0, 0, kEncryptedFrame, bytes_to_write); cb.AddSimpleBlock(kVideoTrackNum, 0, 0, data, data_size);
return cb.Finish();
}
// Creates a Cluster with one vp9 frame (keyframe).
scoped_ptr<Cluster> CreateVP9Cluster() {
ClusterBuilder cb;
cb.SetClusterTimecode(0);
cb.AddSimpleBlock(kVideoTrackNum, 0, 0, kVP9Frame, arraysize(kVP9Frame));
return cb.Finish(); return cb.Finish();
} }
@ -378,7 +371,8 @@ class WebMClusterParserTest : public testing::Test {
const std::string& audio_encryption_key_id, const std::string& audio_encryption_key_id,
const std::string& video_encryption_key_id, const std::string& video_encryption_key_id,
const AudioCodec audio_codec, const AudioCodec audio_codec,
const VideoCodec video_codec) { const VideoCodec video_codec,
const MediaParser::InitCB& init_cb) {
audio_stream_info_->set_codec(audio_codec); audio_stream_info_->set_codec(audio_codec);
video_stream_info_->set_codec(video_codec); video_stream_info_->set_codec(video_codec);
return new WebMClusterParser( return new WebMClusterParser(
@ -387,14 +381,15 @@ class WebMClusterParserTest : public testing::Test {
ignored_tracks, audio_encryption_key_id, video_encryption_key_id, ignored_tracks, audio_encryption_key_id, video_encryption_key_id,
base::Bind(&WebMClusterParserTest::NewSampleEvent, base::Bind(&WebMClusterParserTest::NewSampleEvent,
base::Unretained(this)), base::Unretained(this)),
base::Bind(&WebMClusterParserTest::InitEvent, base::Unretained(this))); init_cb);
} }
// Create a default version of the parser for test. // Create a default version of the parser for test.
WebMClusterParser* CreateDefaultParser() { WebMClusterParser* CreateDefaultParser() {
return CreateParserHelper(kNoTimestamp, kNoTimestamp, TextTracks(), return CreateParserHelper(kNoTimestamp, kNoTimestamp, TextTracks(),
std::set<int64_t>(), std::string(), std::string(), std::set<int64_t>(), std::string(), std::string(),
kUnknownAudioCodec, kCodecVP8); kUnknownAudioCodec, kCodecVP8,
MediaParser::InitCB());
} }
// Create a parser for test with custom audio and video default durations, and // Create a parser for test with custom audio and video default durations, and
@ -405,15 +400,16 @@ class WebMClusterParserTest : public testing::Test {
const WebMTracksParser::TextTracks& text_tracks = TextTracks()) { const WebMTracksParser::TextTracks& text_tracks = TextTracks()) {
return CreateParserHelper(audio_default_duration, video_default_duration, return CreateParserHelper(audio_default_duration, video_default_duration,
text_tracks, std::set<int64_t>(), std::string(), text_tracks, std::set<int64_t>(), std::string(),
std::string(), kUnknownAudioCodec, kCodecVP8); std::string(), kUnknownAudioCodec, kCodecVP8,
MediaParser::InitCB());
} }
// Create a parser for test with custom ignored tracks. // Create a parser for test with custom ignored tracks.
WebMClusterParser* CreateParserWithIgnoredTracks( WebMClusterParser* CreateParserWithIgnoredTracks(
std::set<int64_t>& ignored_tracks) { std::set<int64_t>& ignored_tracks) {
return CreateParserHelper(kNoTimestamp, kNoTimestamp, TextTracks(), return CreateParserHelper(
ignored_tracks, std::string(), std::string(), kNoTimestamp, kNoTimestamp, TextTracks(), ignored_tracks, std::string(),
kUnknownAudioCodec, kCodecVP8); std::string(), kUnknownAudioCodec, kCodecVP8, MediaParser::InitCB());
} }
// Create a parser for test with custom encryption key ids and audio codec. // Create a parser for test with custom encryption key ids and audio codec.
@ -423,14 +419,17 @@ class WebMClusterParserTest : public testing::Test {
const AudioCodec audio_codec) { const AudioCodec audio_codec) {
return CreateParserHelper(kNoTimestamp, kNoTimestamp, TextTracks(), return CreateParserHelper(kNoTimestamp, kNoTimestamp, TextTracks(),
std::set<int64_t>(), audio_encryption_key_id, std::set<int64_t>(), audio_encryption_key_id,
video_encryption_key_id, audio_codec, kCodecVP8); video_encryption_key_id, audio_codec, kCodecVP8,
MediaParser::InitCB());
} }
// Create a parser for test with custom video codec. // Create a parser for test with custom video codec, also check for init
// events.
WebMClusterParser* CreateParserWithVideoCodec(const VideoCodec video_codec) { WebMClusterParser* CreateParserWithVideoCodec(const VideoCodec video_codec) {
return CreateParserHelper(kNoTimestamp, kNoTimestamp, TextTracks(), return CreateParserHelper(
std::set<int64_t>(), std::string(), std::string(), kNoTimestamp, kNoTimestamp, TextTracks(), std::set<int64_t>(),
kUnknownAudioCodec, video_codec); std::string(), std::string(), kUnknownAudioCodec, video_codec,
base::Bind(&WebMClusterParserTest::InitEvent, base::Unretained(this)));
} }
bool VerifyBuffers(const BlockInfo* block_info, int block_count) { bool VerifyBuffers(const BlockInfo* block_info, int block_count) {
@ -563,10 +562,6 @@ TEST_F(WebMClusterParserTest, ParseClusterWithSingleCall) {
int result = parser_->Parse(cluster->data(), cluster->size()); int result = parser_->Parse(cluster->data(), cluster->size());
EXPECT_EQ(cluster->size(), result); EXPECT_EQ(cluster->size(), result);
ASSERT_TRUE(VerifyBuffers(kDefaultBlockInfo, block_count)); ASSERT_TRUE(VerifyBuffers(kDefaultBlockInfo, block_count));
// Verify init event called.
ASSERT_EQ(2u, streams_from_init_event_.size());
EXPECT_EQ(kStreamAudio, streams_from_init_event_[0]->stream_type());
EXPECT_EQ(kStreamVideo, streams_from_init_event_[1]->stream_type());
} }
TEST_F(WebMClusterParserTest, ParseClusterWithMultipleCalls) { TEST_F(WebMClusterParserTest, ParseClusterWithMultipleCalls) {
@ -626,7 +621,7 @@ TEST_F(WebMClusterParserTest, ParseBlockGroup) {
0xA1, 0x85, 0x82, 0x00, 0x21, 0x00, 0x55, // Block(size=5, track=2, ts=33) 0xA1, 0x85, 0x82, 0x00, 0x21, 0x00, 0x55, // Block(size=5, track=2, ts=33)
0x9B, 0x81, 0x22, // BlockDuration(size=1, value=34) 0x9B, 0x81, 0x22, // BlockDuration(size=1, value=34)
}; };
const int kClusterSize = sizeof(kClusterData); const int kClusterSize = arraysize(kClusterData);
int result = parser_->Parse(kClusterData, kClusterSize); int result = parser_->Parse(kClusterData, kClusterSize);
EXPECT_EQ(kClusterSize, result); EXPECT_EQ(kClusterSize, result);
@ -780,8 +775,21 @@ TEST_F(WebMClusterParserTest, ParseMultipleTextTracks) {
} }
} }
TEST_F(WebMClusterParserTest, ParseVP8) {
scoped_ptr<Cluster> cluster(CreateCluster(kVP8Frame, arraysize(kVP8Frame)));
parser_.reset(CreateParserWithVideoCodec(kCodecVP8));
EXPECT_EQ(cluster->size(), parser_->Parse(cluster->data(), cluster->size()));
ASSERT_EQ(2u, streams_from_init_event_.size());
EXPECT_EQ(kStreamAudio, streams_from_init_event_[0]->stream_type());
EXPECT_EQ(kStreamVideo, streams_from_init_event_[1]->stream_type());
EXPECT_EQ("vp08.01.00.08.01.01.00.00",
streams_from_init_event_[1]->codec_string());
}
TEST_F(WebMClusterParserTest, ParseVP9) { TEST_F(WebMClusterParserTest, ParseVP9) {
scoped_ptr<Cluster> cluster(CreateVP9Cluster()); scoped_ptr<Cluster> cluster(CreateCluster(kVP9Frame, arraysize(kVP9Frame)));
parser_.reset(CreateParserWithVideoCodec(kCodecVP9)); parser_.reset(CreateParserWithVideoCodec(kCodecVP9));
EXPECT_EQ(cluster->size(), parser_->Parse(cluster->data(), cluster->size())); EXPECT_EQ(cluster->size(), parser_->Parse(cluster->data(), cluster->size()));
@ -794,7 +802,8 @@ TEST_F(WebMClusterParserTest, ParseVP9) {
} }
TEST_F(WebMClusterParserTest, ParseEncryptedBlock) { TEST_F(WebMClusterParserTest, ParseEncryptedBlock) {
scoped_ptr<Cluster> cluster(CreateEncryptedCluster(sizeof(kEncryptedFrame))); scoped_ptr<Cluster> cluster(
CreateCluster(kEncryptedFrame, arraysize(kEncryptedFrame)));
parser_.reset(CreateParserWithKeyIdsAndAudioCodec( parser_.reset(CreateParserWithKeyIdsAndAudioCodec(
std::string(), "video_key_id", kUnknownAudioCodec)); std::string(), "video_key_id", kUnknownAudioCodec));
@ -809,7 +818,7 @@ TEST_F(WebMClusterParserTest, ParseEncryptedBlock) {
TEST_F(WebMClusterParserTest, ParseBadEncryptedBlock) { TEST_F(WebMClusterParserTest, ParseBadEncryptedBlock) {
scoped_ptr<Cluster> cluster( scoped_ptr<Cluster> cluster(
CreateEncryptedCluster(sizeof(kEncryptedFrame) - 2)); CreateCluster(kEncryptedFrame, arraysize(kEncryptedFrame) - 2));
parser_.reset(CreateParserWithKeyIdsAndAudioCodec( parser_.reset(CreateParserWithKeyIdsAndAudioCodec(
std::string(), "video_key_id", kUnknownAudioCodec)); std::string(), "video_key_id", kUnknownAudioCodec));
@ -822,7 +831,7 @@ TEST_F(WebMClusterParserTest, ParseInvalidZeroSizedCluster) {
0x1F, 0x43, 0xB6, 0x75, 0x80, // CLUSTER (size = 0) 0x1F, 0x43, 0xB6, 0x75, 0x80, // CLUSTER (size = 0)
}; };
EXPECT_EQ(-1, parser_->Parse(kBuffer, sizeof(kBuffer))); EXPECT_EQ(-1, parser_->Parse(kBuffer, arraysize(kBuffer)));
// Verify init event not called. // Verify init event not called.
ASSERT_EQ(0u, streams_from_init_event_.size()); ASSERT_EQ(0u, streams_from_init_event_.size());
} }
@ -833,7 +842,7 @@ TEST_F(WebMClusterParserTest, ParseInvalidUnknownButActuallyZeroSizedCluster) {
0x1F, 0x43, 0xB6, 0x75, 0x85, // CLUSTER (size = 5) 0x1F, 0x43, 0xB6, 0x75, 0x85, // CLUSTER (size = 5)
}; };
EXPECT_EQ(-1, parser_->Parse(kBuffer, sizeof(kBuffer))); EXPECT_EQ(-1, parser_->Parse(kBuffer, arraysize(kBuffer)));
} }
TEST_F(WebMClusterParserTest, ParseInvalidTextBlockGroupWithoutDuration) { TEST_F(WebMClusterParserTest, ParseInvalidTextBlockGroupWithoutDuration) {