Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(818)

Side by Side Diff: media/formats/mp2t/es_parser_h264_unittest.cc

Issue 399433003: Mpeg2 TS - Fail when no valid timestamp in the ADTS parser. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix comments from patch set #12. Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « media/formats/mp2t/es_parser_adts_unittest.cc ('k') | media/formats/mp2t/es_parser_test_base.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <algorithm> 5 #include <sstream>
6 #include <string>
6 #include <vector> 7 #include <vector>
7 8
8 #include "base/bind.h" 9 #include "base/bind.h"
9 #include "base/command_line.h"
10 #include "base/files/memory_mapped_file.h"
11 #include "base/logging.h" 10 #include "base/logging.h"
12 #include "base/path_service.h" 11 #include "base/strings/string_util.h"
13 #include "base/time/time.h" 12 #include "base/time/time.h"
14 #include "media/base/stream_parser_buffer.h" 13 #include "media/base/stream_parser_buffer.h"
15 #include "media/base/test_data_util.h"
16 #include "media/filters/h264_parser.h" 14 #include "media/filters/h264_parser.h"
17 #include "media/formats/mp2t/es_parser_h264.h" 15 #include "media/formats/mp2t/es_parser_h264.h"
16 #include "media/formats/mp2t/es_parser_test_base.h"
18 #include "testing/gtest/include/gtest/gtest.h" 17 #include "testing/gtest/include/gtest/gtest.h"
19 18
20 namespace media { 19 namespace media {
21 class VideoDecoderConfig; 20 class VideoDecoderConfig;
22 21
23 namespace mp2t { 22 namespace mp2t {
24 23
25 namespace { 24 class EsParserH264Test : public EsParserTestBase,
25 public testing::Test {
26 public:
27 EsParserH264Test() {}
28 virtual ~EsParserH264Test() {}
26 29
27 struct Packet { 30 protected:
28 // Offset in the stream. 31 void LoadH264Stream(const char* filename);
29 size_t offset; 32 void GetPesTimestamps(std::vector<Packet>* pes_packets);
33 bool Process(const std::vector<Packet>& pes_packets, bool force_timing);
34 void CheckAccessUnits();
30 35
31 // Size of the packet. 36 // Access units of the stream with AUD NALUs.
32 size_t size; 37 std::vector<Packet> access_units_;
33 38
34 // Timestamp of the packet. 39 private:
35 base::TimeDelta pts; 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();
45
46 // Insert an AUD before each access unit.
47 // Update |stream_| and |access_units_| accordingly.
48 void InsertAUD();
49
50 DISALLOW_COPY_AND_ASSIGN(EsParserH264Test);
36 }; 51 };
37 52
38 // Compute the size of each packet assuming packets are given in stream order 53 void EsParserH264Test::LoadH264Stream(const char* filename) {
39 // and the last packet covers the end of the stream. 54 // Load the input H264 file and segment it into access units.
40 void ComputePacketSize(std::vector<Packet>& packets, size_t stream_size) { 55 LoadStream(filename);
41 for (size_t k = 0; k < packets.size() - 1; k++) { 56 GetAccessUnits();
42 DCHECK_GE(packets[k + 1].offset, packets[k].offset); 57 ASSERT_GT(access_units_.size(), 0u);
43 packets[k].size = packets[k + 1].offset - packets[k].offset; 58
44 } 59 // Insert AUDs into the stream.
45 packets[packets.size() - 1].size = 60 InsertAUD();
46 stream_size - packets[packets.size() - 1].offset; 61
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);
47 } 65 }
48 66
49 // Get the offset of the start of each access unit. 67 void EsParserH264Test::GetAccessUnits() {
50 // This function assumes there is only one slice per access unit. 68 access_units_.resize(0);
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; 69 bool start_access_unit = true;
56 70
57 // In a first pass, retrieve the offsets of all access units. 71 // In a first pass, retrieve the offsets of all access units.
58 size_t offset = 0; 72 size_t offset = 0;
59 while (true) { 73 while (true) {
60 // Find the next start code. 74 // Find the next start code.
61 off_t relative_offset = 0; 75 off_t relative_offset = 0;
62 off_t start_code_size = 0; 76 off_t start_code_size = 0;
63 bool success = H264Parser::FindStartCode( 77 bool success = H264Parser::FindStartCode(
64 &stream[offset], stream_size - offset, 78 &stream_[offset], stream_.size() - offset,
65 &relative_offset, &start_code_size); 79 &relative_offset, &start_code_size);
66 if (!success) 80 if (!success)
67 break; 81 break;
68 offset += relative_offset; 82 offset += relative_offset;
69 83
70 if (start_access_unit) { 84 if (start_access_unit) {
71 Packet cur_access_unit; 85 Packet cur_access_unit;
72 cur_access_unit.offset = offset; 86 cur_access_unit.offset = offset;
73 access_units.push_back(cur_access_unit); 87 access_units_.push_back(cur_access_unit);
74 start_access_unit = false; 88 start_access_unit = false;
75 } 89 }
76 90
77 // Get the NALU type. 91 // Get the NALU type.
78 offset += start_code_size; 92 offset += start_code_size;
79 if (offset >= stream_size) 93 if (offset >= stream_.size())
80 break; 94 break;
81 int nal_unit_type = stream[offset] & 0x1f; 95 int nal_unit_type = stream_[offset] & 0x1f;
82 96
83 // We assume there is only one slice per access unit. 97 // We assume there is only one slice per access unit.
84 if (nal_unit_type == H264NALU::kIDRSlice || 98 if (nal_unit_type == H264NALU::kIDRSlice ||
85 nal_unit_type == H264NALU::kNonIDRSlice) { 99 nal_unit_type == H264NALU::kNonIDRSlice) {
86 start_access_unit = true; 100 start_access_unit = true;
87 } 101 }
88 } 102 }
89 103
90 ComputePacketSize(access_units, stream_size); 104 ComputePacketSize(&access_units_);
91 return access_units;
92 } 105 }
93 106
94 // Append an AUD NALU at the beginning of each access unit 107 void EsParserH264Test::InsertAUD() {
95 // needed for streams which do not already have AUD NALUs.
96 void AppendAUD(
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 }; 108 uint8 aud[] = { 0x00, 0x00, 0x01, 0x09 };
102 stream_with_aud.resize(stream_size + access_units.size() * sizeof(aud)); 109
103 access_units_with_aud.resize(access_units.size()); 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());
104 114
105 size_t offset = 0; 115 size_t offset = 0;
106 for (size_t k = 0; k < access_units.size(); k++) { 116 for (size_t k = 0; k < access_units_.size(); k++) {
107 access_units_with_aud[k].offset = offset; 117 access_units_with_aud[k].offset = offset;
108 access_units_with_aud[k].size = access_units[k].size + sizeof(aud); 118 access_units_with_aud[k].size = access_units_[k].size + sizeof(aud);
109 119
110 memcpy(&stream_with_aud[offset], aud, sizeof(aud)); 120 memcpy(&stream_with_aud[offset], aud, sizeof(aud));
111 offset += sizeof(aud); 121 offset += sizeof(aud);
112 122
113 memcpy(&stream_with_aud[offset], 123 memcpy(&stream_with_aud[offset],
114 &stream[access_units[k].offset], access_units[k].size); 124 &stream_[access_units_[k].offset], access_units_[k].size);
115 offset += access_units[k].size; 125 offset += access_units_[k].size;
116 } 126 }
127
128 // Update the stream and access units used for the test.
129 stream_ = stream_with_aud;
130 access_units_ = access_units_with_aud;
117 } 131 }
118 132
119 } // namespace 133 void EsParserH264Test::GetPesTimestamps(std::vector<Packet>* pes_packets_ptr) {
134 const std::vector<Packet>& pes_packets = *pes_packets_ptr;
wolenetz 2014/08/14 23:13:53 nit: first, guard against NULL, like DCHECK(pes_pa
damienv1 2014/08/14 23:30:02 Done.
120 135
121 class EsParserH264Test : public testing::Test {
122 public:
123 EsParserH264Test() : buffer_count_(0) {
124 }
125 virtual ~EsParserH264Test() {}
126
127 protected:
128 void LoadStream(const char* filename);
129 void GetPesTimestamps(std::vector<Packet>& pes_packets);
130 void ProcessPesPackets(const std::vector<Packet>& pes_packets,
131 bool force_timing);
132
133 // Stream with AUD NALUs.
134 std::vector<uint8> stream_;
135
136 // Access units of the stream with AUD NALUs.
137 std::vector<Packet> access_units_;
138
139 // Number of buffers generated while parsing the H264 stream.
140 size_t buffer_count_;
141
142 private:
143 void EmitBuffer(scoped_refptr<StreamParserBuffer> buffer);
144
145 void NewVideoConfig(const VideoDecoderConfig& config) {
146 }
147
148 DISALLOW_COPY_AND_ASSIGN(EsParserH264Test);
149 };
150
151 void EsParserH264Test::LoadStream(const char* filename) {
152 base::FilePath file_path = GetTestDataFilePath(filename);
153
154 base::MemoryMappedFile stream_without_aud;
155 ASSERT_TRUE(stream_without_aud.Initialize(file_path))
156 << "Couldn't open stream file: " << file_path.MaybeAsASCII();
157
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_);
165
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);
169 }
170
171 void EsParserH264Test::GetPesTimestamps(std::vector<Packet>& pes_packets) {
172 // Default: set to a negative timestamp to be able to differentiate from 136 // Default: set to a negative timestamp to be able to differentiate from
173 // real timestamps. 137 // real timestamps.
174 // Note: we don't use kNoTimestamp() here since this one has already 138 // Note: we don't use kNoTimestamp() here since this one has already
175 // a special meaning in EsParserH264. The negative timestamps should be 139 // a special meaning in EsParserH264. The negative timestamps should be
176 // ultimately discarded by the H264 parser since not relevant. 140 // ultimately discarded by the H264 parser since not relevant.
177 for (size_t k = 0; k < pes_packets.size(); k++) { 141 for (size_t k = 0; k < pes_packets.size(); k++) {
178 pes_packets[k].pts = base::TimeDelta::FromMilliseconds(-1); 142 (*pes_packets_ptr)[k].pts = base::TimeDelta::FromMilliseconds(-1);
wolenetz 2014/08/14 23:13:53 nit: why not s/(*pes_packets_ptr)/pes_packets/ ?
damienv1 2014/08/14 23:30:03 Does not work.
wolenetz 2014/08/14 23:39:54 Acknowledged. (I'm sorry!)
179 } 143 }
180 144
181 // Set a valid timestamp for PES packets which include the start 145 // Set a valid timestamp for PES packets which include the start
182 // of an H264 access unit. 146 // of an H264 access unit.
183 size_t pes_idx = 0; 147 size_t pes_idx = 0;
184 for (size_t k = 0; k < access_units_.size(); k++) { 148 for (size_t k = 0; k < access_units_.size(); k++) {
185 for (; pes_idx < pes_packets.size(); pes_idx++) { 149 for (; pes_idx < pes_packets.size(); pes_idx++) {
186 size_t pes_start = pes_packets[pes_idx].offset; 150 size_t pes_start = pes_packets[pes_idx].offset;
187 size_t pes_end = pes_packets[pes_idx].offset + pes_packets[pes_idx].size; 151 size_t pes_end = pes_packets[pes_idx].offset + pes_packets[pes_idx].size;
188 if (pes_start <= access_units_[k].offset && 152 if (pes_start <= access_units_[k].offset &&
189 pes_end > access_units_[k].offset) { 153 pes_end > access_units_[k].offset) {
190 pes_packets[pes_idx].pts = access_units_[k].pts; 154 (*pes_packets_ptr)[pes_idx].pts = access_units_[k].pts;
wolenetz 2014/08/14 23:13:53 nit: ditto
damienv1 2014/08/14 23:30:02 Same.
191 break; 155 break;
192 } 156 }
193 } 157 }
194 } 158 }
195 } 159 }
196 160
197 void EsParserH264Test::ProcessPesPackets( 161 bool EsParserH264Test::Process(
198 const std::vector<Packet>& pes_packets, 162 const std::vector<Packet>& pes_packets,
199 bool force_timing) { 163 bool force_timing) {
200 EsParserH264 es_parser( 164 EsParserH264 es_parser(
201 base::Bind(&EsParserH264Test::NewVideoConfig, base::Unretained(this)), 165 base::Bind(&EsParserH264Test::NewVideoConfig, base::Unretained(this)),
202 base::Bind(&EsParserH264Test::EmitBuffer, base::Unretained(this))); 166 base::Bind(&EsParserH264Test::EmitBuffer, base::Unretained(this)));
203 167 return ProcessPesPackets(&es_parser, pes_packets, force_timing);
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;
207
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;
212
213 ASSERT_TRUE(
214 es_parser.Parse(&stream_[cur_pes_offset], cur_pes_size, pts, dts));
215 }
216 es_parser.Flush();
217 } 168 }
218 169
219 void EsParserH264Test::EmitBuffer(scoped_refptr<StreamParserBuffer> buffer) { 170 void EsParserH264Test::CheckAccessUnits() {
220 ASSERT_LT(buffer_count_, access_units_.size()); 171 EXPECT_EQ(buffer_count_, access_units_.size());
221 EXPECT_EQ(buffer->timestamp(), access_units_[buffer_count_].pts); 172
222 buffer_count_++; 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()
177 << ") ";
178 }
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);
223 } 183 }
224 184
225 TEST_F(EsParserH264Test, OneAccessUnitPerPes) { 185 TEST_F(EsParserH264Test, OneAccessUnitPerPes) {
226 LoadStream("bear.h264"); 186 LoadH264Stream("bear.h264");
227 187
228 // One to one equivalence between PES packets and access units. 188 // One to one equivalence between PES packets and access units.
229 std::vector<Packet> pes_packets(access_units_); 189 std::vector<Packet> pes_packets(access_units_);
230 GetPesTimestamps(pes_packets); 190 GetPesTimestamps(&pes_packets);
231 191
232 // Process each PES packet. 192 // Process each PES packet.
233 ProcessPesPackets(pes_packets, false); 193 EXPECT_TRUE(Process(pes_packets, false));
234 EXPECT_EQ(buffer_count_, access_units_.size()); 194 CheckAccessUnits();
235 } 195 }
236 196
237 TEST_F(EsParserH264Test, NonAlignedPesPacket) { 197 TEST_F(EsParserH264Test, NonAlignedPesPacket) {
238 LoadStream("bear.h264"); 198 LoadH264Stream("bear.h264");
239 199
240 // Generate the PES packets. 200 // Generate the PES packets.
241 std::vector<Packet> pes_packets; 201 std::vector<Packet> pes_packets;
242 Packet cur_pes_packet; 202 Packet cur_pes_packet;
243 cur_pes_packet.offset = 0; 203 cur_pes_packet.offset = 0;
244 for (size_t k = 0; k < access_units_.size(); k++) { 204 for (size_t k = 0; k < access_units_.size(); k++) {
245 pes_packets.push_back(cur_pes_packet); 205 pes_packets.push_back(cur_pes_packet);
246 206
247 // The current PES packet includes the remaining bytes of the previous 207 // The current PES packet includes the remaining bytes of the previous
248 // access unit and some bytes of the current access unit 208 // 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 209 // (487 bytes in this unit test but no more than the current access unit
250 // size). 210 // size).
251 cur_pes_packet.offset = access_units_[k].offset + 211 cur_pes_packet.offset = access_units_[k].offset +
252 std::min<size_t>(487u, access_units_[k].size); 212 std::min<size_t>(487u, access_units_[k].size);
253 } 213 }
254 ComputePacketSize(pes_packets, stream_.size()); 214 ComputePacketSize(&pes_packets);
255 GetPesTimestamps(pes_packets); 215 GetPesTimestamps(&pes_packets);
256 216
257 // Process each PES packet. 217 // Process each PES packet.
258 ProcessPesPackets(pes_packets, false); 218 EXPECT_TRUE(Process(pes_packets, false));
259 EXPECT_EQ(buffer_count_, access_units_.size()); 219 CheckAccessUnits();
260 } 220 }
261 221
262 TEST_F(EsParserH264Test, SeveralPesPerAccessUnit) { 222 TEST_F(EsParserH264Test, SeveralPesPerAccessUnit) {
263 LoadStream("bear.h264"); 223 LoadH264Stream("bear.h264");
264 224
265 // Get the minimum size of an access unit. 225 // Get the minimum size of an access unit.
266 size_t min_access_unit_size = stream_.size(); 226 size_t min_access_unit_size = stream_.size();
267 for (size_t k = 0; k < access_units_.size(); k++) { 227 for (size_t k = 0; k < access_units_.size(); k++) {
268 if (min_access_unit_size >= access_units_[k].size) 228 if (min_access_unit_size >= access_units_[k].size)
269 min_access_unit_size = access_units_[k].size; 229 min_access_unit_size = access_units_[k].size;
270 } 230 }
271 231
272 // Use a small PES packet size or the minimum access unit size 232 // Use a small PES packet size or the minimum access unit size
273 // if it is even smaller. 233 // if it is even smaller.
274 size_t pes_size = 512; 234 size_t pes_size = 512;
275 if (min_access_unit_size < pes_size) 235 if (min_access_unit_size < pes_size)
276 pes_size = min_access_unit_size; 236 pes_size = min_access_unit_size;
277 237
278 std::vector<Packet> pes_packets; 238 std::vector<Packet> pes_packets;
279 Packet cur_pes_packet; 239 Packet cur_pes_packet;
280 cur_pes_packet.offset = 0; 240 cur_pes_packet.offset = 0;
281 while (cur_pes_packet.offset < stream_.size()) { 241 while (cur_pes_packet.offset < stream_.size()) {
282 pes_packets.push_back(cur_pes_packet); 242 pes_packets.push_back(cur_pes_packet);
283 cur_pes_packet.offset += pes_size; 243 cur_pes_packet.offset += pes_size;
284 } 244 }
285 ComputePacketSize(pes_packets, stream_.size()); 245 ComputePacketSize(&pes_packets);
286 GetPesTimestamps(pes_packets); 246 GetPesTimestamps(&pes_packets);
287 247
288 // Process each PES packet. 248 // Process each PES packet.
289 ProcessPesPackets(pes_packets, false); 249 EXPECT_TRUE(Process(pes_packets, false));
290 EXPECT_EQ(buffer_count_, access_units_.size()); 250 CheckAccessUnits();
291 251
292 // Process PES packets forcing timings for each PES packet. 252 // Process PES packets forcing timings for each PES packet.
293 buffer_count_ = 0; 253 EXPECT_TRUE(Process(pes_packets, true));
294 ProcessPesPackets(pes_packets, true); 254 CheckAccessUnits();
295 EXPECT_EQ(buffer_count_, access_units_.size());
296 } 255 }
297 256
298 } // namespace mp2t 257 } // namespace mp2t
299 } // namespace media 258 } // namespace media
300 259
OLDNEW
« no previous file with comments | « media/formats/mp2t/es_parser_adts_unittest.cc ('k') | media/formats/mp2t/es_parser_test_base.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698