Chromium Code Reviews| 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 |