summaryrefslogtreecommitdiffstats
path: root/chromium/media/formats/mp2t/es_parser_h264_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/media/formats/mp2t/es_parser_h264_unittest.cc')
-rw-r--r--chromium/media/formats/mp2t/es_parser_h264_unittest.cc300
1 files changed, 300 insertions, 0 deletions
diff --git a/chromium/media/formats/mp2t/es_parser_h264_unittest.cc b/chromium/media/formats/mp2t/es_parser_h264_unittest.cc
new file mode 100644
index 00000000000..2c13df0d853
--- /dev/null
+++ b/chromium/media/formats/mp2t/es_parser_h264_unittest.cc
@@ -0,0 +1,300 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+#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/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 "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+class VideoDecoderConfig;
+
+namespace mp2t {
+
+namespace {
+
+struct Packet {
+ // Offset in the stream.
+ size_t offset;
+
+ // Size of the packet.
+ size_t size;
+
+ // Timestamp of the packet.
+ base::TimeDelta pts;
+};
+
+// 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;
+}
+
+// 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;
+ bool start_access_unit = true;
+
+ // In a first pass, retrieve the offsets of all access units.
+ size_t offset = 0;
+ while (true) {
+ // Find the next start code.
+ off_t relative_offset = 0;
+ off_t start_code_size = 0;
+ bool success = H264Parser::FindStartCode(
+ &stream[offset], stream_size - offset,
+ &relative_offset, &start_code_size);
+ if (!success)
+ break;
+ offset += relative_offset;
+
+ if (start_access_unit) {
+ Packet cur_access_unit;
+ cur_access_unit.offset = offset;
+ access_units.push_back(cur_access_unit);
+ start_access_unit = false;
+ }
+
+ // Get the NALU type.
+ offset += start_code_size;
+ if (offset >= stream_size)
+ break;
+ int nal_unit_type = stream[offset] & 0x1f;
+
+ // We assume there is only one slice per access unit.
+ if (nal_unit_type == H264NALU::kIDRSlice ||
+ nal_unit_type == H264NALU::kNonIDRSlice) {
+ start_access_unit = true;
+ }
+ }
+
+ ComputePacketSize(access_units, stream_size);
+ return 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) {
+ 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());
+
+ size_t offset = 0;
+ 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);
+
+ 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) {
+ }
+ 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);
+}
+
+void EsParserH264Test::GetPesTimestamps(std::vector<Packet>& pes_packets) {
+ // 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);
+ }
+
+ // Set a valid timestamp for PES packets which include the start
+ // of an H264 access unit.
+ size_t pes_idx = 0;
+ for (size_t k = 0; k < access_units_.size(); k++) {
+ for (; pes_idx < pes_packets.size(); pes_idx++) {
+ size_t pes_start = pes_packets[pes_idx].offset;
+ 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;
+ break;
+ }
+ }
+ }
+}
+
+void EsParserH264Test::ProcessPesPackets(
+ 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)));
+
+ 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();
+ base::TimeDelta dts = kNoTimestamp();
+ if (pes_packets[k].pts >= base::TimeDelta() || force_timing)
+ pts = pes_packets[k].pts;
+
+ ASSERT_TRUE(
+ es_parser.Parse(&stream_[cur_pes_offset], cur_pes_size, pts, dts));
+ }
+ 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_++;
+}
+
+TEST_F(EsParserH264Test, OneAccessUnitPerPes) {
+ LoadStream("bear.h264");
+
+ // One to one equivalence between PES packets and access units.
+ std::vector<Packet> pes_packets(access_units_);
+ GetPesTimestamps(pes_packets);
+
+ // Process each PES packet.
+ ProcessPesPackets(pes_packets, false);
+ EXPECT_EQ(buffer_count_, access_units_.size());
+}
+
+TEST_F(EsParserH264Test, NonAlignedPesPacket) {
+ LoadStream("bear.h264");
+
+ // Generate the PES packets.
+ std::vector<Packet> pes_packets;
+ Packet cur_pes_packet;
+ cur_pes_packet.offset = 0;
+ for (size_t k = 0; k < access_units_.size(); k++) {
+ pes_packets.push_back(cur_pes_packet);
+
+ // The current PES packet includes the remaining bytes of the previous
+ // access unit and some bytes of the current access unit
+ // (487 bytes in this unit test but no more than the current access unit
+ // size).
+ 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);
+
+ // Process each PES packet.
+ ProcessPesPackets(pes_packets, false);
+ EXPECT_EQ(buffer_count_, access_units_.size());
+}
+
+TEST_F(EsParserH264Test, SeveralPesPerAccessUnit) {
+ LoadStream("bear.h264");
+
+ // Get the minimum size of an access unit.
+ size_t min_access_unit_size = stream_.size();
+ for (size_t k = 0; k < access_units_.size(); k++) {
+ if (min_access_unit_size >= access_units_[k].size)
+ min_access_unit_size = access_units_[k].size;
+ }
+
+ // Use a small PES packet size or the minimum access unit size
+ // if it is even smaller.
+ size_t pes_size = 512;
+ if (min_access_unit_size < pes_size)
+ pes_size = min_access_unit_size;
+
+ std::vector<Packet> pes_packets;
+ Packet cur_pes_packet;
+ cur_pes_packet.offset = 0;
+ while (cur_pes_packet.offset < stream_.size()) {
+ pes_packets.push_back(cur_pes_packet);
+ cur_pes_packet.offset += pes_size;
+ }
+ ComputePacketSize(pes_packets, stream_.size());
+ GetPesTimestamps(pes_packets);
+
+ // Process each PES packet.
+ ProcessPesPackets(pes_packets, false);
+ EXPECT_EQ(buffer_count_, access_units_.size());
+
+ // Process PES packets forcing timings for each PES packet.
+ buffer_count_ = 0;
+ ProcessPesPackets(pes_packets, true);
+ EXPECT_EQ(buffer_count_, access_units_.size());
+}
+
+} // namespace mp2t
+} // namespace media
+