Add more unit tests for EsParserH26x.

This add several unit tests for EsParserH26x to test the various
NAL unit orders and access unit contents.  Some tests are disabled
and will be fixed in another patch.

Change-Id: Id5e3291e22f1fe17ada2c03c42e2cdfe226abcb2
This commit is contained in:
Jacob Trimble 2016-05-04 14:19:40 -07:00
parent 86369efc30
commit 9bb6c5d8d2
7 changed files with 323 additions and 22 deletions

View File

@ -46,6 +46,7 @@ class Nalu {
}; };
enum H265NaluType { enum H265NaluType {
H265_TRAIL_N = 0, H265_TRAIL_N = 0,
H265_TRAIL_R = 1,
H265_TSA_N = 2, H265_TSA_N = 2,
H265_TSA_R = 3, H265_TSA_R = 3,
H265_STSA_N = 4, H265_STSA_N = 4,
@ -71,6 +72,7 @@ class Nalu {
H265_EOS = 36, H265_EOS = 36,
H265_EOB = 37, H265_EOB = 37,
H265_FD = 38,
H265_PREFIX_SEI = 39, H265_PREFIX_SEI = 39,

View File

@ -22,7 +22,11 @@ namespace mp2t {
EsParserH264::EsParserH264(uint32_t pid, EsParserH264::EsParserH264(uint32_t pid,
const NewStreamInfoCB& new_stream_info_cb, const NewStreamInfoCB& new_stream_info_cb,
const EmitSampleCB& emit_sample_cb) const EmitSampleCB& emit_sample_cb)
: EsParserH26x(Nalu::kH264, pid, emit_sample_cb), : EsParserH26x(Nalu::kH264,
scoped_ptr<H26xByteToUnitStreamConverter>(
new H264ByteToUnitStreamConverter()),
pid,
emit_sample_cb),
new_stream_info_cb_(new_stream_info_cb), new_stream_info_cb_(new_stream_info_cb),
decoder_config_check_pending_(false), decoder_config_check_pending_(false),
h264_parser_(new H264Parser()) {} h264_parser_(new H264Parser()) {}

View File

@ -14,8 +14,8 @@
#include "packager/media/base/timestamp.h" #include "packager/media/base/timestamp.h"
#include "packager/media/base/video_stream_info.h" #include "packager/media/base/video_stream_info.h"
#include "packager/media/filters/hevc_decoder_configuration.h" #include "packager/media/filters/hevc_decoder_configuration.h"
#include "packager/media/filters/h265_byte_to_unit_stream_converter.h"
#include "packager/media/filters/h265_parser.h" #include "packager/media/filters/h265_parser.h"
#include "packager/media/filters/h26x_byte_to_unit_stream_converter.h"
#include "packager/media/formats/mp2t/mp2t_common.h" #include "packager/media/formats/mp2t/mp2t_common.h"
namespace edash_packager { namespace edash_packager {
@ -25,7 +25,11 @@ namespace mp2t {
EsParserH265::EsParserH265(uint32_t pid, EsParserH265::EsParserH265(uint32_t pid,
const NewStreamInfoCB& new_stream_info_cb, const NewStreamInfoCB& new_stream_info_cb,
const EmitSampleCB& emit_sample_cb) const EmitSampleCB& emit_sample_cb)
: EsParserH26x(Nalu::kH265, pid, emit_sample_cb), : EsParserH26x(Nalu::kH265,
scoped_ptr<H26xByteToUnitStreamConverter>(
new H265ByteToUnitStreamConverter()),
pid,
emit_sample_cb),
new_stream_info_cb_(new_stream_info_cb), new_stream_info_cb_(new_stream_info_cb),
decoder_config_check_pending_(false), decoder_config_check_pending_(false),
h265_parser_(new H265Parser()) {} h265_parser_(new H265Parser()) {}

View File

@ -20,32 +20,20 @@ namespace edash_packager {
namespace media { namespace media {
namespace mp2t { namespace mp2t {
namespace { EsParserH26x::EsParserH26x(
Nalu::CodecType type,
H26xByteToUnitStreamConverter* CreateStreamConverter(Nalu::CodecType type) { scoped_ptr<H26xByteToUnitStreamConverter> stream_converter,
if (type == Nalu::kH264) { uint32_t pid,
return new H264ByteToUnitStreamConverter(); const EmitSampleCB& emit_sample_cb)
} else {
DCHECK_EQ(Nalu::kH265, type);
return new H265ByteToUnitStreamConverter();
}
}
} // anonymous namespace
EsParserH26x::EsParserH26x(Nalu::CodecType type,
uint32_t pid,
const EmitSampleCB& emit_sample_cb)
: EsParser(pid), : EsParser(pid),
emit_sample_cb_(emit_sample_cb), emit_sample_cb_(emit_sample_cb),
type_(type), type_(type),
es_queue_(new media::OffsetByteQueue()), es_queue_(new media::OffsetByteQueue()),
current_access_unit_pos_(0), current_access_unit_pos_(0),
found_access_unit_(false), found_access_unit_(false),
stream_converter_(CreateStreamConverter(type)), stream_converter_(stream_converter.Pass()),
pending_sample_duration_(0), pending_sample_duration_(0),
waiting_for_key_frame_(true) { waiting_for_key_frame_(true) {}
}
EsParserH26x::~EsParserH26x() {} EsParserH26x::~EsParserH26x() {}

View File

@ -27,6 +27,7 @@ namespace mp2t {
class EsParserH26x : public EsParser { class EsParserH26x : public EsParser {
public: public:
EsParserH26x(Nalu::CodecType type, EsParserH26x(Nalu::CodecType type,
scoped_ptr<H26xByteToUnitStreamConverter> stream_converter,
uint32_t pid, uint32_t pid,
const EmitSampleCB& emit_sample_cb); const EmitSampleCB& emit_sample_cb);
~EsParserH26x() override; ~EsParserH26x() override;

View File

@ -0,0 +1,301 @@
// 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 <vector>
#include "packager/base/bind.h"
#include "packager/base/logging.h"
#include "packager/media/base/media_sample.h"
#include "packager/media/base/stream_info.h"
#include "packager/media/base/timestamp.h"
#include "packager/media/filters/h26x_byte_to_unit_stream_converter.h"
#include "packager/media/formats/mp2t/es_parser_h26x.h"
namespace edash_packager {
namespace media {
namespace mp2t {
namespace {
// NAL unit types used for testing.
enum H265NaluType {
kAud = Nalu::H265_AUD,
kSps = Nalu::H265_SPS,
kSei = Nalu::H265_PREFIX_SEI,
// Something with |can_start_access_unit() == false|.
kRsv = Nalu::H265_FD,
// Non-key-frame video slice.
kVcl = Nalu::H265_TRAIL_N,
kVclKeyFrame = Nalu::H265_IDR_W_RADL,
// Needs to be different than |kVCL| so we can tell the difference.
kVclWithNuhLayer = Nalu::H265_TRAIL_R,
// Used to separate expected access units.
kSeparator = 0xff,
};
class FakeByteToUnitStreamConverter : public H26xByteToUnitStreamConverter {
public:
FakeByteToUnitStreamConverter()
: H26xByteToUnitStreamConverter(Nalu::kH265) {}
bool GetDecoderConfigurationRecord(
std::vector<uint8_t>* decoder_config) const override {
return true;
}
bool ProcessNalu(const Nalu& nalu) override {
// This processed nothing, base class should copy everything.
return false;
}
};
// This is the code-under-test. This implements the required abstract methods
// to ignore the contents of the NAL units. This behaves the same as the
// H.264 and H.265 types.
class TestableEsParser : public EsParserH26x {
public:
TestableEsParser(const NewStreamInfoCB& new_stream_info_cb,
const EmitSampleCB& emit_sample_cb)
: EsParserH26x(Nalu::kH265,
scoped_ptr<H26xByteToUnitStreamConverter>(
new FakeByteToUnitStreamConverter()),
0,
emit_sample_cb),
new_stream_info_cb_(new_stream_info_cb),
decoder_config_check_pending_(false) {}
bool ProcessNalu(const Nalu& nalu,
bool* is_key_frame,
int* pps_id_for_access_unit) override {
if (nalu.type() == Nalu::H265_SPS) {
decoder_config_check_pending_ = true;
} else if (nalu.is_video_slice()) {
// This should be the same as EsParserH265::ProcessNalu.
*is_key_frame = nalu.type() == Nalu::H265_IDR_W_RADL ||
nalu.type() == Nalu::H265_IDR_N_LP;
*pps_id_for_access_unit = kTestPpsId;
}
return true;
}
bool UpdateVideoDecoderConfig(int pps_id) override {
if (decoder_config_check_pending_) {
EXPECT_EQ(kTestPpsId, pps_id);
new_stream_info_cb_.Run(nullptr);
decoder_config_check_pending_ = false;
}
return true;
}
private:
const int kTestPpsId = 123;
NewStreamInfoCB new_stream_info_cb_;
bool decoder_config_check_pending_;
};
std::vector<uint8_t> CreateNalu(H265NaluType type, int i) {
std::vector<uint8_t> ret;
ret.resize(4);
ret[0] = (type << 1);
// nuh_layer_id == 1, nuh_temporal_id_plus1 == 1
ret[1] = (type == kVclWithNuhLayer ? 9 : 1);
// Add some extra data to tell consecutive frames apart.
ret[2] = 0xff;
ret[3] = i + 1;
return ret;
}
} // namespace
class EsParserH26xTest : public testing::Test {
public:
EsParserH26xTest() : sample_count_(0), has_stream_info_(false) {}
// Runs a test by constructing NAL units of the given types and passing them
// to the parser. Access units should be separated by |kSeparator|, there
// should be one at the start and not at the end.
void RunTest(const H265NaluType* types, size_t types_count);
void EmitSample(uint32_t pid, const scoped_refptr<MediaSample>& sample) {
size_t sample_id = sample_count_;
sample_count_++;
if (sample_count_ == 1)
EXPECT_TRUE(sample->is_key_frame());
ASSERT_GT(samples_.size(), sample_id);
const std::vector<uint8_t> sample_data(
sample->data(), sample->data() + sample->data_size());
EXPECT_EQ(samples_[sample_id], sample_data);
}
void NewVideoConfig(const scoped_refptr<StreamInfo>& config) {
has_stream_info_ = true;
}
protected:
std::vector<std::vector<uint8_t>> samples_;
size_t sample_count_;
bool has_stream_info_;
};
void EsParserH26xTest::RunTest(const H265NaluType* types,
size_t types_count) {
// Duration of one 25fps video frame in 90KHz clock units.
const uint32_t kMpegTicksPerFrame = 3600;
const uint8_t kStartCode[] = {0x00, 0x00, 0x01};
TestableEsParser es_parser(
base::Bind(&EsParserH26xTest::NewVideoConfig, base::Unretained(this)),
base::Bind(&EsParserH26xTest::EmitSample, base::Unretained(this)));
bool seen_key_frame = false;
std::vector<uint8_t> cur_sample_data;
ASSERT_EQ(kSeparator, types[0]);
for (size_t k = 1; k < types_count; k++) {
if (types[k] == kSeparator) {
// We should not be emitting samples until we see a key frame.
if (seen_key_frame)
samples_.push_back(cur_sample_data);
cur_sample_data.clear();
} else {
if (types[k] == kVclKeyFrame)
seen_key_frame = true;
std::vector<uint8_t> es_data = CreateNalu(types[k], k);
cur_sample_data.push_back(0);
cur_sample_data.push_back(0);
cur_sample_data.push_back(0);
cur_sample_data.push_back(es_data.size());
cur_sample_data.insert(cur_sample_data.end(), es_data.begin(),
es_data.end());
es_data.insert(es_data.begin(), kStartCode,
kStartCode + arraysize(kStartCode));
const int64_t pts = k * kMpegTicksPerFrame;
const int64_t dts = k * kMpegTicksPerFrame;
// This may process the previous sample; but since we don't know whether
// we are at the end yet, this will not process the current sample until
// later.
ASSERT_TRUE(es_parser.Parse(es_data.data(), es_data.size(), pts, dts));
}
}
if (seen_key_frame)
samples_.push_back(cur_sample_data);
es_parser.Flush();
}
TEST_F(EsParserH26xTest, BasicSupport) {
const H265NaluType kData[] = {
kSeparator, kAud, kSps, kVclKeyFrame,
kSeparator, kAud, kVcl,
kSeparator, kAud, kVcl,
};
RunTest(kData, arraysize(kData));
EXPECT_EQ(3u, sample_count_);
EXPECT_TRUE(has_stream_info_);
}
TEST_F(EsParserH26xTest, DeterminesAccessUnitsWithoutAUD) {
const H265NaluType kData[] = {
kSeparator, kSps, kVclKeyFrame,
kSeparator, kVcl,
kSeparator, kVcl,
kSeparator, kSei, kVcl,
};
RunTest(kData, arraysize(kData));
EXPECT_EQ(4u, sample_count_);
EXPECT_TRUE(has_stream_info_);
}
TEST_F(EsParserH26xTest, DoesNotStartOnRsv) {
const H265NaluType kData[] = {
kSeparator, kSps, kVclKeyFrame, kRsv,
kSeparator, kAud, kVcl,
kSeparator, kSei, kVcl,
};
RunTest(kData, arraysize(kData));
EXPECT_EQ(3u, sample_count_);
EXPECT_TRUE(has_stream_info_);
}
TEST_F(EsParserH26xTest, DISABLED_SupportsNonZeroNuhLayerId) {
const H265NaluType kData[] = {
kSeparator, kSps, kVclKeyFrame,
kSeparator, kAud, kVcl, kSei, kSei, kVclWithNuhLayer, kRsv,
kSeparator, kSei, kVcl,
kSeparator, kAud, kVcl, kSps, kRsv, kVclWithNuhLayer,
kSeparator, kVcl,
};
RunTest(kData, arraysize(kData));
EXPECT_EQ(5u, sample_count_);
EXPECT_TRUE(has_stream_info_);
}
TEST_F(EsParserH26xTest, WaitsForKeyFrame) {
const H265NaluType kData[] = {
kSeparator, kVcl,
kSeparator, kVcl,
kSeparator, kSps, kVclKeyFrame,
kSeparator, kVcl,
kSeparator, kVcl,
};
RunTest(kData, arraysize(kData));
EXPECT_EQ(3u, sample_count_);
EXPECT_TRUE(has_stream_info_);
}
TEST_F(EsParserH26xTest, EmitsFramesWithNoStreamInfo) {
const H265NaluType kData[] = {
kSeparator, kVclKeyFrame,
kSeparator, kVcl,
kSeparator, kVcl,
};
RunTest(kData, arraysize(kData));
EXPECT_EQ(3u, sample_count_);
EXPECT_FALSE(has_stream_info_);
}
// TODO(modmaker): Currently, the SEI here will not be included. This needs to
// be fixed.
TEST_F(EsParserH26xTest, DISABLED_EmitsLastFrameWhenDoesntEndOnVCL) {
// This tests that it will emit the last frame and last frame will include
// the correct data and nothing extra.
const H265NaluType kData[] = {
kSeparator, kVclKeyFrame,
kSeparator, kVcl,
kSeparator, kVcl, kSei,
};
RunTest(kData, arraysize(kData));
EXPECT_EQ(3u, sample_count_);
EXPECT_FALSE(has_stream_info_);
}
TEST_F(EsParserH26xTest, DISABLED_EmitsLastFrameWithNuhLayerId) {
const H265NaluType kData[] = {
kSeparator, kVclKeyFrame,
kSeparator, kVcl,
kSeparator, kVcl, kVclWithNuhLayer, kSei,
};
RunTest(kData, arraysize(kData));
EXPECT_EQ(3u, sample_count_);
EXPECT_FALSE(has_stream_info_);
}
} // namespace mp2t
} // namespace media
} // namespace edash_packager

View File

@ -61,6 +61,7 @@
'sources': [ 'sources': [
'adts_header_unittest.cc', 'adts_header_unittest.cc',
'es_parser_h264_unittest.cc', 'es_parser_h264_unittest.cc',
'es_parser_h26x_unittest.cc',
'mp2t_media_parser_unittest.cc', 'mp2t_media_parser_unittest.cc',
'pes_packet_generator_unittest.cc', 'pes_packet_generator_unittest.cc',
'ts_segmenter_unittest.cc', 'ts_segmenter_unittest.cc',