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.
9 #include "base/command_line.h"
10 #include "base/files/memory_mapped_file.h"
11 #include "base/logging.h"
12 #include "base/path_service.h"
13 #include "base/time/time.h"
14 #include "media/base/stream_parser_buffer.h"
15 #include "media/base/test_data_util.h"
16 #include "media/filters/h264_parser.h"
17 #include "media/formats/mp2t/es_parser_h264.h"
18 #include "testing/gtest/include/gtest/gtest.h"
21 class VideoDecoderConfig
;
28 // Offset in the stream.
31 // Size of the packet.
34 // Timestamp of the packet.
38 // Compute the size of each packet assuming packets are given in stream order
39 // and the last packet covers the end of the stream.
40 void ComputePacketSize(std::vector
<Packet
>& packets
, size_t stream_size
) {
41 for (size_t k
= 0; k
< packets
.size() - 1; k
++) {
42 DCHECK_GE(packets
[k
+ 1].offset
, packets
[k
].offset
);
43 packets
[k
].size
= packets
[k
+ 1].offset
- packets
[k
].offset
;
45 packets
[packets
.size() - 1].size
=
46 stream_size
- packets
[packets
.size() - 1].offset
;
49 // Get the offset of the start of each access unit.
50 // This function assumes there is only one slice per access unit.
51 // This is a very simplified access unit segmenter that is good
52 // enough for unit tests.
53 std::vector
<Packet
> GetAccessUnits(const uint8
* stream
, size_t stream_size
) {
54 std::vector
<Packet
> access_units
;
55 bool start_access_unit
= true;
57 // In a first pass, retrieve the offsets of all access units.
60 // Find the next start code.
61 off_t relative_offset
= 0;
62 off_t start_code_size
= 0;
63 bool success
= H264Parser::FindStartCode(
64 &stream
[offset
], stream_size
- offset
,
65 &relative_offset
, &start_code_size
);
68 offset
+= relative_offset
;
70 if (start_access_unit
) {
71 Packet cur_access_unit
;
72 cur_access_unit
.offset
= offset
;
73 access_units
.push_back(cur_access_unit
);
74 start_access_unit
= false;
78 offset
+= start_code_size
;
79 if (offset
>= stream_size
)
81 int nal_unit_type
= stream
[offset
] & 0x1f;
83 // We assume there is only one slice per access unit.
84 if (nal_unit_type
== H264NALU::kIDRSlice
||
85 nal_unit_type
== H264NALU::kNonIDRSlice
) {
86 start_access_unit
= true;
90 ComputePacketSize(access_units
, stream_size
);
94 // Append an AUD NALU at the beginning of each access unit
95 // needed for streams which do not already have AUD NALUs.
97 const uint8
* stream
, size_t stream_size
,
98 const std::vector
<Packet
>& access_units
,
99 std::vector
<uint8
>& stream_with_aud
,
100 std::vector
<Packet
>& access_units_with_aud
) {
101 uint8 aud
[] = { 0x00, 0x00, 0x01, 0x09 };
102 stream_with_aud
.resize(stream_size
+ access_units
.size() * sizeof(aud
));
103 access_units_with_aud
.resize(access_units
.size());
106 for (size_t k
= 0; k
< access_units
.size(); k
++) {
107 access_units_with_aud
[k
].offset
= offset
;
108 access_units_with_aud
[k
].size
= access_units
[k
].size
+ sizeof(aud
);
110 memcpy(&stream_with_aud
[offset
], aud
, sizeof(aud
));
111 offset
+= sizeof(aud
);
113 memcpy(&stream_with_aud
[offset
],
114 &stream
[access_units
[k
].offset
], access_units
[k
].size
);
115 offset
+= access_units
[k
].size
;
121 class EsParserH264Test
: public testing::Test
{
123 EsParserH264Test() : buffer_count_(0) {
125 virtual ~EsParserH264Test() {}
128 void LoadStream(const char* filename
);
129 void GetPesTimestamps(std::vector
<Packet
>& pes_packets
);
130 void ProcessPesPackets(const std::vector
<Packet
>& pes_packets
,
133 // Stream with AUD NALUs.
134 std::vector
<uint8
> stream_
;
136 // Access units of the stream with AUD NALUs.
137 std::vector
<Packet
> access_units_
;
139 // Number of buffers generated while parsing the H264 stream.
140 size_t buffer_count_
;
143 void EmitBuffer(scoped_refptr
<StreamParserBuffer
> buffer
);
145 void NewVideoConfig(const VideoDecoderConfig
& config
) {
148 DISALLOW_COPY_AND_ASSIGN(EsParserH264Test
);
151 void EsParserH264Test::LoadStream(const char* filename
) {
152 base::FilePath file_path
= GetTestDataFilePath(filename
);
154 base::MemoryMappedFile stream_without_aud
;
155 ASSERT_TRUE(stream_without_aud
.Initialize(file_path
))
156 << "Couldn't open stream file: " << file_path
.MaybeAsASCII();
158 // The input file does not have AUDs.
159 std::vector
<Packet
> access_units_without_aud
= GetAccessUnits(
160 stream_without_aud
.data(), stream_without_aud
.length());
161 ASSERT_GT(access_units_without_aud
.size(), 0u);
162 AppendAUD(stream_without_aud
.data(), stream_without_aud
.length(),
163 access_units_without_aud
,
164 stream_
, access_units_
);
166 // Generate some timestamps based on a 25fps stream.
167 for (size_t k
= 0; k
< access_units_
.size(); k
++)
168 access_units_
[k
].pts
= base::TimeDelta::FromMilliseconds(k
* 40u);
171 void EsParserH264Test::GetPesTimestamps(std::vector
<Packet
>& pes_packets
) {
172 // Default: set to a negative timestamp to be able to differentiate from
174 // Note: we don't use kNoTimestamp() here since this one has already
175 // a special meaning in EsParserH264. The negative timestamps should be
176 // ultimately discarded by the H264 parser since not relevant.
177 for (size_t k
= 0; k
< pes_packets
.size(); k
++) {
178 pes_packets
[k
].pts
= base::TimeDelta::FromMilliseconds(-1);
181 // Set a valid timestamp for PES packets which include the start
182 // of an H264 access unit.
184 for (size_t k
= 0; k
< access_units_
.size(); k
++) {
185 for (; pes_idx
< pes_packets
.size(); pes_idx
++) {
186 size_t pes_start
= pes_packets
[pes_idx
].offset
;
187 size_t pes_end
= pes_packets
[pes_idx
].offset
+ pes_packets
[pes_idx
].size
;
188 if (pes_start
<= access_units_
[k
].offset
&&
189 pes_end
> access_units_
[k
].offset
) {
190 pes_packets
[pes_idx
].pts
= access_units_
[k
].pts
;
197 void EsParserH264Test::ProcessPesPackets(
198 const std::vector
<Packet
>& pes_packets
,
200 EsParserH264
es_parser(
201 base::Bind(&EsParserH264Test::NewVideoConfig
, base::Unretained(this)),
202 base::Bind(&EsParserH264Test::EmitBuffer
, base::Unretained(this)));
204 for (size_t k
= 0; k
< pes_packets
.size(); k
++) {
205 size_t cur_pes_offset
= pes_packets
[k
].offset
;
206 size_t cur_pes_size
= pes_packets
[k
].size
;
208 base::TimeDelta pts
= kNoTimestamp();
209 base::TimeDelta dts
= kNoTimestamp();
210 if (pes_packets
[k
].pts
>= base::TimeDelta() || force_timing
)
211 pts
= pes_packets
[k
].pts
;
214 es_parser
.Parse(&stream_
[cur_pes_offset
], cur_pes_size
, pts
, dts
));
219 void EsParserH264Test::EmitBuffer(scoped_refptr
<StreamParserBuffer
> buffer
) {
220 ASSERT_LT(buffer_count_
, access_units_
.size());
221 EXPECT_EQ(buffer
->timestamp(), access_units_
[buffer_count_
].pts
);
225 TEST_F(EsParserH264Test
, OneAccessUnitPerPes
) {
226 LoadStream("bear.h264");
228 // One to one equivalence between PES packets and access units.
229 std::vector
<Packet
> pes_packets(access_units_
);
230 GetPesTimestamps(pes_packets
);
232 // Process each PES packet.
233 ProcessPesPackets(pes_packets
, false);
234 EXPECT_EQ(buffer_count_
, access_units_
.size());
237 TEST_F(EsParserH264Test
, NonAlignedPesPacket
) {
238 LoadStream("bear.h264");
240 // Generate the PES packets.
241 std::vector
<Packet
> pes_packets
;
242 Packet cur_pes_packet
;
243 cur_pes_packet
.offset
= 0;
244 for (size_t k
= 0; k
< access_units_
.size(); k
++) {
245 pes_packets
.push_back(cur_pes_packet
);
247 // The current PES packet includes the remaining bytes of the previous
248 // access unit and some bytes of the current access unit
249 // (487 bytes in this unit test but no more than the current access unit
251 cur_pes_packet
.offset
= access_units_
[k
].offset
+
252 std::min
<size_t>(487u, access_units_
[k
].size
);
254 ComputePacketSize(pes_packets
, stream_
.size());
255 GetPesTimestamps(pes_packets
);
257 // Process each PES packet.
258 ProcessPesPackets(pes_packets
, false);
259 EXPECT_EQ(buffer_count_
, access_units_
.size());
262 TEST_F(EsParserH264Test
, SeveralPesPerAccessUnit
) {
263 LoadStream("bear.h264");
265 // Get the minimum size of an access unit.
266 size_t min_access_unit_size
= stream_
.size();
267 for (size_t k
= 0; k
< access_units_
.size(); k
++) {
268 if (min_access_unit_size
>= access_units_
[k
].size
)
269 min_access_unit_size
= access_units_
[k
].size
;
272 // Use a small PES packet size or the minimum access unit size
273 // if it is even smaller.
274 size_t pes_size
= 512;
275 if (min_access_unit_size
< pes_size
)
276 pes_size
= min_access_unit_size
;
278 std::vector
<Packet
> pes_packets
;
279 Packet cur_pes_packet
;
280 cur_pes_packet
.offset
= 0;
281 while (cur_pes_packet
.offset
< stream_
.size()) {
282 pes_packets
.push_back(cur_pes_packet
);
283 cur_pes_packet
.offset
+= pes_size
;
285 ComputePacketSize(pes_packets
, stream_
.size());
286 GetPesTimestamps(pes_packets
);
288 // Process each PES packet.
289 ProcessPesPackets(pes_packets
, false);
290 EXPECT_EQ(buffer_count_
, access_units_
.size());
292 // Process PES packets forcing timings for each PES packet.
294 ProcessPesPackets(pes_packets
, true);
295 EXPECT_EQ(buffer_count_
, access_units_
.size());