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

Side by Side Diff: media/formats/mp4/avc_unittest.cc

Issue 246853005: Fix SPS/PPS insertion logic in MP4StreamParser. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address CR comments Created 6 years, 8 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 <string.h> 5 #include <string.h>
6 6
7 #include "base/basictypes.h" 7 #include "base/basictypes.h"
8 #include "base/strings/string_util.h"
8 #include "media/base/stream_parser_buffer.h" 9 #include "media/base/stream_parser_buffer.h"
10 #include "media/filters/h264_parser.h"
9 #include "media/formats/mp4/avc.h" 11 #include "media/formats/mp4/avc.h"
10 #include "media/formats/mp4/box_definitions.h" 12 #include "media/formats/mp4/box_definitions.h"
11 #include "testing/gtest/include/gtest/gtest.h" 13 #include "testing/gtest/include/gtest/gtest.h"
12 14
13 namespace media { 15 namespace media {
14 namespace mp4 { 16 namespace mp4 {
15 17
16 static const uint8 kNALU1[] = { 0x01, 0x02, 0x03 }; 18 static const uint8 kNALU1[] = { 0x01, 0x02, 0x03 };
17 static const uint8 kNALU2[] = { 0x04, 0x05, 0x06, 0x07 }; 19 static const uint8 kNALU2[] = { 0x04, 0x05, 0x06, 0x07 };
18 static const uint8 kExpected[] = { 20 static const uint8 kExpected[] = {
19 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x03, 21 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x03,
20 0x00, 0x00, 0x00, 0x01, 0x04, 0x05, 0x06, 0x07 }; 22 0x00, 0x00, 0x00, 0x01, 0x04, 0x05, 0x06, 0x07 };
21 23
22 static const uint8 kExpectedParamSets[] = { 24 static const uint8 kExpectedParamSets[] = {
23 0x00, 0x00, 0x00, 0x01, 0x67, 0x12, 25 0x00, 0x00, 0x00, 0x01, 0x67, 0x12,
24 0x00, 0x00, 0x00, 0x01, 0x67, 0x34, 26 0x00, 0x00, 0x00, 0x01, 0x67, 0x34,
25 0x00, 0x00, 0x00, 0x01, 0x68, 0x56, 0x78}; 27 0x00, 0x00, 0x00, 0x01, 0x68, 0x56, 0x78};
26 28
29
scherkus (not reviewing) 2014/04/24 17:16:35 remove extra blank line
acolwell GONE FROM CHROMIUM 2014/04/24 23:52:15 Done.
30 static H264NALU::Type StringToNALUType(const std::string& name) {
31 if (name == "P")
32 return H264NALU::kNonIDRSlice;
33
34 if (name == "I")
35 return H264NALU::kIDRSlice;
36
37 if (name == "SEI")
38 return H264NALU::kSEIMessage;
39
40 if (name == "SPS")
41 return H264NALU::kSPS;
42
43 if (name == "SPSExt")
44 return H264NALU::kSPSExt;
45
46 if (name == "PPS")
47 return H264NALU::kPPS;
48
49 if (name == "AUD")
50 return H264NALU::kAUD;
51
52 if (name == "EOSeq")
53 return H264NALU::kEOSeq;
54
55 if (name == "EOStr")
56 return H264NALU::kEOStream;
57
58 if (name == "FILL")
59 return H264NALU::kFiller;
60
61 if (name == "R14")
62 return H264NALU::kReserved14;
63
64 NOTREACHED() << "Unexpected name: " << name;
scherkus (not reviewing) 2014/04/24 17:16:35 consider the only clients are this unit test, mayb
acolwell GONE FROM CHROMIUM 2014/04/24 23:52:15 Done.
65 return H264NALU::kUnspecified;
66 }
67
68 static std::string NALUTypeToString(int type) {
69 switch (type) {
70 case H264NALU::kNonIDRSlice:
71 return "P";
72 case H264NALU::kSliceDataA:
73 return "SDA";
74 case H264NALU::kSliceDataB:
75 return "SDB";
76 case H264NALU::kSliceDataC:
77 return "SDC";
78 case H264NALU::kIDRSlice:
79 return "I";
80 case H264NALU::kSEIMessage:
81 return "SEI";
82 case H264NALU::kSPS:
83 return "SPS";
84 case H264NALU::kSPSExt:
85 return "SPSExt";
86 case H264NALU::kPPS:
87 return "PPS";
88 case H264NALU::kAUD:
89 return "AUD";
90 case H264NALU::kEOSeq:
91 return "EOSeq";
92 case H264NALU::kEOStream:
93 return "EOStr";
94 case H264NALU::kFiller:
95 return "FILL";
96 case H264NALU::kReserved14:
97 return "R14";
98 default:
scherkus (not reviewing) 2014/04/24 17:16:35 leave out default in favour of being explicit w/ a
acolwell GONE FROM CHROMIUM 2014/04/24 23:52:15 Done.
99 NOTREACHED() << "Unexpected type: " << type;
100 return "UnsupportedType";
101 };
102
103 NOTREACHED() << "Unexpected type: " << type;
104 return "UnsupportedType";
scherkus (not reviewing) 2014/04/24 17:16:35 ditto -- but your call
acolwell GONE FROM CHROMIUM 2014/04/24 23:52:15 Done.
105 }
106
107 void StringToAnnexB(const std::string& str, std::vector<uint8>* buffer) {
108 DCHECK(!str.empty());
109
110 std::vector<std::string> tokens;
111 EXPECT_GT(Tokenize(str, " ", &tokens), 0u);
112
113 buffer->clear();
114 for (size_t i = 0; i < tokens.size(); ++i) {
115 // Write the start code.
116 buffer->push_back(0x00);
117 buffer->push_back(0x00);
118 buffer->push_back(0x00);
119 buffer->push_back(0x01);
120
121 // Write NALU type.
122 buffer->push_back(StringToNALUType(tokens[i]));
123
124 // Write junk for the payload since the current code doesn't
125 // actually look at it.
126 buffer->push_back(0x32);
127 buffer->push_back(0x12);
128 buffer->push_back(0x67);
129 }
130 }
131
132 std::string AnnexBToString(const std::vector<uint8>& buffer) {
133 std::stringstream ss;
134
135 H264Parser parser;
136 parser.SetStream(&buffer[0], buffer.size());
137
138 H264NALU nalu;
139 bool first = true;
140 while (parser.AdvanceToNextNALU(&nalu) == H264Parser::kOk) {
141 if (!first)
142 ss << " ";
143 else
144 first = false;
145
146 ss << NALUTypeToString(nalu.nal_unit_type);
147 }
148 return ss.str();
149 }
150
27 class AVCConversionTest : public testing::TestWithParam<int> { 151 class AVCConversionTest : public testing::TestWithParam<int> {
28 protected: 152 protected:
29 void MakeInputForLength(int length_size, std::vector<uint8>* buf) { 153 void WriteLength(int length_size, int length, std::vector<uint8>* buf) {
30 buf->clear(); 154 DCHECK_GE(length, 0);
31 for (int i = 1; i < length_size; i++) 155 DCHECK_LE(length, 255);
32 buf->push_back(0);
33 buf->push_back(sizeof(kNALU1));
34 buf->insert(buf->end(), kNALU1, kNALU1 + sizeof(kNALU1));
35 156
36 for (int i = 1; i < length_size; i++) 157 for (int i = 1; i < length_size; i++)
37 buf->push_back(0); 158 buf->push_back(0);
38 buf->push_back(sizeof(kNALU2)); 159 buf->push_back(length);
160 }
161
162 void MakeInputForLength(int length_size, std::vector<uint8>* buf) {
163 buf->clear();
164
165 WriteLength(length_size, sizeof(kNALU1), buf);
166 buf->insert(buf->end(), kNALU1, kNALU1 + sizeof(kNALU1));
167
168 WriteLength(length_size, sizeof(kNALU2), buf);
39 buf->insert(buf->end(), kNALU2, kNALU2 + sizeof(kNALU2)); 169 buf->insert(buf->end(), kNALU2, kNALU2 + sizeof(kNALU2));
40 } 170 }
171
41 }; 172 };
42 173
43 TEST_P(AVCConversionTest, ParseCorrectly) { 174 TEST_P(AVCConversionTest, ParseCorrectly) {
44 std::vector<uint8> buf; 175 std::vector<uint8> buf;
45 MakeInputForLength(GetParam(), &buf); 176 MakeInputForLength(GetParam(), &buf);
46 EXPECT_TRUE(AVC::ConvertFrameToAnnexB(GetParam(), &buf)); 177 EXPECT_TRUE(AVC::ConvertFrameToAnnexB(GetParam(), &buf));
178 EXPECT_TRUE(AVC::IsValidAnnexB(buf));
47 EXPECT_EQ(buf.size(), sizeof(kExpected)); 179 EXPECT_EQ(buf.size(), sizeof(kExpected));
48 EXPECT_EQ(0, memcmp(kExpected, &buf[0], sizeof(kExpected))); 180 EXPECT_EQ(0, memcmp(kExpected, &buf[0], sizeof(kExpected)));
181 EXPECT_EQ("P SDC", AnnexBToString(buf));
182 }
183
184 // Intentionally write NALU sizes that are larger than the buffer.
185 TEST_P(AVCConversionTest, NALUSizeTooLarge) {
186 std::vector<uint8> buf;
187 WriteLength(GetParam(), 10 * sizeof(kNALU1), &buf);
188 buf.insert(buf.end(), kNALU1, kNALU1 + sizeof(kNALU1));
189 EXPECT_FALSE(AVC::ConvertFrameToAnnexB(GetParam(), &buf));
190 }
191
192 TEST_P(AVCConversionTest, NALUSizeIsZero) {
193 std::vector<uint8> buf;
194 WriteLength(GetParam(), 0, &buf);
195
196 WriteLength(GetParam(), sizeof(kNALU1), &buf);
197 buf.insert(buf.end(), kNALU1, kNALU1 + sizeof(kNALU1));
198
199 WriteLength(GetParam(), 0, &buf);
200
201 WriteLength(GetParam(), sizeof(kNALU2), &buf);
202 buf.insert(buf.end(), kNALU2, kNALU2 + sizeof(kNALU2));
203
204 EXPECT_FALSE(AVC::ConvertFrameToAnnexB(GetParam(), &buf));
49 } 205 }
50 206
51 TEST_P(AVCConversionTest, ParsePartial) { 207 TEST_P(AVCConversionTest, ParsePartial) {
52 std::vector<uint8> buf; 208 std::vector<uint8> buf;
53 MakeInputForLength(GetParam(), &buf); 209 MakeInputForLength(GetParam(), &buf);
54 buf.pop_back(); 210 buf.pop_back();
55 EXPECT_FALSE(AVC::ConvertFrameToAnnexB(GetParam(), &buf)); 211 EXPECT_FALSE(AVC::ConvertFrameToAnnexB(GetParam(), &buf));
56 // This tests a buffer ending in the middle of a NAL length. For length size 212 // This tests a buffer ending in the middle of a NAL length. For length size
57 // of one, this can't happen, so we skip that case. 213 // of one, this can't happen, so we skip that case.
58 if (GetParam() != 1) { 214 if (GetParam() != 1) {
(...skipping 19 matching lines...) Expand all
78 avc_config.sps_list[0].push_back(0x67); 234 avc_config.sps_list[0].push_back(0x67);
79 avc_config.sps_list[0].push_back(0x12); 235 avc_config.sps_list[0].push_back(0x12);
80 avc_config.sps_list[1].push_back(0x67); 236 avc_config.sps_list[1].push_back(0x67);
81 avc_config.sps_list[1].push_back(0x34); 237 avc_config.sps_list[1].push_back(0x34);
82 avc_config.pps_list.resize(1); 238 avc_config.pps_list.resize(1);
83 avc_config.pps_list[0].push_back(0x68); 239 avc_config.pps_list[0].push_back(0x68);
84 avc_config.pps_list[0].push_back(0x56); 240 avc_config.pps_list[0].push_back(0x56);
85 avc_config.pps_list[0].push_back(0x78); 241 avc_config.pps_list[0].push_back(0x78);
86 242
87 std::vector<uint8> buf; 243 std::vector<uint8> buf;
88 EXPECT_TRUE(AVC::ConvertConfigToAnnexB(avc_config, &buf)); 244 std::vector<SubsampleEntry> subsamples;
245 EXPECT_TRUE(AVC::ConvertConfigToAnnexB(avc_config, &buf, &subsamples));
89 EXPECT_EQ(0, memcmp(kExpectedParamSets, &buf[0], 246 EXPECT_EQ(0, memcmp(kExpectedParamSets, &buf[0],
90 sizeof(kExpectedParamSets))); 247 sizeof(kExpectedParamSets)));
248 EXPECT_EQ("SPS SPS PPS", AnnexBToString(buf));
249 }
250
251 // Verify that we can round trip string -> Annex B -> string.
252 TEST_F(AVCConversionTest, StringConversionFunctions) {
253 std::string str =
254 "AUD SPS SPSExt SPS PPS SEI SEI R14 I P FILL EOSeq EOStr";
255 std::vector<uint8> buf;
256
257 StringToAnnexB(str, &buf);
258
259 EXPECT_TRUE(AVC::IsValidAnnexB(buf));
260
261 EXPECT_EQ(str, AnnexBToString(buf));
262 }
263
264 TEST_F(AVCConversionTest, ValidAnnexBConstructs) {
265 const char* test_cases[] = {
266 "I",
267 "I I I I",
268 "AUD I",
269 "AUD SPS PPS I",
270 "I EOSeq",
271 "I EOSeq EOStr",
272 "I EOStr",
273 "P",
274 "P P P P",
275 "AUD SPS PPS P",
276 "SEI SEI I",
277 "SEI SEI R14 I",
278 "SPS SPSExt SPS PPS I P",
279 "R14 SEI I",
280 };
281
282 for (size_t i = 0; i < arraysize(test_cases); ++i) {
283 std::vector<uint8> buf;
284 StringToAnnexB(test_cases[i], &buf);
285 EXPECT_TRUE(AVC::IsValidAnnexB(buf)) << "'" <<test_cases[i] << "' failed";
scherkus (not reviewing) 2014/04/24 17:16:35 add space past <<
acolwell GONE FROM CHROMIUM 2014/04/24 23:52:15 Done.
286 }
287 }
288
289 TEST_F(AVCConversionTest, InvalidAnnexBConstructs) {
290 static const char* test_cases[] = {
291 "AUD", // No VCL present.
292 "SPS PPS", // No VCL present.
293 "SPS PPS AUD I", // Parameter sets must come after AUD.
294 "SPSExt SPS P", // SPS must come before SPSExt.
295 "SPS PPS SPSExt P", // SPSExt must follow an SPS.
296 "EOSeq", // EOSeq must come after a VCL.
297 "EOStr", // EOStr must come after a VCL.
298 "I EOStr EOSeq", // EOSeq must come before EOStr.
299 "I R14", // Reserved14-18 must come before first VCL.
300 "I SEI", // SEI must come before first VCL.
301 "P SPS P", // SPS after first VCL would indicate a new access unit.
302 };
303
304 for (size_t i = 0; i < arraysize(test_cases); ++i) {
305 std::vector<uint8> buf;
306 StringToAnnexB(test_cases[i], &buf);
307 EXPECT_FALSE(AVC::IsValidAnnexB(buf)) << "'" <<test_cases[i] << "' failed";
scherkus (not reviewing) 2014/04/24 17:16:35 ditto
acolwell GONE FROM CHROMIUM 2014/04/24 23:52:15 Done.
308 }
309 }
310
311 typedef struct {
312 const char* input;
313 const char* expected;
314 } InsertTestCases;
315
316 TEST_F(AVCConversionTest, InsertParamSetsAnnexB) {
317 static const InsertTestCases test_cases[] = {
318 { "I", "SPS SPS PPS I" },
319 { "AUD I", "AUD SPS SPS PPS I" },
320
321 // Cases where param sets in |avc_config| are placed before
322 // the existing ones.
323 { "SPS PPS I", "SPS SPS PPS SPS PPS I" },
324 { "AUD SPS PPS I", "AUD SPS SPS PPS SPS PPS I" }, // Note: params placed
325 // after AUD.
326 };
327
328 AVCDecoderConfigurationRecord avc_config;
329 avc_config.sps_list.resize(2);
330 avc_config.sps_list[0].push_back(0x67);
331 avc_config.sps_list[0].push_back(0x12);
332 avc_config.sps_list[1].push_back(0x67);
333 avc_config.sps_list[1].push_back(0x34);
334 avc_config.pps_list.resize(1);
335 avc_config.pps_list[0].push_back(0x68);
336 avc_config.pps_list[0].push_back(0x56);
337 avc_config.pps_list[0].push_back(0x78);
338
339 for (size_t i = 0; i < arraysize(test_cases); ++i) {
340 std::vector<uint8> buf;
341 std::vector<SubsampleEntry> subsamples;
342
343 StringToAnnexB(test_cases[i].input, &buf);
344
345 EXPECT_TRUE(AVC::InsertParamSetsAnnexB(avc_config, &buf, &subsamples))
346 << "'" << test_cases[i].input << "' insert failed.";
347 EXPECT_TRUE(AVC::IsValidAnnexB(buf))
348 << "'" << test_cases[i].input << "' created invalid AnnexB.";
349 EXPECT_EQ(test_cases[i].expected, AnnexBToString(buf))
350 << "'" << test_cases[i].input << "' generated unexpected output.";
351 }
352
scherkus (not reviewing) 2014/04/24 17:16:35 remove extra blank line
acolwell GONE FROM CHROMIUM 2014/04/24 23:52:15 Done.
91 } 353 }
92 354
93 } // namespace mp4 355 } // namespace mp4
94 } // namespace media 356 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698