OLD | NEW |
---|---|
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 Loading... | |
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 |
OLD | NEW |