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
3// found in the LICENSE file.
4
5#include <string.h>
6
7#include "base/basictypes.h"
8#include "base/strings/string_util.h"
9#include "media/base/decrypt_config.h"
10#include "media/base/stream_parser_buffer.h"
11#include "media/filters/h264_parser.h"
12#include "media/formats/mp4/avc.h"
13#include "media/formats/mp4/box_definitions.h"
14#include "testing/gtest/include/gtest/gtest.h"
15
16namespace media {
17namespace mp4 {
18
19static const uint8 kNALU1[] = { 0x01, 0x02, 0x03 };
20static const uint8 kNALU2[] = { 0x04, 0x05, 0x06, 0x07 };
21static const uint8 kExpected[] = {
22  0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x03,
23  0x00, 0x00, 0x00, 0x01, 0x04, 0x05, 0x06, 0x07 };
24
25static const uint8 kExpectedParamSets[] = {
26  0x00, 0x00, 0x00, 0x01, 0x67, 0x12,
27  0x00, 0x00, 0x00, 0x01, 0x67, 0x34,
28  0x00, 0x00, 0x00, 0x01, 0x68, 0x56, 0x78};
29
30static 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  CHECK(false) << "Unexpected name: " << name;
65  return H264NALU::kUnspecified;
66}
67
68static 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
99    case H264NALU::kUnspecified:
100    case H264NALU::kReserved15:
101    case H264NALU::kReserved16:
102    case H264NALU::kReserved17:
103    case H264NALU::kReserved18:
104    case H264NALU::kCodedSliceAux:
105    case H264NALU::kCodedSliceExtension:
106      CHECK(false) << "Unexpected type: " << type;
107      break;
108  };
109
110  return "UnsupportedType";
111}
112
113static void WriteStartCodeAndNALUType(std::vector<uint8>* buffer,
114                                      const std::string& nal_unit_type) {
115  buffer->push_back(0x00);
116  buffer->push_back(0x00);
117  buffer->push_back(0x00);
118  buffer->push_back(0x01);
119  buffer->push_back(StringToNALUType(nal_unit_type));
120}
121
122void StringToAnnexB(const std::string& str, std::vector<uint8>* buffer,
123                    std::vector<SubsampleEntry>* subsamples) {
124  DCHECK(!str.empty());
125
126  std::vector<std::string> tokens;
127  EXPECT_GT(Tokenize(str, " ", &tokens), 0u);
128
129  buffer->clear();
130  for (size_t i = 0; i < tokens.size(); ++i) {
131    SubsampleEntry entry;
132    size_t start = buffer->size();
133
134    WriteStartCodeAndNALUType(buffer, tokens[i]);
135
136    entry.clear_bytes = buffer->size() - start;
137
138    // Write junk for the payload since the current code doesn't
139    // actually look at it.
140    buffer->push_back(0x32);
141    buffer->push_back(0x12);
142    buffer->push_back(0x67);
143
144    if (subsamples) {
145      // Simulate the encrypted bits containing something that looks
146      // like a SPS NALU.
147      WriteStartCodeAndNALUType(buffer, "SPS");
148    }
149
150    entry.cypher_bytes = buffer->size() - start - entry.clear_bytes;
151
152    if (subsamples) {
153      subsamples->push_back(entry);
154    }
155  }
156}
157
158std::string AnnexBToString(const std::vector<uint8>& buffer,
159                           const std::vector<SubsampleEntry>& subsamples) {
160  std::stringstream ss;
161
162  H264Parser parser;
163  parser.SetEncryptedStream(&buffer[0], buffer.size(), subsamples);
164
165  H264NALU nalu;
166  bool first = true;
167  while (parser.AdvanceToNextNALU(&nalu) == H264Parser::kOk) {
168    if (!first)
169      ss << " ";
170    else
171      first = false;
172
173    ss << NALUTypeToString(nalu.nal_unit_type);
174  }
175  return ss.str();
176}
177
178class AVCConversionTest : public testing::TestWithParam<int> {
179 protected:
180  void WriteLength(int length_size, int length, std::vector<uint8>* buf) {
181    DCHECK_GE(length, 0);
182    DCHECK_LE(length, 255);
183
184    for (int i = 1; i < length_size; i++)
185      buf->push_back(0);
186    buf->push_back(length);
187  }
188
189  void MakeInputForLength(int length_size, std::vector<uint8>* buf) {
190    buf->clear();
191
192    WriteLength(length_size, sizeof(kNALU1), buf);
193    buf->insert(buf->end(), kNALU1, kNALU1 + sizeof(kNALU1));
194
195    WriteLength(length_size, sizeof(kNALU2), buf);
196    buf->insert(buf->end(), kNALU2, kNALU2 + sizeof(kNALU2));
197  }
198
199};
200
201TEST_P(AVCConversionTest, ParseCorrectly) {
202  std::vector<uint8> buf;
203  std::vector<SubsampleEntry> subsamples;
204  MakeInputForLength(GetParam(), &buf);
205  EXPECT_TRUE(AVC::ConvertFrameToAnnexB(GetParam(), &buf));
206  EXPECT_TRUE(AVC::IsValidAnnexB(buf, subsamples));
207  EXPECT_EQ(buf.size(), sizeof(kExpected));
208  EXPECT_EQ(0, memcmp(kExpected, &buf[0], sizeof(kExpected)));
209  EXPECT_EQ("P SDC", AnnexBToString(buf, subsamples));
210}
211
212// Intentionally write NALU sizes that are larger than the buffer.
213TEST_P(AVCConversionTest, NALUSizeTooLarge) {
214  std::vector<uint8> buf;
215  WriteLength(GetParam(), 10 * sizeof(kNALU1), &buf);
216  buf.insert(buf.end(), kNALU1, kNALU1 + sizeof(kNALU1));
217  EXPECT_FALSE(AVC::ConvertFrameToAnnexB(GetParam(), &buf));
218}
219
220TEST_P(AVCConversionTest, NALUSizeIsZero) {
221  std::vector<uint8> buf;
222  WriteLength(GetParam(), 0, &buf);
223
224  WriteLength(GetParam(), sizeof(kNALU1), &buf);
225  buf.insert(buf.end(), kNALU1, kNALU1 + sizeof(kNALU1));
226
227  WriteLength(GetParam(), 0, &buf);
228
229  WriteLength(GetParam(), sizeof(kNALU2), &buf);
230  buf.insert(buf.end(), kNALU2, kNALU2 + sizeof(kNALU2));
231
232  EXPECT_FALSE(AVC::ConvertFrameToAnnexB(GetParam(), &buf));
233}
234
235TEST_P(AVCConversionTest, ParsePartial) {
236  std::vector<uint8> buf;
237  MakeInputForLength(GetParam(), &buf);
238  buf.pop_back();
239  EXPECT_FALSE(AVC::ConvertFrameToAnnexB(GetParam(), &buf));
240  // This tests a buffer ending in the middle of a NAL length. For length size
241  // of one, this can't happen, so we skip that case.
242  if (GetParam() != 1) {
243    MakeInputForLength(GetParam(), &buf);
244    buf.erase(buf.end() - (sizeof(kNALU2) + 1), buf.end());
245    EXPECT_FALSE(AVC::ConvertFrameToAnnexB(GetParam(), &buf));
246  }
247}
248
249TEST_P(AVCConversionTest, ParseEmpty) {
250  std::vector<uint8> buf;
251  EXPECT_TRUE(AVC::ConvertFrameToAnnexB(GetParam(), &buf));
252  EXPECT_EQ(0u, buf.size());
253}
254
255INSTANTIATE_TEST_CASE_P(AVCConversionTestValues,
256                        AVCConversionTest,
257                        ::testing::Values(1, 2, 4));
258
259TEST_F(AVCConversionTest, ConvertConfigToAnnexB) {
260  AVCDecoderConfigurationRecord avc_config;
261  avc_config.sps_list.resize(2);
262  avc_config.sps_list[0].push_back(0x67);
263  avc_config.sps_list[0].push_back(0x12);
264  avc_config.sps_list[1].push_back(0x67);
265  avc_config.sps_list[1].push_back(0x34);
266  avc_config.pps_list.resize(1);
267  avc_config.pps_list[0].push_back(0x68);
268  avc_config.pps_list[0].push_back(0x56);
269  avc_config.pps_list[0].push_back(0x78);
270
271  std::vector<uint8> buf;
272  std::vector<SubsampleEntry> subsamples;
273  EXPECT_TRUE(AVC::ConvertConfigToAnnexB(avc_config, &buf, &subsamples));
274  EXPECT_EQ(0, memcmp(kExpectedParamSets, &buf[0],
275                      sizeof(kExpectedParamSets)));
276  EXPECT_EQ("SPS SPS PPS", AnnexBToString(buf, subsamples));
277}
278
279// Verify that we can round trip string -> Annex B -> string.
280TEST_F(AVCConversionTest, StringConversionFunctions) {
281  std::string str =
282      "AUD SPS SPSExt SPS PPS SEI SEI R14 I P FILL EOSeq EOStr";
283  std::vector<uint8> buf;
284  std::vector<SubsampleEntry> subsamples;
285  StringToAnnexB(str, &buf, &subsamples);
286  EXPECT_TRUE(AVC::IsValidAnnexB(buf, subsamples));
287
288  EXPECT_EQ(str, AnnexBToString(buf, subsamples));
289}
290
291TEST_F(AVCConversionTest, ValidAnnexBConstructs) {
292  const char* test_cases[] = {
293    "I",
294    "I I I I",
295    "AUD I",
296    "AUD SPS PPS I",
297    "I EOSeq",
298    "I EOSeq EOStr",
299    "I EOStr",
300    "P",
301    "P P P P",
302    "AUD SPS PPS P",
303    "SEI SEI I",
304    "SEI SEI R14 I",
305    "SPS SPSExt SPS PPS I P",
306    "R14 SEI I",
307  };
308
309  for (size_t i = 0; i < arraysize(test_cases); ++i) {
310    std::vector<uint8> buf;
311    std::vector<SubsampleEntry> subsamples;
312    StringToAnnexB(test_cases[i], &buf, NULL);
313    EXPECT_TRUE(AVC::IsValidAnnexB(buf, subsamples)) << "'" << test_cases[i]
314                                                     << "' failed";
315  }
316}
317
318TEST_F(AVCConversionTest, InvalidAnnexBConstructs) {
319  static const char* test_cases[] = {
320    "AUD",  // No VCL present.
321    "SPS PPS",  // No VCL present.
322    "SPS PPS AUD I",  // Parameter sets must come after AUD.
323    "SPSExt SPS P",  // SPS must come before SPSExt.
324    "SPS PPS SPSExt P",  // SPSExt must follow an SPS.
325    "EOSeq",  // EOSeq must come after a VCL.
326    "EOStr",  // EOStr must come after a VCL.
327    "I EOStr EOSeq",  // EOSeq must come before EOStr.
328    "I R14",  // Reserved14-18 must come before first VCL.
329    "I SEI",  // SEI must come before first VCL.
330    "P SPS P", // SPS after first VCL would indicate a new access unit.
331  };
332
333  for (size_t i = 0; i < arraysize(test_cases); ++i) {
334    std::vector<uint8> buf;
335    std::vector<SubsampleEntry> subsamples;
336    StringToAnnexB(test_cases[i], &buf, NULL);
337    EXPECT_FALSE(AVC::IsValidAnnexB(buf, subsamples)) << "'" << test_cases[i]
338                                                      << "' failed";
339  }
340}
341
342typedef struct {
343  const char* input;
344  const char* expected;
345} InsertTestCases;
346
347TEST_F(AVCConversionTest, InsertParamSetsAnnexB) {
348  static const InsertTestCases test_cases[] = {
349    { "I", "SPS SPS PPS I" },
350    { "AUD I", "AUD SPS SPS PPS I" },
351
352    // Cases where param sets in |avc_config| are placed before
353    // the existing ones.
354    { "SPS PPS I", "SPS SPS PPS SPS PPS I" },
355    { "AUD SPS PPS I", "AUD SPS SPS PPS SPS PPS I" },  // Note: params placed
356                                                       // after AUD.
357  };
358
359  AVCDecoderConfigurationRecord avc_config;
360  avc_config.sps_list.resize(2);
361  avc_config.sps_list[0].push_back(0x67);
362  avc_config.sps_list[0].push_back(0x12);
363  avc_config.sps_list[1].push_back(0x67);
364  avc_config.sps_list[1].push_back(0x34);
365  avc_config.pps_list.resize(1);
366  avc_config.pps_list[0].push_back(0x68);
367  avc_config.pps_list[0].push_back(0x56);
368  avc_config.pps_list[0].push_back(0x78);
369
370  for (size_t i = 0; i < arraysize(test_cases); ++i) {
371    std::vector<uint8> buf;
372    std::vector<SubsampleEntry> subsamples;
373
374    StringToAnnexB(test_cases[i].input, &buf, &subsamples);
375
376    EXPECT_TRUE(AVC::InsertParamSetsAnnexB(avc_config, &buf, &subsamples))
377        << "'" << test_cases[i].input << "' insert failed.";
378    EXPECT_TRUE(AVC::IsValidAnnexB(buf, subsamples))
379        << "'" << test_cases[i].input << "' created invalid AnnexB.";
380    EXPECT_EQ(test_cases[i].expected, AnnexBToString(buf, subsamples))
381        << "'" << test_cases[i].input << "' generated unexpected output.";
382  }
383}
384
385}  // namespace mp4
386}  // namespace media
387