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

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: 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
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 <algorithm>
6 #include <sstream>
7 #include <string>
6 #include <vector> 8 #include <vector>
7 9
8 #include "base/bind.h" 10 #include "base/bind.h"
9 #include "base/command_line.h" 11 #include "base/command_line.h"
wolenetz 2014/08/14 18:46:29 Here and in the other class derived from EsParserT
damienv1 2014/08/14 22:31:22 Done.
10 #include "base/files/memory_mapped_file.h" 12 #include "base/files/memory_mapped_file.h"
11 #include "base/logging.h" 13 #include "base/logging.h"
12 #include "base/path_service.h" 14 #include "base/path_service.h"
15 #include "base/strings/string_util.h"
13 #include "base/time/time.h" 16 #include "base/time/time.h"
14 #include "media/base/stream_parser_buffer.h" 17 #include "media/base/stream_parser_buffer.h"
15 #include "media/base/test_data_util.h" 18 #include "media/base/test_data_util.h"
16 #include "media/filters/h264_parser.h" 19 #include "media/filters/h264_parser.h"
17 #include "media/formats/mp2t/es_parser_h264.h" 20 #include "media/formats/mp2t/es_parser_h264.h"
21 #include "media/formats/mp2t/es_parser_test_base.h"
18 #include "testing/gtest/include/gtest/gtest.h" 22 #include "testing/gtest/include/gtest/gtest.h"
19 23
20 namespace media { 24 namespace media {
21 class VideoDecoderConfig; 25 class VideoDecoderConfig;
22 26
23 namespace mp2t { 27 namespace mp2t {
24 28
25 namespace { 29 class EsParserH264Test : public EsParserTestBase,
30 public testing::Test {
31 public:
32 EsParserH264Test() {}
33 virtual ~EsParserH264Test() {}
26 34
27 struct Packet { 35 protected:
28 // Offset in the stream. 36 void LoadH264Stream(const char* filename);
29 size_t offset; 37 void GetPesTimestamps(std::vector<Packet>& pes_packets);
38 bool Process(const std::vector<Packet>& pes_packets, bool force_timing);
39 void CheckAccessUnits();
30 40
31 // Size of the packet. 41 // Access units of the stream with AUD NALUs.
32 size_t size; 42 std::vector<Packet> access_units_;
33 43
34 // Timestamp of the packet. 44 private:
35 base::TimeDelta pts; 45 // Get the offset of the start of each access unit of |stream_|.
46 // This function assumes there is only one slice per access unit.
47 // This is a very simplified access unit segmenter that is good
48 // enough for unit tests.
49 void GetAccessUnits();
50
51 // Insert an AUD before each access unit.
52 // Update |stream_| and |access_units_| accordingly.
53 void InsertAUD();
54
55 DISALLOW_COPY_AND_ASSIGN(EsParserH264Test);
36 }; 56 };
37 57
38 // Compute the size of each packet assuming packets are given in stream order 58 void EsParserH264Test::LoadH264Stream(const char* filename) {
39 // and the last packet covers the end of the stream. 59 // Load the input H264 file and segment it into access units.
40 void ComputePacketSize(std::vector<Packet>& packets, size_t stream_size) { 60 LoadStream(filename);
41 for (size_t k = 0; k < packets.size() - 1; k++) { 61 GetAccessUnits();
42 DCHECK_GE(packets[k + 1].offset, packets[k].offset); 62 ASSERT_GT(access_units_.size(), 0u);
43 packets[k].size = packets[k + 1].offset - packets[k].offset; 63
44 } 64 // Insert AUDs into the stream.
45 packets[packets.size() - 1].size = 65 InsertAUD();
46 stream_size - packets[packets.size() - 1].offset; 66
67 // Generate some timestamps based on a 25fps stream.
68 for (size_t k = 0; k < access_units_.size(); k++)
69 access_units_[k].pts = base::TimeDelta::FromMilliseconds(k * 40u);
47 } 70 }
48 71
49 // Get the offset of the start of each access unit. 72 void EsParserH264Test::GetAccessUnits() {
50 // This function assumes there is only one slice per access unit. 73 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; 74 bool start_access_unit = true;
56 75
57 // In a first pass, retrieve the offsets of all access units. 76 // In a first pass, retrieve the offsets of all access units.
58 size_t offset = 0; 77 size_t offset = 0;
59 while (true) { 78 while (true) {
60 // Find the next start code. 79 // Find the next start code.
61 off_t relative_offset = 0; 80 off_t relative_offset = 0;
62 off_t start_code_size = 0; 81 off_t start_code_size = 0;
63 bool success = H264Parser::FindStartCode( 82 bool success = H264Parser::FindStartCode(
64 &stream[offset], stream_size - offset, 83 &stream_[offset], stream_.size() - offset,
65 &relative_offset, &start_code_size); 84 &relative_offset, &start_code_size);
66 if (!success) 85 if (!success)
67 break; 86 break;
68 offset += relative_offset; 87 offset += relative_offset;
69 88
70 if (start_access_unit) { 89 if (start_access_unit) {
71 Packet cur_access_unit; 90 Packet cur_access_unit;
72 cur_access_unit.offset = offset; 91 cur_access_unit.offset = offset;
73 access_units.push_back(cur_access_unit); 92 access_units_.push_back(cur_access_unit);
74 start_access_unit = false; 93 start_access_unit = false;
75 } 94 }
76 95
77 // Get the NALU type. 96 // Get the NALU type.
78 offset += start_code_size; 97 offset += start_code_size;
79 if (offset >= stream_size) 98 if (offset >= stream_.size())
80 break; 99 break;
81 int nal_unit_type = stream[offset] & 0x1f; 100 int nal_unit_type = stream_[offset] & 0x1f;
82 101
83 // We assume there is only one slice per access unit. 102 // We assume there is only one slice per access unit.
84 if (nal_unit_type == H264NALU::kIDRSlice || 103 if (nal_unit_type == H264NALU::kIDRSlice ||
85 nal_unit_type == H264NALU::kNonIDRSlice) { 104 nal_unit_type == H264NALU::kNonIDRSlice) {
86 start_access_unit = true; 105 start_access_unit = true;
87 } 106 }
88 } 107 }
89 108
90 ComputePacketSize(access_units, stream_size); 109 ComputePacketSize(&access_units_);
91 return access_units;
92 } 110 }
93 111
94 // Append an AUD NALU at the beginning of each access unit 112 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 }; 113 uint8 aud[] = { 0x00, 0x00, 0x01, 0x09 };
102 stream_with_aud.resize(stream_size + access_units.size() * sizeof(aud)); 114
103 access_units_with_aud.resize(access_units.size()); 115 std::vector<uint8> stream_with_aud(
116 stream_.size() + access_units_.size() * sizeof(aud));
117 std::vector<EsParserTestBase::Packet> access_units_with_aud(
118 access_units_.size());
104 119
105 size_t offset = 0; 120 size_t offset = 0;
106 for (size_t k = 0; k < access_units.size(); k++) { 121 for (size_t k = 0; k < access_units_.size(); k++) {
107 access_units_with_aud[k].offset = offset; 122 access_units_with_aud[k].offset = offset;
108 access_units_with_aud[k].size = access_units[k].size + sizeof(aud); 123 access_units_with_aud[k].size = access_units_[k].size + sizeof(aud);
109 124
110 memcpy(&stream_with_aud[offset], aud, sizeof(aud)); 125 memcpy(&stream_with_aud[offset], aud, sizeof(aud));
111 offset += sizeof(aud); 126 offset += sizeof(aud);
112 127
113 memcpy(&stream_with_aud[offset], 128 memcpy(&stream_with_aud[offset],
114 &stream[access_units[k].offset], access_units[k].size); 129 &stream_[access_units_[k].offset], access_units_[k].size);
115 offset += access_units[k].size; 130 offset += access_units_[k].size;
116 }
117 }
118
119 } // namespace
120
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 } 131 }
147 132
148 DISALLOW_COPY_AND_ASSIGN(EsParserH264Test); 133 // Update the stream and access units used for the test.
149 }; 134 stream_ = stream_with_aud;
150 135 access_units_ = access_units_with_aud;
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 } 136 }
170 137
171 void EsParserH264Test::GetPesTimestamps(std::vector<Packet>& pes_packets) { 138 void EsParserH264Test::GetPesTimestamps(std::vector<Packet>& pes_packets) {
acolwell GONE FROM CHROMIUM 2014/08/14 17:45:48 nit: This should probably be std::vector<Packet>*
damienv1 2014/08/14 22:31:22 Done.
172 // Default: set to a negative timestamp to be able to differentiate from 139 // Default: set to a negative timestamp to be able to differentiate from
173 // real timestamps. 140 // real timestamps.
174 // Note: we don't use kNoTimestamp() here since this one has already 141 // Note: we don't use kNoTimestamp() here since this one has already
175 // a special meaning in EsParserH264. The negative timestamps should be 142 // a special meaning in EsParserH264. The negative timestamps should be
176 // ultimately discarded by the H264 parser since not relevant. 143 // ultimately discarded by the H264 parser since not relevant.
177 for (size_t k = 0; k < pes_packets.size(); k++) { 144 for (size_t k = 0; k < pes_packets.size(); k++) {
178 pes_packets[k].pts = base::TimeDelta::FromMilliseconds(-1); 145 pes_packets[k].pts = base::TimeDelta::FromMilliseconds(-1);
179 } 146 }
180 147
181 // Set a valid timestamp for PES packets which include the start 148 // Set a valid timestamp for PES packets which include the start
182 // of an H264 access unit. 149 // of an H264 access unit.
183 size_t pes_idx = 0; 150 size_t pes_idx = 0;
184 for (size_t k = 0; k < access_units_.size(); k++) { 151 for (size_t k = 0; k < access_units_.size(); k++) {
185 for (; pes_idx < pes_packets.size(); pes_idx++) { 152 for (; pes_idx < pes_packets.size(); pes_idx++) {
186 size_t pes_start = pes_packets[pes_idx].offset; 153 size_t pes_start = pes_packets[pes_idx].offset;
187 size_t pes_end = pes_packets[pes_idx].offset + pes_packets[pes_idx].size; 154 size_t pes_end = pes_packets[pes_idx].offset + pes_packets[pes_idx].size;
188 if (pes_start <= access_units_[k].offset && 155 if (pes_start <= access_units_[k].offset &&
189 pes_end > access_units_[k].offset) { 156 pes_end > access_units_[k].offset) {
190 pes_packets[pes_idx].pts = access_units_[k].pts; 157 pes_packets[pes_idx].pts = access_units_[k].pts;
191 break; 158 break;
192 } 159 }
193 } 160 }
194 } 161 }
195 } 162 }
196 163
197 void EsParserH264Test::ProcessPesPackets( 164 bool EsParserH264Test::Process(
198 const std::vector<Packet>& pes_packets, 165 const std::vector<Packet>& pes_packets,
199 bool force_timing) { 166 bool force_timing) {
200 EsParserH264 es_parser( 167 EsParserH264 es_parser(
201 base::Bind(&EsParserH264Test::NewVideoConfig, base::Unretained(this)), 168 base::Bind(&EsParserH264Test::NewVideoConfig, base::Unretained(this)),
202 base::Bind(&EsParserH264Test::EmitBuffer, base::Unretained(this))); 169 base::Bind(&EsParserH264Test::EmitBuffer, base::Unretained(this)));
203 170 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 } 171 }
218 172
219 void EsParserH264Test::EmitBuffer(scoped_refptr<StreamParserBuffer> buffer) { 173 void EsParserH264Test::CheckAccessUnits() {
220 ASSERT_LT(buffer_count_, access_units_.size()); 174 EXPECT_EQ(buffer_count_, access_units_.size());
221 EXPECT_EQ(buffer->timestamp(), access_units_[buffer_count_].pts); 175
222 buffer_count_++; 176 std::stringstream buffer_timestamps_stream;
177 for (size_t k = 0; k < access_units_.size(); k++) {
178 buffer_timestamps_stream << "("
179 << access_units_[k].pts.InMilliseconds()
180 << ") ";
181 }
182 std::string buffer_timestamps = buffer_timestamps_stream.str();
183 base::TrimWhitespaceASCII(
184 buffer_timestamps, base::TRIM_ALL, &buffer_timestamps);
185 EXPECT_EQ(buffer_timestamps_, buffer_timestamps);
223 } 186 }
224 187
225 TEST_F(EsParserH264Test, OneAccessUnitPerPes) { 188 TEST_F(EsParserH264Test, OneAccessUnitPerPes) {
226 LoadStream("bear.h264"); 189 LoadH264Stream("bear.h264");
227 190
228 // One to one equivalence between PES packets and access units. 191 // One to one equivalence between PES packets and access units.
229 std::vector<Packet> pes_packets(access_units_); 192 std::vector<Packet> pes_packets(access_units_);
230 GetPesTimestamps(pes_packets); 193 GetPesTimestamps(pes_packets);
231 194
232 // Process each PES packet. 195 // Process each PES packet.
233 ProcessPesPackets(pes_packets, false); 196 EXPECT_TRUE(Process(pes_packets, false));
234 EXPECT_EQ(buffer_count_, access_units_.size()); 197 CheckAccessUnits();
235 } 198 }
236 199
237 TEST_F(EsParserH264Test, NonAlignedPesPacket) { 200 TEST_F(EsParserH264Test, NonAlignedPesPacket) {
238 LoadStream("bear.h264"); 201 LoadH264Stream("bear.h264");
239 202
240 // Generate the PES packets. 203 // Generate the PES packets.
241 std::vector<Packet> pes_packets; 204 std::vector<Packet> pes_packets;
242 Packet cur_pes_packet; 205 Packet cur_pes_packet;
243 cur_pes_packet.offset = 0; 206 cur_pes_packet.offset = 0;
244 for (size_t k = 0; k < access_units_.size(); k++) { 207 for (size_t k = 0; k < access_units_.size(); k++) {
245 pes_packets.push_back(cur_pes_packet); 208 pes_packets.push_back(cur_pes_packet);
246 209
247 // The current PES packet includes the remaining bytes of the previous 210 // The current PES packet includes the remaining bytes of the previous
248 // access unit and some bytes of the current access unit 211 // 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 212 // (487 bytes in this unit test but no more than the current access unit
250 // size). 213 // size).
251 cur_pes_packet.offset = access_units_[k].offset + 214 cur_pes_packet.offset = access_units_[k].offset +
252 std::min<size_t>(487u, access_units_[k].size); 215 std::min<size_t>(487u, access_units_[k].size);
253 } 216 }
254 ComputePacketSize(pes_packets, stream_.size()); 217 ComputePacketSize(&pes_packets);
255 GetPesTimestamps(pes_packets); 218 GetPesTimestamps(pes_packets);
256 219
257 // Process each PES packet. 220 // Process each PES packet.
258 ProcessPesPackets(pes_packets, false); 221 EXPECT_TRUE(Process(pes_packets, false));
259 EXPECT_EQ(buffer_count_, access_units_.size()); 222 CheckAccessUnits();
260 } 223 }
261 224
262 TEST_F(EsParserH264Test, SeveralPesPerAccessUnit) { 225 TEST_F(EsParserH264Test, SeveralPesPerAccessUnit) {
263 LoadStream("bear.h264"); 226 LoadH264Stream("bear.h264");
264 227
265 // Get the minimum size of an access unit. 228 // Get the minimum size of an access unit.
266 size_t min_access_unit_size = stream_.size(); 229 size_t min_access_unit_size = stream_.size();
267 for (size_t k = 0; k < access_units_.size(); k++) { 230 for (size_t k = 0; k < access_units_.size(); k++) {
268 if (min_access_unit_size >= access_units_[k].size) 231 if (min_access_unit_size >= access_units_[k].size)
269 min_access_unit_size = access_units_[k].size; 232 min_access_unit_size = access_units_[k].size;
270 } 233 }
271 234
272 // Use a small PES packet size or the minimum access unit size 235 // Use a small PES packet size or the minimum access unit size
273 // if it is even smaller. 236 // if it is even smaller.
274 size_t pes_size = 512; 237 size_t pes_size = 512;
275 if (min_access_unit_size < pes_size) 238 if (min_access_unit_size < pes_size)
276 pes_size = min_access_unit_size; 239 pes_size = min_access_unit_size;
277 240
278 std::vector<Packet> pes_packets; 241 std::vector<Packet> pes_packets;
279 Packet cur_pes_packet; 242 Packet cur_pes_packet;
280 cur_pes_packet.offset = 0; 243 cur_pes_packet.offset = 0;
281 while (cur_pes_packet.offset < stream_.size()) { 244 while (cur_pes_packet.offset < stream_.size()) {
282 pes_packets.push_back(cur_pes_packet); 245 pes_packets.push_back(cur_pes_packet);
283 cur_pes_packet.offset += pes_size; 246 cur_pes_packet.offset += pes_size;
284 } 247 }
285 ComputePacketSize(pes_packets, stream_.size()); 248 ComputePacketSize(&pes_packets);
286 GetPesTimestamps(pes_packets); 249 GetPesTimestamps(pes_packets);
287 250
288 // Process each PES packet. 251 // Process each PES packet.
289 ProcessPesPackets(pes_packets, false); 252 EXPECT_TRUE(Process(pes_packets, false));
290 EXPECT_EQ(buffer_count_, access_units_.size()); 253 CheckAccessUnits();
291 254
292 // Process PES packets forcing timings for each PES packet. 255 // Process PES packets forcing timings for each PES packet.
293 buffer_count_ = 0; 256 EXPECT_TRUE(Process(pes_packets, true));
294 ProcessPesPackets(pes_packets, true); 257 CheckAccessUnits();
295 EXPECT_EQ(buffer_count_, access_units_.size());
296 } 258 }
297 259
298 } // namespace mp2t 260 } // namespace mp2t
299 } // namespace media 261 } // namespace media
300 262
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698