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

Unified 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: Move typedef in test to try to make the Android bot happy. 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 side-by-side diff with in-line comments
Download patch
Index: media/formats/mp4/avc_unittest.cc
diff --git a/media/formats/mp4/avc_unittest.cc b/media/formats/mp4/avc_unittest.cc
index f6a1d569c11b97a830af382c4218d9558f008ce0..18acfc6f6d6511fc4dd132bbf773e2859eb4bb10 100644
--- a/media/formats/mp4/avc_unittest.cc
+++ b/media/formats/mp4/avc_unittest.cc
@@ -5,7 +5,9 @@
#include <string.h>
#include "base/basictypes.h"
+#include "base/strings/string_util.h"
#include "media/base/stream_parser_buffer.h"
+#include "media/filters/h264_parser.h"
#include "media/formats/mp4/avc.h"
#include "media/formats/mp4/box_definitions.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -24,28 +26,182 @@ static const uint8 kExpectedParamSets[] = {
0x00, 0x00, 0x00, 0x01, 0x67, 0x34,
0x00, 0x00, 0x00, 0x01, 0x68, 0x56, 0x78};
+
+static H264NALU::Type StringToNALUType(const std::string& name) {
+ if (name == "P")
+ return H264NALU::kNonIDRSlice;
+
+ if (name == "I")
+ return H264NALU::kIDRSlice;
+
+ if (name == "SEI")
+ return H264NALU::kSEIMessage;
+
+ if (name == "SPS")
+ return H264NALU::kSPS;
+
+ if (name == "SPSExt")
+ return H264NALU::kSPSExt;
+
+ if (name == "PPS")
+ return H264NALU::kPPS;
+
+ if (name == "AUD")
+ return H264NALU::kAUD;
+
+ if (name == "EOSeq")
+ return H264NALU::kEOSeq;
+
+ if (name == "EOStr")
+ return H264NALU::kEOStream;
+
+ if (name == "FILL")
+ return H264NALU::kFiller;
+
+ if (name == "R14")
+ return H264NALU::kReserved14;
+
+ NOTREACHED() << "Unexpected name: " << name;
+ return H264NALU::kUnspecified;
+}
+
+static std::string NALUTypeToString(int type) {
+ switch (type) {
+ case H264NALU::kNonIDRSlice:
+ return "P";
+ case H264NALU::kSliceDataA:
+ return "SDA";
+ case H264NALU::kSliceDataB:
+ return "SDB";
+ case H264NALU::kSliceDataC:
+ return "SDC";
+ case H264NALU::kIDRSlice:
+ return "I";
+ case H264NALU::kSEIMessage:
+ return "SEI";
+ case H264NALU::kSPS:
+ return "SPS";
+ case H264NALU::kSPSExt:
+ return "SPSExt";
+ case H264NALU::kPPS:
+ return "PPS";
+ case H264NALU::kAUD:
+ return "AUD";
+ case H264NALU::kEOSeq:
+ return "EOSeq";
+ case H264NALU::kEOStream:
+ return "EOStr";
+ case H264NALU::kFiller:
+ return "FILL";
+ case H264NALU::kReserved14:
+ return "R14";
+ default:
+ NOTREACHED() << "Unexpected type: " << type;
+ return "UnsupportedType";
+ };
+
+ NOTREACHED() << "Unexpected type: " << type;
+ return "UnsupportedType";
+}
+
+void StringToAnnexB(const std::string& str, std::vector<uint8>* buffer) {
+ DCHECK(!str.empty());
+
+ std::vector<std::string> tokens;
+ EXPECT_GT(Tokenize(str, " ", &tokens), 0u);
+
+ buffer->clear();
+ for (size_t i = 0; i < tokens.size(); ++i) {
+ // Write the start code.
+ buffer->push_back(0x00);
+ buffer->push_back(0x00);
+ buffer->push_back(0x00);
+ buffer->push_back(0x01);
+
+ // Write NALU type.
+ buffer->push_back(StringToNALUType(tokens[i]));
+
+ // Write junk for the payload since the current code doesn't
+ // actually look at it.
+ buffer->push_back(0x32);
+ buffer->push_back(0x12);
+ buffer->push_back(0x67);
+ }
+}
+
+std::string AnnexBToString(const std::vector<uint8>& buffer) {
+ std::stringstream ss;
+
+ H264Parser parser;
+ parser.SetStream(&buffer[0], buffer.size());
+
+ H264NALU nalu;
+ bool first = true;
+ while (parser.AdvanceToNextNALU(&nalu) == H264Parser::kOk) {
+ if (!first)
+ ss << " ";
+ else
+ first = false;
+
+ ss << NALUTypeToString(nalu.nal_unit_type);
+ }
+ return ss.str();
+}
+
class AVCConversionTest : public testing::TestWithParam<int> {
protected:
- void MakeInputForLength(int length_size, std::vector<uint8>* buf) {
- buf->clear();
+ void WriteLength(int length_size, int length, std::vector<uint8>* buf) {
+ DCHECK_GE(length, 0);
+ DCHECK_LE(length, 255);
+
for (int i = 1; i < length_size; i++)
buf->push_back(0);
- buf->push_back(sizeof(kNALU1));
+ buf->push_back(length);
+ }
+
+ void MakeInputForLength(int length_size, std::vector<uint8>* buf) {
+ buf->clear();
+
+ WriteLength(length_size, sizeof(kNALU1), buf);
buf->insert(buf->end(), kNALU1, kNALU1 + sizeof(kNALU1));
- for (int i = 1; i < length_size; i++)
- buf->push_back(0);
- buf->push_back(sizeof(kNALU2));
+ WriteLength(length_size, sizeof(kNALU2), buf);
buf->insert(buf->end(), kNALU2, kNALU2 + sizeof(kNALU2));
}
+
};
TEST_P(AVCConversionTest, ParseCorrectly) {
std::vector<uint8> buf;
MakeInputForLength(GetParam(), &buf);
EXPECT_TRUE(AVC::ConvertFrameToAnnexB(GetParam(), &buf));
+ EXPECT_TRUE(AVC::IsValidAnnexB(buf));
EXPECT_EQ(buf.size(), sizeof(kExpected));
EXPECT_EQ(0, memcmp(kExpected, &buf[0], sizeof(kExpected)));
+ EXPECT_EQ("P SDC", AnnexBToString(buf));
+}
+
+// Intentionally write NALU sizes that are larger than the buffer.
+TEST_P(AVCConversionTest, NALUSizeTooLarge) {
+ std::vector<uint8> buf;
+ WriteLength(GetParam(), 10 * sizeof(kNALU1), &buf);
+ buf.insert(buf.end(), kNALU1, kNALU1 + sizeof(kNALU1));
+ EXPECT_FALSE(AVC::ConvertFrameToAnnexB(GetParam(), &buf));
+}
+
+TEST_P(AVCConversionTest, NALUSizeIsZero) {
+ std::vector<uint8> buf;
+ WriteLength(GetParam(), 0, &buf);
+
+ WriteLength(GetParam(), sizeof(kNALU1), &buf);
+ buf.insert(buf.end(), kNALU1, kNALU1 + sizeof(kNALU1));
+
+ WriteLength(GetParam(), 0, &buf);
+
+ WriteLength(GetParam(), sizeof(kNALU2), &buf);
+ buf.insert(buf.end(), kNALU2, kNALU2 + sizeof(kNALU2));
+
+ EXPECT_FALSE(AVC::ConvertFrameToAnnexB(GetParam(), &buf));
}
TEST_P(AVCConversionTest, ParsePartial) {
@@ -88,6 +244,109 @@ TEST_F(AVCConversionTest, ConvertConfigToAnnexB) {
EXPECT_TRUE(AVC::ConvertConfigToAnnexB(avc_config, &buf));
EXPECT_EQ(0, memcmp(kExpectedParamSets, &buf[0],
sizeof(kExpectedParamSets)));
+ EXPECT_EQ("SPS SPS PPS", AnnexBToString(buf));
+}
+
+// Verify that we can round trip string -> Annex B -> string.
+TEST_F(AVCConversionTest, StringConversionFunctions) {
+ std::string str =
+ "AUD SPS SPSExt SPS PPS SEI SEI R14 I P FILL EOSeq EOStr";
+ std::vector<uint8> buf;
+
+ StringToAnnexB(str, &buf);
+
+ EXPECT_TRUE(AVC::IsValidAnnexB(buf));
+
+ EXPECT_EQ(str, AnnexBToString(buf));
+}
+
+TEST_F(AVCConversionTest, ValidAnnexBConstructs) {
+ const char* test_cases[] = {
+ "I",
+ "I I I I",
+ "AUD I",
+ "AUD SPS PPS I",
+ "I EOSeq",
+ "I EOSeq EOStr",
+ "I EOStr",
+ "P",
+ "P P P P",
+ "AUD SPS PPS P",
+ "SEI SEI I",
+ "SEI SEI R14 I",
+ "SPS SPSExt SPS PPS I P",
+ "P P SPS P",
+ "R14 SEI I",
+ };
+
+ for (size_t i = 0; i < arraysize(test_cases); ++i) {
+ std::vector<uint8> buf;
+ StringToAnnexB(test_cases[i], &buf);
+ EXPECT_TRUE(AVC::IsValidAnnexB(buf)) << "'" <<test_cases[i] << "' failed";
+ }
+}
+
+TEST_F(AVCConversionTest, InvalidAnnexBConstructs) {
+ static const char* test_cases[] = {
+ "AUD", // No VCL present.
+ "SPS PPS", // No VCL present.
+ "SPS PPS AUD I", // Parameter sets must come after AUD.
+ "SPSExt SPS P", // SPS must come before SPSExt.
+ "SPS PPS SPSExt P", // SPSExt must follow an SPS.
+ "EOSeq", // EOSeq must come after a VCL.
+ "EOStr", // EOStr must come after a VCL.
+ "I EOStr EOSeq", // EOSeq must come before EOStr.
+ "I R14", // Reserved14-18 must come before first VCL.
+ "I SEI", // SEI must come before first VCL.
+ };
+
+ for (size_t i = 0; i < arraysize(test_cases); ++i) {
+ std::vector<uint8> buf;
+ StringToAnnexB(test_cases[i], &buf);
+ EXPECT_FALSE(AVC::IsValidAnnexB(buf)) << "'" <<test_cases[i] << "' failed";
+ }
+}
+
+typedef struct {
+ const char* input;
+ const char* expected;
+} InsertTestCases;
+
+TEST_F(AVCConversionTest, InsertParamSetsAnnexB) {
+ static const InsertTestCases test_cases[] = {
+ { "I", "SPS SPS PPS I" },
+ { "AUD I", "AUD SPS SPS PPS I" },
+
+ // Cases where param sets in |avc_config| are placed before
+ // the existing ones.
+ { "SPS PPS I", "SPS SPS PPS SPS PPS I" },
+ { "AUD SPS PPS I", "AUD SPS SPS PPS SPS PPS I" }, // Note: params placed
+ // after AUD.
+ };
+
+ AVCDecoderConfigurationRecord avc_config;
+ avc_config.sps_list.resize(2);
+ avc_config.sps_list[0].push_back(0x67);
+ avc_config.sps_list[0].push_back(0x12);
+ avc_config.sps_list[1].push_back(0x67);
+ avc_config.sps_list[1].push_back(0x34);
+ avc_config.pps_list.resize(1);
+ avc_config.pps_list[0].push_back(0x68);
+ avc_config.pps_list[0].push_back(0x56);
+ avc_config.pps_list[0].push_back(0x78);
+
+ for (size_t i = 0; i < arraysize(test_cases); ++i) {
+ std::vector<uint8> buf;
+ StringToAnnexB(test_cases[i].input, &buf);
+
+ EXPECT_TRUE(AVC::InsertParamSetsAnnexB(avc_config, &buf))
+ << "'" << test_cases[i].input << "' insert failed.";
+ EXPECT_TRUE(AVC::IsValidAnnexB(buf))
+ << "'" << test_cases[i].input << "' created invalid AnnexB.";
+ EXPECT_EQ(test_cases[i].expected, AnnexBToString(buf))
+ << "'" << test_cases[i].input << "' generated unexpected output.";
+ }
+
}
} // namespace mp4

Powered by Google App Engine
This is Rietveld 408576698