| Index: media/formats/mp2t/es_parser_h264_unittest.cc
|
| diff --git a/media/formats/mp2t/es_parser_h264_unittest.cc b/media/formats/mp2t/es_parser_h264_unittest.cc
|
| index 2ac94c79943ec6393dad615e61c4d932e40ed6cb..5228b6e8a1e58884cae9950accfe0297530353f1 100644
|
| --- a/media/formats/mp2t/es_parser_h264_unittest.cc
|
| +++ b/media/formats/mp2t/es_parser_h264_unittest.cc
|
| @@ -2,19 +2,18 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| -#include <algorithm>
|
| +#include <sstream>
|
| +#include <string>
|
| #include <vector>
|
|
|
| #include "base/bind.h"
|
| -#include "base/command_line.h"
|
| -#include "base/files/memory_mapped_file.h"
|
| #include "base/logging.h"
|
| -#include "base/path_service.h"
|
| +#include "base/strings/string_util.h"
|
| #include "base/time/time.h"
|
| #include "media/base/stream_parser_buffer.h"
|
| -#include "media/base/test_data_util.h"
|
| #include "media/filters/h264_parser.h"
|
| #include "media/formats/mp2t/es_parser_h264.h"
|
| +#include "media/formats/mp2t/es_parser_test_base.h"
|
| #include "testing/gtest/include/gtest/gtest.h"
|
|
|
| namespace media {
|
| @@ -22,36 +21,51 @@ class VideoDecoderConfig;
|
|
|
| namespace mp2t {
|
|
|
| -namespace {
|
| +class EsParserH264Test : public EsParserTestBase,
|
| + public testing::Test {
|
| + public:
|
| + EsParserH264Test() {}
|
| + virtual ~EsParserH264Test() {}
|
| +
|
| + protected:
|
| + void LoadH264Stream(const char* filename);
|
| + void GetPesTimestamps(std::vector<Packet>* pes_packets);
|
| + bool Process(const std::vector<Packet>& pes_packets, bool force_timing);
|
| + void CheckAccessUnits();
|
| +
|
| + // Access units of the stream with AUD NALUs.
|
| + std::vector<Packet> access_units_;
|
|
|
| -struct Packet {
|
| - // Offset in the stream.
|
| - size_t offset;
|
| + private:
|
| + // Get the offset of the start of each access unit of |stream_|.
|
| + // This function assumes there is only one slice per access unit.
|
| + // This is a very simplified access unit segmenter that is good
|
| + // enough for unit tests.
|
| + void GetAccessUnits();
|
|
|
| - // Size of the packet.
|
| - size_t size;
|
| + // Insert an AUD before each access unit.
|
| + // Update |stream_| and |access_units_| accordingly.
|
| + void InsertAUD();
|
|
|
| - // Timestamp of the packet.
|
| - base::TimeDelta pts;
|
| + DISALLOW_COPY_AND_ASSIGN(EsParserH264Test);
|
| };
|
|
|
| -// Compute the size of each packet assuming packets are given in stream order
|
| -// and the last packet covers the end of the stream.
|
| -void ComputePacketSize(std::vector<Packet>& packets, size_t stream_size) {
|
| - for (size_t k = 0; k < packets.size() - 1; k++) {
|
| - DCHECK_GE(packets[k + 1].offset, packets[k].offset);
|
| - packets[k].size = packets[k + 1].offset - packets[k].offset;
|
| - }
|
| - packets[packets.size() - 1].size =
|
| - stream_size - packets[packets.size() - 1].offset;
|
| +void EsParserH264Test::LoadH264Stream(const char* filename) {
|
| + // Load the input H264 file and segment it into access units.
|
| + LoadStream(filename);
|
| + GetAccessUnits();
|
| + ASSERT_GT(access_units_.size(), 0u);
|
| +
|
| + // Insert AUDs into the stream.
|
| + InsertAUD();
|
| +
|
| + // Generate some timestamps based on a 25fps stream.
|
| + for (size_t k = 0; k < access_units_.size(); k++)
|
| + access_units_[k].pts = base::TimeDelta::FromMilliseconds(k * 40u);
|
| }
|
|
|
| -// Get the offset of the start of each access unit.
|
| -// This function assumes there is only one slice per access unit.
|
| -// This is a very simplified access unit segmenter that is good
|
| -// enough for unit tests.
|
| -std::vector<Packet> GetAccessUnits(const uint8* stream, size_t stream_size) {
|
| - std::vector<Packet> access_units;
|
| +void EsParserH264Test::GetAccessUnits() {
|
| + access_units_.resize(0);
|
| bool start_access_unit = true;
|
|
|
| // In a first pass, retrieve the offsets of all access units.
|
| @@ -61,7 +75,7 @@ std::vector<Packet> GetAccessUnits(const uint8* stream, size_t stream_size) {
|
| off_t relative_offset = 0;
|
| off_t start_code_size = 0;
|
| bool success = H264Parser::FindStartCode(
|
| - &stream[offset], stream_size - offset,
|
| + &stream_[offset], stream_.size() - offset,
|
| &relative_offset, &start_code_size);
|
| if (!success)
|
| break;
|
| @@ -70,15 +84,15 @@ std::vector<Packet> GetAccessUnits(const uint8* stream, size_t stream_size) {
|
| if (start_access_unit) {
|
| Packet cur_access_unit;
|
| cur_access_unit.offset = offset;
|
| - access_units.push_back(cur_access_unit);
|
| + access_units_.push_back(cur_access_unit);
|
| start_access_unit = false;
|
| }
|
|
|
| // Get the NALU type.
|
| offset += start_code_size;
|
| - if (offset >= stream_size)
|
| + if (offset >= stream_.size())
|
| break;
|
| - int nal_unit_type = stream[offset] & 0x1f;
|
| + int nal_unit_type = stream_[offset] & 0x1f;
|
|
|
| // We assume there is only one slice per access unit.
|
| if (nal_unit_type == H264NALU::kIDRSlice ||
|
| @@ -87,95 +101,46 @@ std::vector<Packet> GetAccessUnits(const uint8* stream, size_t stream_size) {
|
| }
|
| }
|
|
|
| - ComputePacketSize(access_units, stream_size);
|
| - return access_units;
|
| + ComputePacketSize(&access_units_);
|
| }
|
|
|
| -// Append an AUD NALU at the beginning of each access unit
|
| -// needed for streams which do not already have AUD NALUs.
|
| -void AppendAUD(
|
| - const uint8* stream, size_t stream_size,
|
| - const std::vector<Packet>& access_units,
|
| - std::vector<uint8>& stream_with_aud,
|
| - std::vector<Packet>& access_units_with_aud) {
|
| +void EsParserH264Test::InsertAUD() {
|
| uint8 aud[] = { 0x00, 0x00, 0x01, 0x09 };
|
| - stream_with_aud.resize(stream_size + access_units.size() * sizeof(aud));
|
| - access_units_with_aud.resize(access_units.size());
|
| +
|
| + std::vector<uint8> stream_with_aud(
|
| + stream_.size() + access_units_.size() * sizeof(aud));
|
| + std::vector<EsParserTestBase::Packet> access_units_with_aud(
|
| + access_units_.size());
|
|
|
| size_t offset = 0;
|
| - for (size_t k = 0; k < access_units.size(); k++) {
|
| + for (size_t k = 0; k < access_units_.size(); k++) {
|
| access_units_with_aud[k].offset = offset;
|
| - access_units_with_aud[k].size = access_units[k].size + sizeof(aud);
|
| + access_units_with_aud[k].size = access_units_[k].size + sizeof(aud);
|
|
|
| memcpy(&stream_with_aud[offset], aud, sizeof(aud));
|
| offset += sizeof(aud);
|
|
|
| memcpy(&stream_with_aud[offset],
|
| - &stream[access_units[k].offset], access_units[k].size);
|
| - offset += access_units[k].size;
|
| - }
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -class EsParserH264Test : public testing::Test {
|
| - public:
|
| - EsParserH264Test() : buffer_count_(0) {
|
| + &stream_[access_units_[k].offset], access_units_[k].size);
|
| + offset += access_units_[k].size;
|
| }
|
| - virtual ~EsParserH264Test() {}
|
| -
|
| - protected:
|
| - void LoadStream(const char* filename);
|
| - void GetPesTimestamps(std::vector<Packet>& pes_packets);
|
| - void ProcessPesPackets(const std::vector<Packet>& pes_packets,
|
| - bool force_timing);
|
|
|
| - // Stream with AUD NALUs.
|
| - std::vector<uint8> stream_;
|
| -
|
| - // Access units of the stream with AUD NALUs.
|
| - std::vector<Packet> access_units_;
|
| -
|
| - // Number of buffers generated while parsing the H264 stream.
|
| - size_t buffer_count_;
|
| -
|
| - private:
|
| - void EmitBuffer(scoped_refptr<StreamParserBuffer> buffer);
|
| -
|
| - void NewVideoConfig(const VideoDecoderConfig& config) {
|
| - }
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(EsParserH264Test);
|
| -};
|
| -
|
| -void EsParserH264Test::LoadStream(const char* filename) {
|
| - base::FilePath file_path = GetTestDataFilePath(filename);
|
| -
|
| - base::MemoryMappedFile stream_without_aud;
|
| - ASSERT_TRUE(stream_without_aud.Initialize(file_path))
|
| - << "Couldn't open stream file: " << file_path.MaybeAsASCII();
|
| -
|
| - // The input file does not have AUDs.
|
| - std::vector<Packet> access_units_without_aud = GetAccessUnits(
|
| - stream_without_aud.data(), stream_without_aud.length());
|
| - ASSERT_GT(access_units_without_aud.size(), 0u);
|
| - AppendAUD(stream_without_aud.data(), stream_without_aud.length(),
|
| - access_units_without_aud,
|
| - stream_, access_units_);
|
| -
|
| - // Generate some timestamps based on a 25fps stream.
|
| - for (size_t k = 0; k < access_units_.size(); k++)
|
| - access_units_[k].pts = base::TimeDelta::FromMilliseconds(k * 40u);
|
| + // Update the stream and access units used for the test.
|
| + stream_ = stream_with_aud;
|
| + access_units_ = access_units_with_aud;
|
| }
|
|
|
| -void EsParserH264Test::GetPesTimestamps(std::vector<Packet>& pes_packets) {
|
| +void EsParserH264Test::GetPesTimestamps(std::vector<Packet>* pes_packets_ptr) {
|
| + DCHECK(pes_packets_ptr);
|
| + const std::vector<Packet>& pes_packets = *pes_packets_ptr;
|
| +
|
| // Default: set to a negative timestamp to be able to differentiate from
|
| // real timestamps.
|
| // Note: we don't use kNoTimestamp() here since this one has already
|
| // a special meaning in EsParserH264. The negative timestamps should be
|
| // ultimately discarded by the H264 parser since not relevant.
|
| for (size_t k = 0; k < pes_packets.size(); k++) {
|
| - pes_packets[k].pts = base::TimeDelta::FromMilliseconds(-1);
|
| + (*pes_packets_ptr)[k].pts = base::TimeDelta::FromMilliseconds(-1);
|
| }
|
|
|
| // Set a valid timestamp for PES packets which include the start
|
| @@ -187,55 +152,51 @@ void EsParserH264Test::GetPesTimestamps(std::vector<Packet>& pes_packets) {
|
| size_t pes_end = pes_packets[pes_idx].offset + pes_packets[pes_idx].size;
|
| if (pes_start <= access_units_[k].offset &&
|
| pes_end > access_units_[k].offset) {
|
| - pes_packets[pes_idx].pts = access_units_[k].pts;
|
| + (*pes_packets_ptr)[pes_idx].pts = access_units_[k].pts;
|
| break;
|
| }
|
| }
|
| }
|
| }
|
|
|
| -void EsParserH264Test::ProcessPesPackets(
|
| +bool EsParserH264Test::Process(
|
| const std::vector<Packet>& pes_packets,
|
| bool force_timing) {
|
| EsParserH264 es_parser(
|
| base::Bind(&EsParserH264Test::NewVideoConfig, base::Unretained(this)),
|
| base::Bind(&EsParserH264Test::EmitBuffer, base::Unretained(this)));
|
| + return ProcessPesPackets(&es_parser, pes_packets, force_timing);
|
| +}
|
|
|
| - for (size_t k = 0; k < pes_packets.size(); k++) {
|
| - size_t cur_pes_offset = pes_packets[k].offset;
|
| - size_t cur_pes_size = pes_packets[k].size;
|
| -
|
| - base::TimeDelta pts = kNoTimestamp();
|
| - DecodeTimestamp dts = kNoDecodeTimestamp();
|
| - if (pes_packets[k].pts >= base::TimeDelta() || force_timing)
|
| - pts = pes_packets[k].pts;
|
| +void EsParserH264Test::CheckAccessUnits() {
|
| + EXPECT_EQ(buffer_count_, access_units_.size());
|
|
|
| - ASSERT_TRUE(
|
| - es_parser.Parse(&stream_[cur_pes_offset], cur_pes_size, pts, dts));
|
| + std::stringstream buffer_timestamps_stream;
|
| + for (size_t k = 0; k < access_units_.size(); k++) {
|
| + buffer_timestamps_stream << "("
|
| + << access_units_[k].pts.InMilliseconds()
|
| + << ") ";
|
| }
|
| - es_parser.Flush();
|
| -}
|
| -
|
| -void EsParserH264Test::EmitBuffer(scoped_refptr<StreamParserBuffer> buffer) {
|
| - ASSERT_LT(buffer_count_, access_units_.size());
|
| - EXPECT_EQ(buffer->timestamp(), access_units_[buffer_count_].pts);
|
| - buffer_count_++;
|
| + std::string buffer_timestamps = buffer_timestamps_stream.str();
|
| + base::TrimWhitespaceASCII(
|
| + buffer_timestamps, base::TRIM_ALL, &buffer_timestamps);
|
| + EXPECT_EQ(buffer_timestamps_, buffer_timestamps);
|
| }
|
|
|
| TEST_F(EsParserH264Test, OneAccessUnitPerPes) {
|
| - LoadStream("bear.h264");
|
| + LoadH264Stream("bear.h264");
|
|
|
| // One to one equivalence between PES packets and access units.
|
| std::vector<Packet> pes_packets(access_units_);
|
| - GetPesTimestamps(pes_packets);
|
| + GetPesTimestamps(&pes_packets);
|
|
|
| // Process each PES packet.
|
| - ProcessPesPackets(pes_packets, false);
|
| - EXPECT_EQ(buffer_count_, access_units_.size());
|
| + EXPECT_TRUE(Process(pes_packets, false));
|
| + CheckAccessUnits();
|
| }
|
|
|
| TEST_F(EsParserH264Test, NonAlignedPesPacket) {
|
| - LoadStream("bear.h264");
|
| + LoadH264Stream("bear.h264");
|
|
|
| // Generate the PES packets.
|
| std::vector<Packet> pes_packets;
|
| @@ -251,16 +212,16 @@ TEST_F(EsParserH264Test, NonAlignedPesPacket) {
|
| cur_pes_packet.offset = access_units_[k].offset +
|
| std::min<size_t>(487u, access_units_[k].size);
|
| }
|
| - ComputePacketSize(pes_packets, stream_.size());
|
| - GetPesTimestamps(pes_packets);
|
| + ComputePacketSize(&pes_packets);
|
| + GetPesTimestamps(&pes_packets);
|
|
|
| // Process each PES packet.
|
| - ProcessPesPackets(pes_packets, false);
|
| - EXPECT_EQ(buffer_count_, access_units_.size());
|
| + EXPECT_TRUE(Process(pes_packets, false));
|
| + CheckAccessUnits();
|
| }
|
|
|
| TEST_F(EsParserH264Test, SeveralPesPerAccessUnit) {
|
| - LoadStream("bear.h264");
|
| + LoadH264Stream("bear.h264");
|
|
|
| // Get the minimum size of an access unit.
|
| size_t min_access_unit_size = stream_.size();
|
| @@ -282,17 +243,16 @@ TEST_F(EsParserH264Test, SeveralPesPerAccessUnit) {
|
| pes_packets.push_back(cur_pes_packet);
|
| cur_pes_packet.offset += pes_size;
|
| }
|
| - ComputePacketSize(pes_packets, stream_.size());
|
| - GetPesTimestamps(pes_packets);
|
| + ComputePacketSize(&pes_packets);
|
| + GetPesTimestamps(&pes_packets);
|
|
|
| // Process each PES packet.
|
| - ProcessPesPackets(pes_packets, false);
|
| - EXPECT_EQ(buffer_count_, access_units_.size());
|
| + EXPECT_TRUE(Process(pes_packets, false));
|
| + CheckAccessUnits();
|
|
|
| // Process PES packets forcing timings for each PES packet.
|
| - buffer_count_ = 0;
|
| - ProcessPesPackets(pes_packets, true);
|
| - EXPECT_EQ(buffer_count_, access_units_.size());
|
| + EXPECT_TRUE(Process(pes_packets, true));
|
| + CheckAccessUnits();
|
| }
|
|
|
| } // namespace mp2t
|
|
|