Shaka Packager SDK
Loading...
Searching...
No Matches
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 <cstddef>
8#include <cstdint>
9#include <deque>
10#include <functional>
11#include <ios>
12#include <memory>
13#include <string>
14#include <utility>
15#include <vector>
16
17#include <absl/log/check.h>
18#include <absl/log/log.h>
19
20#include <packager/media/base/audio_stream_info.h>
21#include <packager/media/base/media_parser.h>
22#include <packager/media/base/media_sample.h>
23#include <packager/media/base/stream_info.h>
24#include <packager/media/base/text_sample.h>
25#include <packager/media/formats/mp2t/es_parser.h>
26#include <packager/media/formats/mp2t/es_parser_audio.h>
27#include <packager/media/formats/mp2t/es_parser_dvb.h>
28#include <packager/media/formats/mp2t/es_parser_h264.h>
29#include <packager/media/formats/mp2t/es_parser_h265.h>
30#include <packager/media/formats/mp2t/es_parser_teletext.h>
31#include <packager/media/formats/mp2t/mp2t_common.h>
32#include <packager/media/formats/mp2t/ts_audio_type.h>
33#include <packager/media/formats/mp2t/ts_packet.h>
34#include <packager/media/formats/mp2t/ts_section.h>
35#include <packager/media/formats/mp2t/ts_section_pat.h>
36#include <packager/media/formats/mp2t/ts_section_pes.h>
37#include <packager/media/formats/mp2t/ts_section_pmt.h>
38#include <packager/media/formats/mp2t/ts_stream_type.h>
39
40namespace shaka {
41namespace media {
42namespace mp2t {
43
44class PidState {
45 public:
46 enum PidType {
47 kPidPat,
48 kPidPmt,
49 kPidAudioPes,
50 kPidVideoPes,
51 kPidTextPes,
52 };
53
54 PidState(int pid,
55 PidType pid_type,
56 std::unique_ptr<TsSection> section_parser);
57
58 // Extract the content of the TS packet and parse it.
59 // Return true if successful.
60 bool PushTsPacket(const TsPacket& ts_packet);
61
62 // Flush the PID state (possibly emitting some pending frames)
63 // and reset its state.
64 bool Flush();
65
66 // Enable/disable the PID.
67 // Disabling a PID will reset its state and ignore any further incoming TS
68 // packets.
69 void Enable();
70 void Disable();
71 bool IsEnabled() const;
72
73 PidType pid_type() const { return pid_type_; }
74
75 std::shared_ptr<StreamInfo>& config() { return config_; }
76 void set_config(const std::shared_ptr<StreamInfo>& config) {
77 config_ = config;
78 }
79
80 private:
81 friend Mp2tMediaParser;
82 void ResetState();
83
84 int pid_;
85 PidType pid_type_;
86 std::unique_ptr<TsSection> section_parser_;
87
88 std::deque<std::shared_ptr<MediaSample>> media_sample_queue_;
89 std::deque<std::shared_ptr<TextSample>> text_sample_queue_;
90
91 bool enable_;
92 int continuity_counter_;
93 std::shared_ptr<StreamInfo> config_;
94};
95
96PidState::PidState(int pid,
97 PidType pid_type,
98 std::unique_ptr<TsSection> section_parser)
99 : pid_(pid),
100 pid_type_(pid_type),
101 section_parser_(std::move(section_parser)),
102 enable_(false),
103 continuity_counter_(-1) {
104 DCHECK(section_parser_);
105}
106
107bool PidState::PushTsPacket(const TsPacket& ts_packet) {
108 DCHECK_EQ(ts_packet.pid(), pid_);
109
110 // The current PID is not part of the PID filter,
111 // just discard the incoming TS packet.
112 if (!enable_)
113 return true;
114 // TODO(bzd): continuity_counter_ is never set
115 int expected_continuity_counter = (continuity_counter_ + 1) % 16;
116 if (continuity_counter_ >= 0 &&
117 ts_packet.continuity_counter() != expected_continuity_counter) {
118 LOG(ERROR) << "TS discontinuity detected for pid: " << pid_;
119 // TODO(tinskip): Handle discontinuity better.
120 return false;
121 }
122
123 bool status =
124 section_parser_->Parse(ts_packet.payload_unit_start_indicator(),
125 ts_packet.payload(), ts_packet.payload_size());
126
127 // At the minimum, when parsing failed, auto reset the section parser.
128 // Components that use the Mp2tMediaParser can take further action if needed.
129 if (!status) {
130 LOG(ERROR) << "Parsing failed for pid = " << pid_ << ", type=" << pid_type_;
131 ResetState();
132 }
133
134 return status;
135}
136
137bool PidState::Flush() {
138 RCHECK(section_parser_->Flush());
139 ResetState();
140 return true;
141}
142
143void PidState::Enable() {
144 enable_ = true;
145}
146
147void PidState::Disable() {
148 if (!enable_)
149 return;
150
151 ResetState();
152 enable_ = false;
153}
154
155bool PidState::IsEnabled() const {
156 return enable_;
157}
158
159void PidState::ResetState() {
160 section_parser_->Reset();
161 continuity_counter_ = -1;
162}
163
164Mp2tMediaParser::Mp2tMediaParser()
165 : sbr_in_mimetype_(false), is_initialized_(false) {}
166
167Mp2tMediaParser::~Mp2tMediaParser() {}
168
169void Mp2tMediaParser::Init(const InitCB& init_cb,
170 const NewMediaSampleCB& new_media_sample_cb,
171 const NewTextSampleCB& new_text_sample_cb,
172 KeySource* decryption_key_source) {
173 DCHECK(!is_initialized_);
174 DCHECK(init_cb_ == nullptr);
175 DCHECK(init_cb != nullptr);
176 DCHECK(new_media_sample_cb != nullptr);
177 DCHECK(new_text_sample_cb != nullptr);
178
179 init_cb_ = init_cb;
180 new_media_sample_cb_ = new_media_sample_cb;
181 new_text_sample_cb_ = new_text_sample_cb;
182}
183
184bool Mp2tMediaParser::Flush() {
185 DVLOG(1) << "Mp2tMediaParser::Flush";
186
187 // Flush the buffers and reset the pids.
188 for (const auto& pair : pids_) {
189 DVLOG(1) << "Flushing PID: " << pair.first;
190 PidState* pid_state = pair.second.get();
191 RCHECK(pid_state->Flush());
192 }
193 bool result = EmitRemainingSamples();
194 pids_.clear();
195
196 // Remove any bytes left in the TS buffer.
197 // (i.e. any partial TS packet => less than 188 bytes).
198 ts_byte_queue_.Reset();
199 return result;
200}
201
202bool Mp2tMediaParser::Parse(const uint8_t* buf, int size) {
203 DVLOG(2) << "Mp2tMediaParser::Parse size=" << size;
204
205 // Add the data to the parser state.
206 ts_byte_queue_.Push(buf, size);
207
208 while (true) {
209 const uint8_t* ts_buffer;
210 int ts_buffer_size;
211 ts_byte_queue_.Peek(&ts_buffer, &ts_buffer_size);
212 if (ts_buffer_size < TsPacket::kPacketSize)
213 break;
214
215 // Synchronization.
216 int skipped_bytes = TsPacket::Sync(ts_buffer, ts_buffer_size);
217 if (skipped_bytes > 0) {
218 DVLOG(1) << "Packet not aligned on a TS syncword:"
219 << " skipped_bytes=" << skipped_bytes;
220 ts_byte_queue_.Pop(skipped_bytes);
221 continue;
222 }
223
224 // Parse the TS header, skipping 1 byte if the header is invalid.
225 std::unique_ptr<TsPacket> ts_packet(
226 TsPacket::Parse(ts_buffer, ts_buffer_size));
227 if (!ts_packet) {
228 DVLOG(1) << "Error: invalid TS packet";
229 ts_byte_queue_.Pop(1);
230 continue;
231 }
232 DVLOG(LOG_LEVEL_TS) << "Processing PID=" << ts_packet->pid()
233 << " start_unit="
234 << ts_packet->payload_unit_start_indicator()
235 << " continuity_counter="
236 << ts_packet->continuity_counter();
237 // Parse the section.
238 auto it = pids_.find(ts_packet->pid());
239 if (it == pids_.end() && ts_packet->pid() == TsSection::kPidPat) {
240 // Create the PAT state here if needed.
241 std::unique_ptr<TsSection> pat_section_parser(new TsSectionPat(
242 std::bind(&Mp2tMediaParser::RegisterPmt, this, std::placeholders::_1,
243 std::placeholders::_2)));
244 std::unique_ptr<PidState> pat_pid_state(new PidState(
245 ts_packet->pid(), PidState::kPidPat, std::move(pat_section_parser)));
246 pat_pid_state->Enable();
247 it = pids_.emplace(ts_packet->pid(), std::move(pat_pid_state)).first;
248 }
249
250 if (it != pids_.end()) {
251 RCHECK(it->second->PushTsPacket(*ts_packet));
252 } else {
253 DVLOG(LOG_LEVEL_TS) << "Ignoring TS packet for pid: " << ts_packet->pid();
254 }
255
256 // Go to the next packet.
257 ts_byte_queue_.Pop(TsPacket::kPacketSize);
258 }
259
260 // Emit the A/V buffers that kept accumulating during TS parsing.
261 return EmitRemainingSamples();
262}
263
264void Mp2tMediaParser::RegisterPmt(int program_number, int pmt_pid) {
265 DVLOG(1) << "RegisterPmt:"
266 << " program_number=" << program_number << " pmt_pid=" << pmt_pid;
267
268 // Only one TS program is allowed. Ignore the incoming program map table,
269 // if there is already one registered.
270 for (const auto& pair : pids_) {
271 if (pair.second->pid_type() == PidState::kPidPmt) {
272 if (pmt_pid != pair.first) {
273 DVLOG(1) << "More than one program is defined";
274 }
275 return;
276 }
277 }
278
279 // Create the PMT state here if needed.
280 DVLOG(1) << "Create a new PMT parser";
281 std::unique_ptr<TsSection> pmt_section_parser(new TsSectionPmt(std::bind(
282 &Mp2tMediaParser::RegisterPes, this, pmt_pid, std::placeholders::_1,
283 std::placeholders::_2, std::placeholders::_3, std::placeholders::_4,
284 std::placeholders::_5, std::placeholders::_6, std::placeholders::_7)));
285 std::unique_ptr<PidState> pmt_pid_state(
286 new PidState(pmt_pid, PidState::kPidPmt, std::move(pmt_section_parser)));
287 pmt_pid_state->Enable();
288 pids_.emplace(pmt_pid, std::move(pmt_pid_state));
289}
290
291void Mp2tMediaParser::RegisterPes(int pmt_pid,
292 int pes_pid,
293 TsStreamType stream_type,
294 uint32_t max_bitrate,
295 const std::string& lang,
296 TsAudioType audio_type,
297 const uint8_t* descriptor,
298 size_t descriptor_length) {
299 if (pids_.count(pes_pid) != 0)
300 return;
301 DVLOG(1) << "RegisterPes:"
302 << " pes_pid=" << pes_pid << " stream_type=" << std::hex
303 << static_cast<int>(stream_type) << std::dec
304 << "max_bitrate=" << max_bitrate << " lang=" << lang
305 << "audio_type=" << std::hex << static_cast<int>(audio_type)
306 << std::dec;
307
308 // Create a stream parser corresponding to the stream type.
309 PidState::PidType pid_type = PidState::kPidVideoPes;
310 std::unique_ptr<EsParser> es_parser;
311 auto on_new_stream = std::bind(&Mp2tMediaParser::OnNewStreamInfo, this,
312 pes_pid, std::placeholders::_1);
313 auto on_emit_media = std::bind(&Mp2tMediaParser::OnEmitMediaSample, this,
314 pes_pid, std::placeholders::_1);
315 auto on_emit_text = std::bind(&Mp2tMediaParser::OnEmitTextSample, this,
316 pes_pid, std::placeholders::_1);
317 switch (stream_type) {
318 case TsStreamType::kAvc:
319 es_parser.reset(new EsParserH264(pes_pid, on_new_stream, on_emit_media));
320 break;
321 case TsStreamType::kHevc:
322 es_parser.reset(new EsParserH265(pes_pid, on_new_stream, on_emit_media));
323 break;
324 case TsStreamType::kAdtsAac:
325 case TsStreamType::kMpeg1Audio:
326 case TsStreamType::kAc3:
327 es_parser.reset(
328 new EsParserAudio(pes_pid, static_cast<TsStreamType>(stream_type),
329 on_new_stream, on_emit_media, sbr_in_mimetype_));
330 pid_type = PidState::kPidAudioPes;
331 break;
332 case TsStreamType::kDvbSubtitles:
333 es_parser.reset(new EsParserDvb(pes_pid, on_new_stream, on_emit_text,
334 descriptor, descriptor_length));
335 pid_type = PidState::kPidTextPes;
336 break;
337 case TsStreamType::kTeletextSubtitles:
338 es_parser.reset(new EsParserTeletext(pes_pid, on_new_stream, on_emit_text,
339 descriptor, descriptor_length));
340 pid_type = PidState::kPidTextPes;
341 break;
342
343 default: {
344 auto type = static_cast<int>(stream_type);
345 DCHECK(type <= 0xff);
346 LOG_IF(ERROR, !stream_type_logged_once_[type])
347 << "Ignore unsupported MPEG2TS stream type 0x" << std::hex << type
348 << std::dec;
349 stream_type_logged_once_[type] = true;
350 return;
351 }
352 }
353
354 // Create the PES state here.
355 DVLOG(1) << "Create a new PES state";
356 std::unique_ptr<TsSection> pes_section_parser(
357 new TsSectionPes(std::move(es_parser)));
358 std::unique_ptr<PidState> pes_pid_state(
359 new PidState(pes_pid, pid_type, std::move(pes_section_parser)));
360 pes_pid_state->Enable();
361 pids_.emplace(pes_pid, std::move(pes_pid_state));
362
363 // Store PES metadata.
364 pes_metadata_.insert(
365 std::make_pair(pes_pid, PesMetadata{max_bitrate, lang, audio_type}));
366
367 // Keep track of text pids
368 if (pid_type == PidState::kPidTextPes) {
369 text_pids_.insert(pes_pid);
370 }
371}
372
373void Mp2tMediaParser::OnNewStreamInfo(
374 uint32_t pes_pid,
375 std::shared_ptr<StreamInfo> new_stream_info) {
376 DCHECK(!new_stream_info || new_stream_info->track_id() == pes_pid);
377 DVLOG(1) << "OnVideoConfigChanged for pid=" << pes_pid
378 << ", has_info=" << (new_stream_info ? "true" : "false");
379
380 auto pid_state = pids_.find(pes_pid);
381 if (pid_state == pids_.end()) {
382 LOG(ERROR) << "PID State for new stream not found (pid = "
383 << new_stream_info->track_id() << ").";
384 return;
385 }
386
387 if (new_stream_info) {
388 // Set the stream configuration information for the PID.
389 auto pes_metadata = pes_metadata_.find(pes_pid);
390 DCHECK(pes_metadata != pes_metadata_.end());
391 if (!pes_metadata->second.language.empty())
392 new_stream_info->set_language(pes_metadata->second.language);
393 if (new_stream_info->stream_type() == kStreamAudio) {
394 auto* audio_info = static_cast<AudioStreamInfo*>(new_stream_info.get());
395 audio_info->set_max_bitrate(pes_metadata->second.max_bitrate);
396 // TODO(modernletter) Add some field for audio type to AudioStreamInfo
397 // and set here from audio_type
398 }
399
400 pid_state->second->set_config(new_stream_info);
401 } else {
402 LOG(WARNING) << "Ignoring unsupported stream with pid=" << pes_pid;
403 pid_state->second->Disable();
404 }
405
406 // Finish initialization if all streams have configs.
407 FinishInitializationIfNeeded();
408}
409
410bool Mp2tMediaParser::FinishInitializationIfNeeded() {
411 // Nothing to be done if already initialized.
412 if (is_initialized_)
413 return true;
414
415 // Wait for more data to come to finish initialization.
416 if (pids_.empty())
417 return true;
418
419 std::vector<std::shared_ptr<StreamInfo>> all_stream_info;
420 uint32_t num_es(0);
421 for (const auto& pair : pids_) {
422 if ((pair.second->pid_type() == PidState::kPidAudioPes ||
423 pair.second->pid_type() == PidState::kPidVideoPes ||
424 pair.second->pid_type() == PidState::kPidTextPes) &&
425 pair.second->IsEnabled()) {
426 ++num_es;
427 if (pair.second->config())
428 all_stream_info.push_back(pair.second->config());
429 }
430 }
431 if (num_es && (all_stream_info.size() == num_es)) {
432 // All stream configurations have been received. Initialization can
433 // be completed.
434 init_cb_(all_stream_info);
435 DVLOG(1) << "Mpeg2TS stream parser initialization done";
436 is_initialized_ = true;
437 }
438 return true;
439}
440
441void Mp2tMediaParser::OnEmitMediaSample(
442 uint32_t pes_pid,
443 std::shared_ptr<MediaSample> new_sample) {
444 DCHECK(new_sample);
445 DVLOG(LOG_LEVEL_ES) << "OnEmitMediaSample: "
446 << " pid=" << pes_pid
447 << " size=" << new_sample->data_size()
448 << " dts=" << new_sample->dts()
449 << " pts=" << new_sample->pts();
450
451 // Add the sample to the appropriate PID sample queue.
452 auto pid_state = pids_.find(pes_pid);
453 if (pid_state == pids_.end()) {
454 LOG(ERROR) << "PID State for new sample not found (pid = " << pes_pid
455 << ").";
456 return;
457 }
458
459 // Use video DTS (or PTS if DTS not available) for video streams
460 // Use audio PTS for audio streams
461 int64_t timestamp_for_heartbeat = new_sample->pts();
462 if (pid_state->second->pid_type() == PidState::kPidVideoPes) {
463 // For video, prefer DTS if available, otherwise use PTS
464 // DTS is <= PTS and typically not present if DTS == PTS.
465 timestamp_for_heartbeat = new_sample->dts();
466 if (timestamp_for_heartbeat == 0) {
467 timestamp_for_heartbeat = new_sample->pts();
468 }
469 }
470 // For audio and other streams, use PTS (default already set above)
471
472 update_biggest_pts(timestamp_for_heartbeat);
473 pid_state->second->media_sample_queue_.push_back(std::move(new_sample));
474}
475
476void Mp2tMediaParser::OnEmitTextSample(uint32_t pes_pid,
477 std::shared_ptr<TextSample> new_sample) {
478 DCHECK(new_sample);
479 DVLOG(LOG_LEVEL_ES) << "OnEmitTextSample: "
480 << " pid=" << pes_pid
481 << " start=" << new_sample->start_time();
482
483 // Add the sample to the appropriate PID sample queue.
484 auto pid_state = pids_.find(pes_pid);
485 if (pid_state == pids_.end()) {
486 LOG(ERROR) << "PID State for new sample not found (pid = " << pes_pid
487 << ").";
488 return;
489 }
490
491 // Don't remove heartbeats - they need to be emitted to trigger segment
492 // generation Even when real text cues arrive, heartbeats provide timing
493 // information for proper segment boundaries, especially for sparse teletext
494 // streams
495 pid_state->second->text_sample_queue_.push_back(std::move(new_sample));
496}
497
498bool Mp2tMediaParser::EmitRemainingSamples() {
499 DVLOG(LOG_LEVEL_ES) << "Mp2tMediaParser::EmitRemainingBuffers";
500
501 // No buffer should be sent until fully initialized.
502 if (!is_initialized_)
503 return true;
504
505 // Buffer emission.
506 for (const auto& pid_pair : pids_) {
507 for (auto sample : pid_pair.second->media_sample_queue_) {
508 RCHECK(new_media_sample_cb_(pid_pair.first, sample));
509 }
510 pid_pair.second->media_sample_queue_.clear();
511
512 DVLOG(2) << "EmitRemainingSamples: text_sample_queue_ size="
513 << pid_pair.second->text_sample_queue_.size();
514 for (auto sample : pid_pair.second->text_sample_queue_) {
515 DVLOG(2) << "Emitting text sample: role="
516 << static_cast<int>(sample->role())
517 << " pts=" << sample->start_time()
518 << " is_empty=" << sample->is_empty();
519 bool result = new_text_sample_cb_(pid_pair.first, sample);
520 DVLOG(3) << "new_text_sample_cb_ returned: " << result;
521 RCHECK(result);
522 }
523 pid_pair.second->text_sample_queue_.clear();
524 }
525
526 return true;
527}
528
529void Mp2tMediaParser::update_biggest_pts(int64_t pts) {
530 if (pts >= biggest_pts_ + 9000) { // 100ms larger than last biggest
531 biggest_pts_ = pts;
532 for (auto pid : text_pids_) {
533 auto pid_state = pids_.find(pid);
534 if (pid_state == pids_.end()) {
535 LOG(ERROR) << "PID State for new sample not found (text pid = " << pid
536 << " )";
537 continue;
538 }
539 TextSettings text_settings;
540 auto heartbeat = std::make_shared<TextSample>(
541 "", pts, pts, text_settings, TextFragment({}, ""),
542 TextSampleRole::kMediaHeartBeat);
543 // Set sub_stream_index to match the PID so heartbeats pass through
544 // sub-stream filtering
545 heartbeat->set_sub_stream_index(pid);
546 OnEmitTextSample(uint32_t(pid), heartbeat);
547 }
548 }
549}
550
551} // namespace mp2t
552} // namespace media
553} // namespace shaka
KeySource is responsible for encryption key acquisition.
Definition key_source.h:56
std::function< bool(uint32_t track_id, std::shared_ptr< MediaSample > media_sample)> NewMediaSampleCB
std::function< bool(uint32_t track_id, std::shared_ptr< TextSample > text_sample)> NewTextSampleCB
std::function< void(const std::vector< std::shared_ptr< StreamInfo > > &stream_info)> InitCB
All the methods that are virtual are virtual for mocking.