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