1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
10 #include "base/logging.h"
11 #include "base/strings/string_util.h"
12 #include "base/time/time.h"
13 #include "media/base/stream_parser_buffer.h"
14 #include "media/filters/h264_parser.h"
15 #include "media/formats/mp2t/es_parser_h264.h"
16 #include "media/formats/mp2t/es_parser_test_base.h"
17 #include "testing/gtest/include/gtest/gtest.h"
20 class VideoDecoderConfig
;
24 class EsParserH264Test
: public EsParserTestBase
,
25 public testing::Test
{
28 virtual ~EsParserH264Test() {}
31 void LoadH264Stream(const char* filename
);
32 void GetPesTimestamps(std::vector
<Packet
>* pes_packets
);
33 bool Process(const std::vector
<Packet
>& pes_packets
, bool force_timing
);
34 void CheckAccessUnits();
36 // Access units of the stream with AUD NALUs.
37 std::vector
<Packet
> access_units_
;
40 // Get the offset of the start of each access unit of |stream_|.
41 // This function assumes there is only one slice per access unit.
42 // This is a very simplified access unit segmenter that is good
43 // enough for unit tests.
44 void GetAccessUnits();
46 // Insert an AUD before each access unit.
47 // Update |stream_| and |access_units_| accordingly.
50 DISALLOW_COPY_AND_ASSIGN(EsParserH264Test
);
53 void EsParserH264Test::LoadH264Stream(const char* filename
) {
54 // Load the input H264 file and segment it into access units.
57 ASSERT_GT(access_units_
.size(), 0u);
59 // Insert AUDs into the stream.
62 // Generate some timestamps based on a 25fps stream.
63 for (size_t k
= 0; k
< access_units_
.size(); k
++)
64 access_units_
[k
].pts
= base::TimeDelta::FromMilliseconds(k
* 40u);
67 void EsParserH264Test::GetAccessUnits() {
68 access_units_
.resize(0);
69 bool start_access_unit
= true;
71 // In a first pass, retrieve the offsets of all access units.
74 // Find the next start code.
75 off_t relative_offset
= 0;
76 off_t start_code_size
= 0;
77 bool success
= H264Parser::FindStartCode(
78 &stream_
[offset
], stream_
.size() - offset
,
79 &relative_offset
, &start_code_size
);
82 offset
+= relative_offset
;
84 if (start_access_unit
) {
85 Packet cur_access_unit
;
86 cur_access_unit
.offset
= offset
;
87 access_units_
.push_back(cur_access_unit
);
88 start_access_unit
= false;
92 offset
+= start_code_size
;
93 if (offset
>= stream_
.size())
95 int nal_unit_type
= stream_
[offset
] & 0x1f;
97 // We assume there is only one slice per access unit.
98 if (nal_unit_type
== H264NALU::kIDRSlice
||
99 nal_unit_type
== H264NALU::kNonIDRSlice
) {
100 start_access_unit
= true;
104 ComputePacketSize(&access_units_
);
107 void EsParserH264Test::InsertAUD() {
108 uint8 aud
[] = { 0x00, 0x00, 0x01, 0x09 };
110 std::vector
<uint8
> stream_with_aud(
111 stream_
.size() + access_units_
.size() * sizeof(aud
));
112 std::vector
<EsParserTestBase::Packet
> access_units_with_aud(
113 access_units_
.size());
116 for (size_t k
= 0; k
< access_units_
.size(); k
++) {
117 access_units_with_aud
[k
].offset
= offset
;
118 access_units_with_aud
[k
].size
= access_units_
[k
].size
+ sizeof(aud
);
120 memcpy(&stream_with_aud
[offset
], aud
, sizeof(aud
));
121 offset
+= sizeof(aud
);
123 memcpy(&stream_with_aud
[offset
],
124 &stream_
[access_units_
[k
].offset
], access_units_
[k
].size
);
125 offset
+= access_units_
[k
].size
;
128 // Update the stream and access units used for the test.
129 stream_
= stream_with_aud
;
130 access_units_
= access_units_with_aud
;
133 void EsParserH264Test::GetPesTimestamps(std::vector
<Packet
>* pes_packets_ptr
) {
134 DCHECK(pes_packets_ptr
);
135 const std::vector
<Packet
>& pes_packets
= *pes_packets_ptr
;
137 // Default: set to a negative timestamp to be able to differentiate from
139 // Note: we don't use kNoTimestamp() here since this one has already
140 // a special meaning in EsParserH264. The negative timestamps should be
141 // ultimately discarded by the H264 parser since not relevant.
142 for (size_t k
= 0; k
< pes_packets
.size(); k
++) {
143 (*pes_packets_ptr
)[k
].pts
= base::TimeDelta::FromMilliseconds(-1);
146 // Set a valid timestamp for PES packets which include the start
147 // of an H264 access unit.
149 for (size_t k
= 0; k
< access_units_
.size(); k
++) {
150 for (; pes_idx
< pes_packets
.size(); pes_idx
++) {
151 size_t pes_start
= pes_packets
[pes_idx
].offset
;
152 size_t pes_end
= pes_packets
[pes_idx
].offset
+ pes_packets
[pes_idx
].size
;
153 if (pes_start
<= access_units_
[k
].offset
&&
154 pes_end
> access_units_
[k
].offset
) {
155 (*pes_packets_ptr
)[pes_idx
].pts
= access_units_
[k
].pts
;
162 bool EsParserH264Test::Process(
163 const std::vector
<Packet
>& pes_packets
,
165 EsParserH264
es_parser(
166 base::Bind(&EsParserH264Test::NewVideoConfig
, base::Unretained(this)),
167 base::Bind(&EsParserH264Test::EmitBuffer
, base::Unretained(this)));
168 return ProcessPesPackets(&es_parser
, pes_packets
, force_timing
);
171 void EsParserH264Test::CheckAccessUnits() {
172 EXPECT_EQ(buffer_count_
, access_units_
.size());
174 std::stringstream buffer_timestamps_stream
;
175 for (size_t k
= 0; k
< access_units_
.size(); k
++) {
176 buffer_timestamps_stream
<< "("
177 << access_units_
[k
].pts
.InMilliseconds()
180 std::string buffer_timestamps
= buffer_timestamps_stream
.str();
181 base::TrimWhitespaceASCII(
182 buffer_timestamps
, base::TRIM_ALL
, &buffer_timestamps
);
183 EXPECT_EQ(buffer_timestamps_
, buffer_timestamps
);
186 TEST_F(EsParserH264Test
, OneAccessUnitPerPes
) {
187 LoadH264Stream("bear.h264");
189 // One to one equivalence between PES packets and access units.
190 std::vector
<Packet
> pes_packets(access_units_
);
191 GetPesTimestamps(&pes_packets
);
193 // Process each PES packet.
194 EXPECT_TRUE(Process(pes_packets
, false));
198 TEST_F(EsParserH264Test
, NonAlignedPesPacket
) {
199 LoadH264Stream("bear.h264");
201 // Generate the PES packets.
202 std::vector
<Packet
> pes_packets
;
203 Packet cur_pes_packet
;
204 cur_pes_packet
.offset
= 0;
205 for (size_t k
= 0; k
< access_units_
.size(); k
++) {
206 pes_packets
.push_back(cur_pes_packet
);
208 // The current PES packet includes the remaining bytes of the previous
209 // access unit and some bytes of the current access unit
210 // (487 bytes in this unit test but no more than the current access unit
212 cur_pes_packet
.offset
= access_units_
[k
].offset
+
213 std::min
<size_t>(487u, access_units_
[k
].size
);
215 ComputePacketSize(&pes_packets
);
216 GetPesTimestamps(&pes_packets
);
218 // Process each PES packet.
219 EXPECT_TRUE(Process(pes_packets
, false));
223 TEST_F(EsParserH264Test
, SeveralPesPerAccessUnit
) {
224 LoadH264Stream("bear.h264");
226 // Get the minimum size of an access unit.
227 size_t min_access_unit_size
= stream_
.size();
228 for (size_t k
= 0; k
< access_units_
.size(); k
++) {
229 if (min_access_unit_size
>= access_units_
[k
].size
)
230 min_access_unit_size
= access_units_
[k
].size
;
233 // Use a small PES packet size or the minimum access unit size
234 // if it is even smaller.
235 size_t pes_size
= 512;
236 if (min_access_unit_size
< pes_size
)
237 pes_size
= min_access_unit_size
;
239 std::vector
<Packet
> pes_packets
;
240 Packet cur_pes_packet
;
241 cur_pes_packet
.offset
= 0;
242 while (cur_pes_packet
.offset
< stream_
.size()) {
243 pes_packets
.push_back(cur_pes_packet
);
244 cur_pes_packet
.offset
+= pes_size
;
246 ComputePacketSize(&pes_packets
);
247 GetPesTimestamps(&pes_packets
);
249 // Process each PES packet.
250 EXPECT_TRUE(Process(pes_packets
, false));
253 // Process PES packets forcing timings for each PES packet.
254 EXPECT_TRUE(Process(pes_packets
, true));