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 <sstream>
6#include <string>
7#include <vector>
8
9#include "base/bind.h"
10#include "base/logging.h"
11#include "base/strings/string_util.h"
12#include "base/time/time.h"
13#include "media/base/stream_parser_buffer.h"
14#include "media/filters/h264_parser.h"
15#include "media/formats/mp2t/es_parser_h264.h"
16#include "media/formats/mp2t/es_parser_test_base.h"
17#include "testing/gtest/include/gtest/gtest.h"
18
19namespace media {
20class VideoDecoderConfig;
21
22namespace mp2t {
23
24class EsParserH264Test : public EsParserTestBase,
25                         public testing::Test {
26 public:
27  EsParserH264Test() {}
28  virtual ~EsParserH264Test() {}
29
30 protected:
31  void LoadH264Stream(const char* filename);
32  void GetPesTimestamps(std::vector<Packet>* pes_packets);
33  bool Process(const std::vector<Packet>& pes_packets, bool force_timing);
34  void CheckAccessUnits();
35
36  // Access units of the stream with AUD NALUs.
37  std::vector<Packet> access_units_;
38
39 private:
40  // Get the offset of the start of each access unit of |stream_|.
41  // This function assumes there is only one slice per access unit.
42  // This is a very simplified access unit segmenter that is good
43  // enough for unit tests.
44  void GetAccessUnits();
45
46  // Insert an AUD before each access unit.
47  // Update |stream_| and |access_units_| accordingly.
48  void InsertAUD();
49
50  DISALLOW_COPY_AND_ASSIGN(EsParserH264Test);
51};
52
53void EsParserH264Test::LoadH264Stream(const char* filename) {
54  // Load the input H264 file and segment it into access units.
55  LoadStream(filename);
56  GetAccessUnits();
57  ASSERT_GT(access_units_.size(), 0u);
58
59  // Insert AUDs into the stream.
60  InsertAUD();
61
62  // Generate some timestamps based on a 25fps stream.
63  for (size_t k = 0; k < access_units_.size(); k++)
64    access_units_[k].pts = base::TimeDelta::FromMilliseconds(k * 40u);
65}
66
67void EsParserH264Test::GetAccessUnits() {
68  access_units_.resize(0);
69  bool start_access_unit = true;
70
71  // In a first pass, retrieve the offsets of all access units.
72  size_t offset = 0;
73  while (true) {
74    // Find the next start code.
75    off_t relative_offset = 0;
76    off_t start_code_size = 0;
77    bool success = H264Parser::FindStartCode(
78        &stream_[offset], stream_.size() - offset,
79        &relative_offset, &start_code_size);
80    if (!success)
81      break;
82    offset += relative_offset;
83
84    if (start_access_unit) {
85      Packet cur_access_unit;
86      cur_access_unit.offset = offset;
87      access_units_.push_back(cur_access_unit);
88      start_access_unit = false;
89    }
90
91    // Get the NALU type.
92    offset += start_code_size;
93    if (offset >= stream_.size())
94      break;
95    int nal_unit_type = stream_[offset] & 0x1f;
96
97    // We assume there is only one slice per access unit.
98    if (nal_unit_type == H264NALU::kIDRSlice ||
99        nal_unit_type == H264NALU::kNonIDRSlice) {
100      start_access_unit = true;
101    }
102  }
103
104  ComputePacketSize(&access_units_);
105}
106
107void EsParserH264Test::InsertAUD() {
108  uint8 aud[] = { 0x00, 0x00, 0x01, 0x09 };
109
110  std::vector<uint8> stream_with_aud(
111      stream_.size() + access_units_.size() * sizeof(aud));
112  std::vector<EsParserTestBase::Packet> access_units_with_aud(
113      access_units_.size());
114
115  size_t offset = 0;
116  for (size_t k = 0; k < access_units_.size(); k++) {
117    access_units_with_aud[k].offset = offset;
118    access_units_with_aud[k].size = access_units_[k].size + sizeof(aud);
119
120    memcpy(&stream_with_aud[offset], aud, sizeof(aud));
121    offset += sizeof(aud);
122
123    memcpy(&stream_with_aud[offset],
124           &stream_[access_units_[k].offset], access_units_[k].size);
125    offset += access_units_[k].size;
126  }
127
128  // Update the stream and access units used for the test.
129  stream_ = stream_with_aud;
130  access_units_ = access_units_with_aud;
131}
132
133void EsParserH264Test::GetPesTimestamps(std::vector<Packet>* pes_packets_ptr) {
134  DCHECK(pes_packets_ptr);
135  const std::vector<Packet>& pes_packets = *pes_packets_ptr;
136
137  // Default: set to a negative timestamp to be able to differentiate from
138  // real timestamps.
139  // Note: we don't use kNoTimestamp() here since this one has already
140  // a special meaning in EsParserH264. The negative timestamps should be
141  // ultimately discarded by the H264 parser since not relevant.
142  for (size_t k = 0; k < pes_packets.size(); k++) {
143    (*pes_packets_ptr)[k].pts = base::TimeDelta::FromMilliseconds(-1);
144  }
145
146  // Set a valid timestamp for PES packets which include the start
147  // of an H264 access unit.
148  size_t pes_idx = 0;
149  for (size_t k = 0; k < access_units_.size(); k++) {
150    for (; pes_idx < pes_packets.size(); pes_idx++) {
151      size_t pes_start = pes_packets[pes_idx].offset;
152      size_t pes_end = pes_packets[pes_idx].offset + pes_packets[pes_idx].size;
153      if (pes_start <= access_units_[k].offset &&
154          pes_end > access_units_[k].offset) {
155        (*pes_packets_ptr)[pes_idx].pts = access_units_[k].pts;
156        break;
157      }
158    }
159  }
160}
161
162bool EsParserH264Test::Process(
163    const std::vector<Packet>& pes_packets,
164    bool force_timing) {
165  EsParserH264 es_parser(
166      base::Bind(&EsParserH264Test::NewVideoConfig, base::Unretained(this)),
167      base::Bind(&EsParserH264Test::EmitBuffer, base::Unretained(this)));
168  return ProcessPesPackets(&es_parser, pes_packets, force_timing);
169}
170
171void EsParserH264Test::CheckAccessUnits() {
172  EXPECT_EQ(buffer_count_, access_units_.size());
173
174  std::stringstream buffer_timestamps_stream;
175  for (size_t k = 0; k < access_units_.size(); k++) {
176    buffer_timestamps_stream << "("
177                             << access_units_[k].pts.InMilliseconds()
178                             << ") ";
179  }
180  std::string buffer_timestamps = buffer_timestamps_stream.str();
181  base::TrimWhitespaceASCII(
182      buffer_timestamps, base::TRIM_ALL, &buffer_timestamps);
183  EXPECT_EQ(buffer_timestamps_, buffer_timestamps);
184}
185
186TEST_F(EsParserH264Test, OneAccessUnitPerPes) {
187  LoadH264Stream("bear.h264");
188
189  // One to one equivalence between PES packets and access units.
190  std::vector<Packet> pes_packets(access_units_);
191  GetPesTimestamps(&pes_packets);
192
193  // Process each PES packet.
194  EXPECT_TRUE(Process(pes_packets, false));
195  CheckAccessUnits();
196}
197
198TEST_F(EsParserH264Test, NonAlignedPesPacket) {
199  LoadH264Stream("bear.h264");
200
201  // Generate the PES packets.
202  std::vector<Packet> pes_packets;
203  Packet cur_pes_packet;
204  cur_pes_packet.offset = 0;
205  for (size_t k = 0; k < access_units_.size(); k++) {
206    pes_packets.push_back(cur_pes_packet);
207
208    // The current PES packet includes the remaining bytes of the previous
209    // access unit and some bytes of the current access unit
210    // (487 bytes in this unit test but no more than the current access unit
211    // size).
212    cur_pes_packet.offset = access_units_[k].offset +
213        std::min<size_t>(487u, access_units_[k].size);
214  }
215  ComputePacketSize(&pes_packets);
216  GetPesTimestamps(&pes_packets);
217
218  // Process each PES packet.
219  EXPECT_TRUE(Process(pes_packets, false));
220  CheckAccessUnits();
221}
222
223TEST_F(EsParserH264Test, SeveralPesPerAccessUnit) {
224  LoadH264Stream("bear.h264");
225
226  // Get the minimum size of an access unit.
227  size_t min_access_unit_size = stream_.size();
228  for (size_t k = 0; k < access_units_.size(); k++) {
229    if (min_access_unit_size >= access_units_[k].size)
230      min_access_unit_size = access_units_[k].size;
231  }
232
233  // Use a small PES packet size or the minimum access unit size
234  // if it is even smaller.
235  size_t pes_size = 512;
236  if (min_access_unit_size < pes_size)
237    pes_size = min_access_unit_size;
238
239  std::vector<Packet> pes_packets;
240  Packet cur_pes_packet;
241  cur_pes_packet.offset = 0;
242  while (cur_pes_packet.offset < stream_.size()) {
243    pes_packets.push_back(cur_pes_packet);
244    cur_pes_packet.offset += pes_size;
245  }
246  ComputePacketSize(&pes_packets);
247  GetPesTimestamps(&pes_packets);
248
249  // Process each PES packet.
250  EXPECT_TRUE(Process(pes_packets, false));
251  CheckAccessUnits();
252
253  // Process PES packets forcing timings for each PES packet.
254  EXPECT_TRUE(Process(pes_packets, true));
255  CheckAccessUnits();
256}
257
258}  // namespace mp2t
259}  // namespace media
260