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
{
30 void LoadH264Stream(const char* filename
);
31 void GetPesTimestamps(std::vector
<Packet
>* pes_packets
);
32 bool Process(const std::vector
<Packet
>& pes_packets
, bool force_timing
);
33 void CheckAccessUnits();
35 // Access units of the stream with AUD NALUs.
36 std::vector
<Packet
> access_units_
;
39 // Get the offset of the start of each access unit of |stream_|.
40 // This function assumes there is only one slice per access unit.
41 // This is a very simplified access unit segmenter that is good
42 // enough for unit tests.
43 void GetAccessUnits();
45 // Insert an AUD before each access unit.
46 // Update |stream_| and |access_units_| accordingly.
49 DISALLOW_COPY_AND_ASSIGN(EsParserH264Test
);
52 void EsParserH264Test::LoadH264Stream(const char* filename
) {
53 // Load the input H264 file and segment it into access units.
56 ASSERT_GT(access_units_
.size(), 0u);
58 // Insert AUDs into the stream.
61 // Generate some timestamps based on a 25fps stream.
62 for (size_t k
= 0; k
< access_units_
.size(); k
++)
63 access_units_
[k
].pts
= base::TimeDelta::FromMilliseconds(k
* 40u);
66 void EsParserH264Test::GetAccessUnits() {
67 access_units_
.resize(0);
68 bool start_access_unit
= true;
70 // In a first pass, retrieve the offsets of all access units.
73 // Find the next start code.
74 off_t relative_offset
= 0;
75 off_t start_code_size
= 0;
76 bool success
= H264Parser::FindStartCode(
77 &stream_
[offset
], stream_
.size() - offset
,
78 &relative_offset
, &start_code_size
);
81 offset
+= relative_offset
;
83 if (start_access_unit
) {
84 Packet cur_access_unit
;
85 cur_access_unit
.offset
= offset
;
86 access_units_
.push_back(cur_access_unit
);
87 start_access_unit
= false;
91 offset
+= start_code_size
;
92 if (offset
>= stream_
.size())
94 int nal_unit_type
= stream_
[offset
] & 0x1f;
96 // We assume there is only one slice per access unit.
97 if (nal_unit_type
== H264NALU::kIDRSlice
||
98 nal_unit_type
== H264NALU::kNonIDRSlice
) {
99 start_access_unit
= true;
103 ComputePacketSize(&access_units_
);
106 void EsParserH264Test::InsertAUD() {
107 uint8 aud
[] = { 0x00, 0x00, 0x01, 0x09 };
109 std::vector
<uint8
> stream_with_aud(
110 stream_
.size() + access_units_
.size() * sizeof(aud
));
111 std::vector
<EsParserTestBase::Packet
> access_units_with_aud(
112 access_units_
.size());
115 for (size_t k
= 0; k
< access_units_
.size(); k
++) {
116 access_units_with_aud
[k
].offset
= offset
;
117 access_units_with_aud
[k
].size
= access_units_
[k
].size
+ sizeof(aud
);
119 memcpy(&stream_with_aud
[offset
], aud
, sizeof(aud
));
120 offset
+= sizeof(aud
);
122 memcpy(&stream_with_aud
[offset
],
123 &stream_
[access_units_
[k
].offset
], access_units_
[k
].size
);
124 offset
+= access_units_
[k
].size
;
127 // Update the stream and access units used for the test.
128 stream_
= stream_with_aud
;
129 access_units_
= access_units_with_aud
;
132 void EsParserH264Test::GetPesTimestamps(std::vector
<Packet
>* pes_packets_ptr
) {
133 DCHECK(pes_packets_ptr
);
134 const std::vector
<Packet
>& pes_packets
= *pes_packets_ptr
;
136 // Default: set to a negative timestamp to be able to differentiate from
138 // Note: we don't use kNoTimestamp() here since this one has already
139 // a special meaning in EsParserH264. The negative timestamps should be
140 // ultimately discarded by the H264 parser since not relevant.
141 for (size_t k
= 0; k
< pes_packets
.size(); k
++) {
142 (*pes_packets_ptr
)[k
].pts
= base::TimeDelta::FromMilliseconds(-1);
145 // Set a valid timestamp for PES packets which include the start
146 // of an H264 access unit.
148 for (size_t k
= 0; k
< access_units_
.size(); k
++) {
149 for (; pes_idx
< pes_packets
.size(); pes_idx
++) {
150 size_t pes_start
= pes_packets
[pes_idx
].offset
;
151 size_t pes_end
= pes_packets
[pes_idx
].offset
+ pes_packets
[pes_idx
].size
;
152 if (pes_start
<= access_units_
[k
].offset
&&
153 pes_end
> access_units_
[k
].offset
) {
154 (*pes_packets_ptr
)[pes_idx
].pts
= access_units_
[k
].pts
;
161 bool EsParserH264Test::Process(
162 const std::vector
<Packet
>& pes_packets
,
164 EsParserH264
es_parser(
165 base::Bind(&EsParserH264Test::NewVideoConfig
, base::Unretained(this)),
166 base::Bind(&EsParserH264Test::EmitBuffer
, base::Unretained(this)));
167 return ProcessPesPackets(&es_parser
, pes_packets
, force_timing
);
170 void EsParserH264Test::CheckAccessUnits() {
171 EXPECT_EQ(buffer_count_
, access_units_
.size());
173 std::stringstream buffer_timestamps_stream
;
174 for (size_t k
= 0; k
< access_units_
.size(); k
++) {
175 buffer_timestamps_stream
<< "("
176 << access_units_
[k
].pts
.InMilliseconds()
179 std::string buffer_timestamps
= buffer_timestamps_stream
.str();
180 base::TrimWhitespaceASCII(
181 buffer_timestamps
, base::TRIM_ALL
, &buffer_timestamps
);
182 EXPECT_EQ(buffer_timestamps_
, buffer_timestamps
);
185 TEST_F(EsParserH264Test
, OneAccessUnitPerPes
) {
186 LoadH264Stream("bear.h264");
188 // One to one equivalence between PES packets and access units.
189 std::vector
<Packet
> pes_packets(access_units_
);
190 GetPesTimestamps(&pes_packets
);
192 // Process each PES packet.
193 EXPECT_TRUE(Process(pes_packets
, false));
197 TEST_F(EsParserH264Test
, NonAlignedPesPacket
) {
198 LoadH264Stream("bear.h264");
200 // Generate the PES packets.
201 std::vector
<Packet
> pes_packets
;
202 Packet cur_pes_packet
;
203 cur_pes_packet
.offset
= 0;
204 for (size_t k
= 0; k
< access_units_
.size(); k
++) {
205 pes_packets
.push_back(cur_pes_packet
);
207 // The current PES packet includes the remaining bytes of the previous
208 // access unit and some bytes of the current access unit
209 // (487 bytes in this unit test but no more than the current access unit
211 cur_pes_packet
.offset
= access_units_
[k
].offset
+
212 std::min
<size_t>(487u, access_units_
[k
].size
);
214 ComputePacketSize(&pes_packets
);
215 GetPesTimestamps(&pes_packets
);
217 // Process each PES packet.
218 EXPECT_TRUE(Process(pes_packets
, false));
222 TEST_F(EsParserH264Test
, SeveralPesPerAccessUnit
) {
223 LoadH264Stream("bear.h264");
225 // Get the minimum size of an access unit.
226 size_t min_access_unit_size
= stream_
.size();
227 for (size_t k
= 0; k
< access_units_
.size(); k
++) {
228 if (min_access_unit_size
>= access_units_
[k
].size
)
229 min_access_unit_size
= access_units_
[k
].size
;
232 // Use a small PES packet size or the minimum access unit size
233 // if it is even smaller.
234 size_t pes_size
= 512;
235 if (min_access_unit_size
< pes_size
)
236 pes_size
= min_access_unit_size
;
238 std::vector
<Packet
> pes_packets
;
239 Packet cur_pes_packet
;
240 cur_pes_packet
.offset
= 0;
241 while (cur_pes_packet
.offset
< stream_
.size()) {
242 pes_packets
.push_back(cur_pes_packet
);
243 cur_pes_packet
.offset
+= pes_size
;
245 ComputePacketSize(&pes_packets
);
246 GetPesTimestamps(&pes_packets
);
248 // Process each PES packet.
249 EXPECT_TRUE(Process(pes_packets
, false));
252 // Process PES packets forcing timings for each PES packet.
253 EXPECT_TRUE(Process(pes_packets
, true));