Shaka Packager SDK
mp2t_media_parser.cc
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 "packager/media/formats/mp2t/mp2t_media_parser.h"
6 
7 #include <memory>
8 #include "packager/base/bind.h"
9 #include "packager/media/base/media_sample.h"
10 #include "packager/media/base/stream_info.h"
11 #include "packager/media/formats/mp2t/es_parser.h"
12 #include "packager/media/formats/mp2t/es_parser_audio.h"
13 #include "packager/media/formats/mp2t/es_parser_h264.h"
14 #include "packager/media/formats/mp2t/es_parser_h265.h"
15 #include "packager/media/formats/mp2t/mp2t_common.h"
16 #include "packager/media/formats/mp2t/ts_packet.h"
17 #include "packager/media/formats/mp2t/ts_section.h"
18 #include "packager/media/formats/mp2t/ts_section_pat.h"
19 #include "packager/media/formats/mp2t/ts_section_pes.h"
20 #include "packager/media/formats/mp2t/ts_section_pmt.h"
21 #include "packager/media/formats/mp2t/ts_stream_type.h"
22 
23 namespace shaka {
24 namespace media {
25 namespace mp2t {
26 
27 class PidState {
28  public:
29  enum PidType {
30  kPidPat,
31  kPidPmt,
32  kPidAudioPes,
33  kPidVideoPes,
34  };
35 
36  PidState(int pid,
37  PidType pid_type,
38  std::unique_ptr<TsSection> section_parser);
39 
40  // Extract the content of the TS packet and parse it.
41  // Return true if successful.
42  bool PushTsPacket(const TsPacket& ts_packet);
43 
44  // Flush the PID state (possibly emitting some pending frames)
45  // and reset its state.
46  void Flush();
47 
48  // Enable/disable the PID.
49  // Disabling a PID will reset its state and ignore any further incoming TS
50  // packets.
51  void Enable();
52  void Disable();
53  bool IsEnabled() const;
54 
55  PidType pid_type() const { return pid_type_; }
56 
57  std::shared_ptr<StreamInfo>& config() { return config_; }
58  void set_config(const std::shared_ptr<StreamInfo>& config) {
59  config_ = config;
60  }
61 
62  SampleQueue& sample_queue() { return sample_queue_; }
63 
64  private:
65  void ResetState();
66 
67  int pid_;
68  PidType pid_type_;
69  std::unique_ptr<TsSection> section_parser_;
70 
71  bool enable_;
72  int continuity_counter_;
73  std::shared_ptr<StreamInfo> config_;
74  SampleQueue sample_queue_;
75 };
76 
77 PidState::PidState(int pid,
78  PidType pid_type,
79  std::unique_ptr<TsSection> section_parser)
80  : pid_(pid),
81  pid_type_(pid_type),
82  section_parser_(std::move(section_parser)),
83  enable_(false),
84  continuity_counter_(-1) {
85  DCHECK(section_parser_);
86 }
87 
88 bool PidState::PushTsPacket(const TsPacket& ts_packet) {
89  DCHECK_EQ(ts_packet.pid(), pid_);
90 
91  // The current PID is not part of the PID filter,
92  // just discard the incoming TS packet.
93  if (!enable_)
94  return true;
95 
96  int expected_continuity_counter = (continuity_counter_ + 1) % 16;
97  if (continuity_counter_ >= 0 &&
98  ts_packet.continuity_counter() != expected_continuity_counter) {
99  DVLOG(1) << "TS discontinuity detected for pid: " << pid_;
100  // TODO(tinskip): Handle discontinuity better.
101  return false;
102  }
103 
104  bool status = section_parser_->Parse(
105  ts_packet.payload_unit_start_indicator(),
106  ts_packet.payload(),
107  ts_packet.payload_size());
108 
109  // At the minimum, when parsing failed, auto reset the section parser.
110  // Components that use the Mp2tMediaParser can take further action if needed.
111  if (!status) {
112  DVLOG(1) << "Parsing failed for pid = " << pid_;
113  ResetState();
114  }
115 
116  return status;
117 }
118 
119 void PidState::Flush() {
120  section_parser_->Flush();
121  ResetState();
122 }
123 
124 void PidState::Enable() {
125  enable_ = true;
126 }
127 
128 void PidState::Disable() {
129  if (!enable_)
130  return;
131 
132  ResetState();
133  enable_ = false;
134 }
135 
136 bool PidState::IsEnabled() const {
137  return enable_;
138 }
139 
140 void PidState::ResetState() {
141  section_parser_->Reset();
142  continuity_counter_ = -1;
143 }
144 
145 Mp2tMediaParser::Mp2tMediaParser()
146  : sbr_in_mimetype_(false),
147  is_initialized_(false) {
148 }
149 
150 Mp2tMediaParser::~Mp2tMediaParser() {}
151 
153  const InitCB& init_cb,
154  const NewSampleCB& new_sample_cb,
155  KeySource* decryption_key_source) {
156  DCHECK(!is_initialized_);
157  DCHECK(init_cb_.is_null());
158  DCHECK(!init_cb.is_null());
159  DCHECK(!new_sample_cb.is_null());
160 
161  init_cb_ = init_cb;
162  new_sample_cb_ = new_sample_cb;
163 }
164 
166  DVLOG(1) << "Mp2tMediaParser::Flush";
167 
168  // Flush the buffers and reset the pids.
169  for (const auto& pair : pids_) {
170  DVLOG(1) << "Flushing PID: " << pair.first;
171  PidState* pid_state = pair.second.get();
172  pid_state->Flush();
173  }
174  bool result = EmitRemainingSamples();
175  pids_.clear();
176 
177  // Remove any bytes left in the TS buffer.
178  // (i.e. any partial TS packet => less than 188 bytes).
179  ts_byte_queue_.Reset();
180  return result;
181 }
182 
183 bool Mp2tMediaParser::Parse(const uint8_t* buf, int size) {
184  DVLOG(1) << "Mp2tMediaParser::Parse size=" << size;
185 
186  // Add the data to the parser state.
187  ts_byte_queue_.Push(buf, size);
188 
189  while (true) {
190  const uint8_t* ts_buffer;
191  int ts_buffer_size;
192  ts_byte_queue_.Peek(&ts_buffer, &ts_buffer_size);
193  if (ts_buffer_size < TsPacket::kPacketSize)
194  break;
195 
196  // Synchronization.
197  int skipped_bytes = TsPacket::Sync(ts_buffer, ts_buffer_size);
198  if (skipped_bytes > 0) {
199  DVLOG(1) << "Packet not aligned on a TS syncword:"
200  << " skipped_bytes=" << skipped_bytes;
201  ts_byte_queue_.Pop(skipped_bytes);
202  continue;
203  }
204 
205  // Parse the TS header, skipping 1 byte if the header is invalid.
206  std::unique_ptr<TsPacket> ts_packet(
207  TsPacket::Parse(ts_buffer, ts_buffer_size));
208  if (!ts_packet) {
209  DVLOG(1) << "Error: invalid TS packet";
210  ts_byte_queue_.Pop(1);
211  continue;
212  }
213  DVLOG(LOG_LEVEL_TS)
214  << "Processing PID=" << ts_packet->pid()
215  << " start_unit=" << ts_packet->payload_unit_start_indicator();
216 
217  // Parse the section.
218  std::map<int, std::unique_ptr<PidState>>::iterator it =
219  pids_.find(ts_packet->pid());
220  if (it == pids_.end() &&
221  ts_packet->pid() == TsSection::kPidPat) {
222  // Create the PAT state here if needed.
223  std::unique_ptr<TsSection> pat_section_parser(new TsSectionPat(
224  base::Bind(&Mp2tMediaParser::RegisterPmt, base::Unretained(this))));
225  std::unique_ptr<PidState> pat_pid_state(new PidState(
226  ts_packet->pid(), PidState::kPidPat, std::move(pat_section_parser)));
227  pat_pid_state->Enable();
228  it = pids_
229  .insert(std::pair<int, std::unique_ptr<PidState>>(
230  ts_packet->pid(), std::move(pat_pid_state)))
231  .first;
232  }
233 
234  if (it != pids_.end()) {
235  if (!it->second->PushTsPacket(*ts_packet))
236  return false;
237  } else {
238  DVLOG(LOG_LEVEL_TS) << "Ignoring TS packet for pid: " << ts_packet->pid();
239  }
240 
241  // Go to the next packet.
242  ts_byte_queue_.Pop(TsPacket::kPacketSize);
243  }
244 
245  // Emit the A/V buffers that kept accumulating during TS parsing.
246  return EmitRemainingSamples();
247 }
248 
249 void Mp2tMediaParser::RegisterPmt(int program_number, int pmt_pid) {
250  DVLOG(1) << "RegisterPmt:"
251  << " program_number=" << program_number
252  << " pmt_pid=" << pmt_pid;
253 
254  // Only one TS program is allowed. Ignore the incoming program map table,
255  // if there is already one registered.
256  for (const auto& pair : pids_) {
257  if (pair.second->pid_type() == PidState::kPidPmt) {
258  DVLOG_IF(1, pmt_pid != pair.first) << "More than one program is defined";
259  return;
260  }
261  }
262 
263  // Create the PMT state here if needed.
264  DVLOG(1) << "Create a new PMT parser";
265  std::unique_ptr<TsSection> pmt_section_parser(new TsSectionPmt(base::Bind(
266  &Mp2tMediaParser::RegisterPes, base::Unretained(this), pmt_pid)));
267  std::unique_ptr<PidState> pmt_pid_state(
268  new PidState(pmt_pid, PidState::kPidPmt, std::move(pmt_section_parser)));
269  pmt_pid_state->Enable();
270  pids_.insert(std::pair<int, std::unique_ptr<PidState>>(
271  pmt_pid, std::move(pmt_pid_state)));
272 }
273 
274 void Mp2tMediaParser::RegisterPes(int pmt_pid,
275  int pes_pid,
276  int stream_type) {
277  DVLOG(1) << "RegisterPes:"
278  << " pes_pid=" << pes_pid
279  << " stream_type=" << std::hex << stream_type << std::dec;
280  std::map<int, std::unique_ptr<PidState>>::iterator it = pids_.find(pes_pid);
281  if (it != pids_.end())
282  return;
283 
284  // Create a stream parser corresponding to the stream type.
285  bool is_audio = false;
286  std::unique_ptr<EsParser> es_parser;
287  switch (static_cast<TsStreamType>(stream_type)) {
288  case TsStreamType::kAvc:
289  es_parser.reset(new EsParserH264(
290  pes_pid,
291  base::Bind(&Mp2tMediaParser::OnNewStreamInfo, base::Unretained(this)),
292  base::Bind(&Mp2tMediaParser::OnEmitSample, base::Unretained(this))));
293  break;
294  case TsStreamType::kHevc:
295  es_parser.reset(new EsParserH265(
296  pes_pid,
297  base::Bind(&Mp2tMediaParser::OnNewStreamInfo, base::Unretained(this)),
298  base::Bind(&Mp2tMediaParser::OnEmitSample, base::Unretained(this))));
299  break;
300  case TsStreamType::kAdtsAac:
301  case TsStreamType::kAc3:
302  es_parser.reset(new EsParserAudio(
303  pes_pid, static_cast<TsStreamType>(stream_type),
304  base::Bind(&Mp2tMediaParser::OnNewStreamInfo, base::Unretained(this)),
305  base::Bind(&Mp2tMediaParser::OnEmitSample, base::Unretained(this)),
306  sbr_in_mimetype_));
307  is_audio = true;
308  break;
309  default: {
310  LOG_IF(ERROR, !stream_type_logged_once_[stream_type])
311  << "Ignore unsupported MPEG2TS stream type 0x" << std::hex
312  << stream_type << std::dec;
313  stream_type_logged_once_[stream_type] = true;
314  return;
315  }
316  }
317 
318  // Create the PES state here.
319  DVLOG(1) << "Create a new PES state";
320  std::unique_ptr<TsSection> pes_section_parser(
321  new TsSectionPes(std::move(es_parser)));
322  PidState::PidType pid_type =
323  is_audio ? PidState::kPidAudioPes : PidState::kPidVideoPes;
324  std::unique_ptr<PidState> pes_pid_state(
325  new PidState(pes_pid, pid_type, std::move(pes_section_parser)));
326  pes_pid_state->Enable();
327  pids_.insert(std::pair<int, std::unique_ptr<PidState>>(
328  pes_pid, std::move(pes_pid_state)));
329 }
330 
331 void Mp2tMediaParser::OnNewStreamInfo(
332  const std::shared_ptr<StreamInfo>& new_stream_info) {
333  DCHECK(new_stream_info);
334  DVLOG(1) << "OnVideoConfigChanged for pid=" << new_stream_info->track_id();
335 
336  PidMap::iterator pid_state = pids_.find(new_stream_info->track_id());
337  if (pid_state == pids_.end()) {
338  LOG(ERROR) << "PID State for new stream not found (pid = "
339  << new_stream_info->track_id() << ").";
340  return;
341  }
342 
343  // Set the stream configuration information for the PID.
344  pid_state->second->set_config(new_stream_info);
345 
346  // Finish initialization if all streams have configs.
347  FinishInitializationIfNeeded();
348 }
349 
350 bool Mp2tMediaParser::FinishInitializationIfNeeded() {
351  // Nothing to be done if already initialized.
352  if (is_initialized_)
353  return true;
354 
355  // Wait for more data to come to finish initialization.
356  if (pids_.empty())
357  return true;
358 
359  std::vector<std::shared_ptr<StreamInfo>> all_stream_info;
360  uint32_t num_es(0);
361  for (PidMap::const_iterator iter = pids_.begin(); iter != pids_.end();
362  ++iter) {
363  if (((iter->second->pid_type() == PidState::kPidAudioPes) ||
364  (iter->second->pid_type() == PidState::kPidVideoPes))) {
365  ++num_es;
366  if (iter->second->config())
367  all_stream_info.push_back(iter->second->config());
368  }
369  }
370  if (num_es && (all_stream_info.size() == num_es)) {
371  // All stream configurations have been received. Initialization can
372  // be completed.
373  init_cb_.Run(all_stream_info);
374  DVLOG(1) << "Mpeg2TS stream parser initialization done";
375  is_initialized_ = true;
376  }
377  return true;
378 }
379 
380 void Mp2tMediaParser::OnEmitSample(
381  uint32_t pes_pid,
382  const std::shared_ptr<MediaSample>& new_sample) {
383  DCHECK(new_sample);
384  DVLOG(LOG_LEVEL_ES)
385  << "OnEmitSample: "
386  << " pid="
387  << pes_pid
388  << " size="
389  << new_sample->data_size()
390  << " dts="
391  << new_sample->dts()
392  << " pts="
393  << new_sample->pts();
394 
395  // Add the sample to the appropriate PID sample queue.
396  PidMap::iterator pid_state = pids_.find(pes_pid);
397  if (pid_state == pids_.end()) {
398  LOG(ERROR) << "PID State for new sample not found (pid = "
399  << pes_pid << ").";
400  return;
401  }
402  pid_state->second->sample_queue().push_back(new_sample);
403 }
404 
405 bool Mp2tMediaParser::EmitRemainingSamples() {
406  DVLOG(LOG_LEVEL_ES) << "Mp2tMediaParser::EmitRemainingBuffers";
407 
408  // No buffer should be sent until fully initialized.
409  if (!is_initialized_)
410  return true;
411 
412  // Buffer emission.
413  for (PidMap::const_iterator pid_iter = pids_.begin(); pid_iter != pids_.end();
414  ++pid_iter) {
415  SampleQueue& sample_queue = pid_iter->second->sample_queue();
416  for (SampleQueue::iterator sample_iter = sample_queue.begin();
417  sample_iter != sample_queue.end();
418  ++sample_iter) {
419  if (!new_sample_cb_.Run(pid_iter->first, *sample_iter)) {
420  // Error processing sample. Propagate error condition.
421  return false;
422  }
423  }
424  sample_queue.clear();
425  }
426 
427  return true;
428 }
429 
430 } // namespace mp2t
431 } // namespace media
432 } // namespace shaka
STL namespace.
void Init(const InitCB &init_cb, const NewSampleCB &new_sample_cb, KeySource *decryption_key_source) override
All the methods that are virtual are virtual for mocking.
base::Callback< void(const std::vector< std::shared_ptr< StreamInfo > > &stream_info)> InitCB
Definition: media_parser.h:34
bool Flush() override WARN_UNUSED_RESULT
base::Callback< bool(uint32_t track_id, const std::shared_ptr< MediaSample > &media_sample)> NewSampleCB
Definition: media_parser.h:43
KeySource is responsible for encryption key acquisition.
Definition: key_source.h:50
bool Parse(const uint8_t *buf, int size) override WARN_UNUSED_RESULT