Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(469)

Side by Side Diff: media/mpeg2/mpeg2ts_stream_parser.cc

Issue 23566013: Mpeg2 TS stream parser for media source. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address comments from patch set #3 Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2013 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 "media/mpeg2/mpeg2ts_stream_parser.h"
6
7 #include "base/bind.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/stl_util.h"
10 #include "media/base/audio_decoder_config.h"
11 #include "media/base/buffers.h"
12 #include "media/base/stream_parser_buffer.h"
13 #include "media/base/video_decoder_config.h"
14 #include "media/mpeg2/es_parser.h"
15 #include "media/mpeg2/es_parser_adts.h"
16 #include "media/mpeg2/es_parser_h264.h"
17 #include "media/mpeg2/mpeg2ts_common.h"
18 #include "media/mpeg2/mpeg2ts_pat.h"
19 #include "media/mpeg2/mpeg2ts_pes.h"
20 #include "media/mpeg2/mpeg2ts_pmt.h"
21 #include "media/mpeg2/mpeg2ts_section_parser.h"
22 #include "media/mpeg2/ts_packet.h"
23
24 namespace {
25
26 enum StreamType {
damienv1 2013/09/10 04:10:02 Move to media::mpeg2ts namespace.
damienv1 2013/09/10 21:03:48 Done.
27 // ISO-13818.1 / ITU H.222 Table 2.34 "Stream type assignments"
28 kStreamTypeMpeg1Audio = 0x3,
29 kStreamTypeAAC = 0xf,
30 kStreamTypeAVC = 0x1b,
31 };
32
33 }
34
35 namespace media {
36 namespace mpeg2ts {
37
38 class PidState {
39 public:
40 enum PidType {
41 kPidPat,
42 kPidPmt,
43 kPidAudioPes,
44 kPidVideoPes,
45 };
46
47 PidState(int pid, PidType pid_tyoe,
48 scoped_ptr<Mpeg2TsSectionParser> section_parser);
49
50 // Extract the content of the TS packet and parse it.
51 // Return true if successful.
52 bool PushTsPacket(TsPacket* ts_packet);
53
54 // Flush the PID state (possibly emitting some pending frames)
55 // and reset its state.
56 void Flush();
57
58 // Enable/disable the PID.
59 // Disabling a PID will reset its state and ignore any further incoming TS
60 // packets.
61 void Enable();
62 void Disable();
63 bool IsEnabled() const;
64
65 PidType pid_type() const { return pid_type_; }
66
67 private:
68 void ResetState();
69
70 int pid_;
71 PidType pid_type_;
72 scoped_ptr<Mpeg2TsSectionParser> section_parser_;
73
74 bool enable_;
75
76 int continuity_counter_;
77 };
78
79 PidState::PidState(int pid, PidType pid_type,
80 scoped_ptr<Mpeg2TsSectionParser> section_parser)
81 : pid_(pid),
82 pid_type_(pid_type),
83 section_parser_(section_parser.Pass()),
84 enable_(false),
85 continuity_counter_(-1) {
86 DCHECK(section_parser_);
87 }
88
89 bool PidState::PushTsPacket(TsPacket* ts_packet) {
90 DCHECK_EQ(ts_packet->pid(), pid_);
91
92 // The current PID is not part of the PID filter,
93 // just discard the incoming TS packet.
94 if (!enable_)
95 return true;
96
97 int expected_continuity_counter = (continuity_counter_ + 1) % 16;
98 if (continuity_counter_ >= 0 &&
99 ts_packet->continuity_counter() != expected_continuity_counter) {
100 DVLOG(1) << "TS discontinuity detected for pid: " << pid_;
101 return false;
102 }
103
104 bool status = section_parser_->Parse(
105 ts_packet->payload_unit_start_indicator(),
106 ts_packet->GetPayload(),
107 ts_packet->GetPayloadSize());
108
109 // At the minimum, when parsing failed, auto reset the section parser.
110 // Components that use the StreamParser 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 class Mpeg2TsStreamParser::AudioBufferWithConfig {
146 public:
147 scoped_refptr<StreamParserBuffer> buffer;
148 AudioDecoderConfig config;
149 };
150
151 class Mpeg2TsStreamParser::VideoBufferWithConfig {
152 public:
153 scoped_refptr<StreamParserBuffer> buffer;
154 VideoDecoderConfig config;
155 };
156
157
158 Mpeg2TsStreamParser::Mpeg2TsStreamParser()
159 : selected_audio_pid_(-1),
160 selected_video_pid_(-1),
161 is_initialized_(false),
162 segment_started_(false) {
163 }
164
165 Mpeg2TsStreamParser::~Mpeg2TsStreamParser() {
166 STLDeleteValues(&pids_);
167 }
168
169 void Mpeg2TsStreamParser::Init(
170 const InitCB& init_cb,
171 const NewConfigCB& config_cb,
172 const NewBuffersCB& new_buffers_cb,
173 const NewTextBuffersCB& text_cb,
174 const NeedKeyCB& need_key_cb,
175 const AddTextTrackCB& add_text_track_cb,
176 const NewMediaSegmentCB& new_segment_cb,
177 const base::Closure& end_of_segment_cb,
178 const LogCB& log_cb) {
179 DCHECK(!is_initialized_);
180 DCHECK(init_cb_.is_null());
181 DCHECK(!init_cb.is_null());
182 DCHECK(!config_cb.is_null());
183 DCHECK(!new_buffers_cb.is_null());
184 DCHECK(!need_key_cb.is_null());
185 DCHECK(!end_of_segment_cb.is_null());
186
187 init_cb_ = init_cb;
188 config_cb_ = config_cb;
189 new_buffers_cb_ = new_buffers_cb;
190 need_key_cb_ = need_key_cb;
191 new_segment_cb_ = new_segment_cb;
192 end_of_segment_cb_ = end_of_segment_cb;
193 log_cb_ = log_cb;
194 }
195
196 void Mpeg2TsStreamParser::Flush() {
197 DVLOG(1) << "Mpeg2TsStreamParser::Flush";
198
199 // Flush the buffers and reset the pids.
200 for (std::map<int, PidState*>::iterator it = pids_.begin();
201 it != pids_.end(); ++it) {
202 DVLOG(1) << "Flushing PID: " << it->first;
203 PidState* pid_state = it->second;
204 pid_state->Flush();
205 delete pid_state;
206 }
207 pids_.clear();
208 if (is_initialized_) {
209 EmitRemainingBuffers();
210 DCHECK(audio_buffer_queue_.empty());
211 DCHECK(video_buffer_queue_.empty());
212 } else {
213 audio_buffer_queue_.clear();
214 video_buffer_queue_.clear();
215 }
216
217 // End of the segment.
218 // Note: does not need to invoke |end_of_segment_cb_| since flushing the
219 // stream parser already involves the end of the current segment.
220 segment_started_ = false;
221
222 // Remove any bytes left in the TS buffer.
223 // (i.e. any partial TS packet => less than 188 bytes).
224 ts_byte_queue_.Reset();
225
226 // Reset the selected PIDs.
227 selected_audio_pid_ = -1;
228 selected_video_pid_ = -1;
229
230 // Reset the audio and video configs.
231 audio_config_ = AudioDecoderConfig();
232 video_config_ = VideoDecoderConfig();
233 last_audio_config_ = AudioDecoderConfig();
234 last_video_config_ = VideoDecoderConfig();
235 }
236
237 bool Mpeg2TsStreamParser::Parse(const uint8* buf, int size) {
238 DVLOG(1) << "Mpeg2TsStreamParser::Parse size=" << size;
239
240 // Add the data to the parser state.
241 ts_byte_queue_.Push(buf, size);
242
243 while (true) {
244 const uint8* ts_buffer;
245 int ts_buffer_size;
246 ts_byte_queue_.Peek(&ts_buffer, &ts_buffer_size);
247 if (ts_buffer_size < TsPacket::kPacketSize)
248 break;
249
250 // Synchronization.
251 int skipped_bytes = TsPacket::Sync(ts_buffer, ts_buffer_size);
252 if (skipped_bytes > 0) {
253 DVLOG(1) << "Packet not aligned on a TS syncword:"
254 << " skipped_bytes=" << skipped_bytes;
255 ts_byte_queue_.Pop(skipped_bytes);
256 continue;
257 }
258
259 // Parse the TS header, skipping 1 byte if the header is invalid.
260 scoped_ptr<TsPacket> ts_packet(TsPacket::Parse(ts_buffer, ts_buffer_size));
261 if (!ts_packet) {
262 DVLOG(1) << "Error: invalid TS packet";
263 ts_byte_queue_.Pop(1);
264 continue;
265 }
266 DVLOG(LOG_LEVEL_TS)
267 << "Processing PID=" << ts_packet->pid()
268 << " start_unit=" << ts_packet->payload_unit_start_indicator();
269
270 // Parse the section.
271 std::map<int, PidState*>::iterator it = pids_.find(ts_packet->pid());
272 if (it == pids_.end() &&
273 ts_packet->pid() == Mpeg2TsSectionParser::kPidPat) {
274 // Create the PAT state here if needed.
275 scoped_ptr<Mpeg2TsSectionParser> pat_section_parser(
276 new Mpeg2TsPatParser(
277 base::Bind(&Mpeg2TsStreamParser::RegisterPmt,
278 base::Unretained(this))));
279 scoped_ptr<PidState> pat_pid_state(
280 new PidState(ts_packet->pid(), PidState::kPidPat,
281 pat_section_parser.Pass()));
282 pat_pid_state->Enable();
283 it = pids_.insert(
284 std::pair<int, PidState*>(ts_packet->pid(),
285 pat_pid_state.release())).first;
286 }
287
288 if (it != pids_.end()) {
289 bool status = it->second->PushTsPacket(ts_packet.get());
290 if (!status)
291 return false;
292 } else {
293 DVLOG(LOG_LEVEL_TS) << "Ignoring TS packet for pid: " << ts_packet->pid();
294 }
295
296 // Go to the next packet.
297 ts_byte_queue_.Pop(TsPacket::kPacketSize);
298 }
299
300 // Emit the A/V buffers that kept accumulating during TS parsing.
301 EmitRemainingBuffers();
302
303 return true;
304 }
305
306 void Mpeg2TsStreamParser::RegisterPmt(int program_number, int pmt_pid) {
307 DVLOG(1) << "RegisterPmt:"
308 << " program_number=" << program_number
309 << " pmt_pid=" << pmt_pid;
310
311 // Only one TS program is allowed. Ignore the incoming program map table,
312 // if there is already one registered.
313 for (std::map<int, PidState*>::iterator it = pids_.begin();
314 it != pids_.end(); ++it) {
315 PidState* pid_state = it->second;
316 if (pid_state->pid_type() == PidState::kPidPmt) {
317 int pid = it->first;
318 LOG_IF(WARNING, pmt_pid != pid) << "More than one program is defined";
319 return;
320 }
321 }
322
323 // Create the PMT state here if needed.
324 DVLOG(1) << "Create a new PMT parser";
325 scoped_ptr<Mpeg2TsSectionParser> pmt_section_parser(
326 new Mpeg2TsPmtParser(
327 base::Bind(&Mpeg2TsStreamParser::RegisterPes,
328 base::Unretained(this), pmt_pid)));
329 scoped_ptr<PidState> pmt_pid_state(
330 new PidState(pmt_pid, PidState::kPidPmt, pmt_section_parser.Pass()));
331 pmt_pid_state->Enable();
332 pids_.insert(std::pair<int, PidState*>(pmt_pid, pmt_pid_state.release()));
333 }
334
335 void Mpeg2TsStreamParser::RegisterPes(int pmt_pid,
336 int pes_pid,
337 int stream_type) {
338 // TODO(damienv): check there is no mismatch if the entry already exists.
339 DVLOG(1) << "RegisterPes:"
340 << " pes_pid=" << pes_pid
341 << " stream_type=" << std::hex << stream_type << std::dec;
342 std::map<int, PidState*>::iterator it = pids_.find(pes_pid);
343 if (it != pids_.end())
344 return;
345
346 // Create a stream parser corresponding to the stream type.
347 bool is_audio = false;
348 scoped_ptr<EsParser> es_parser;
349 if (stream_type == kStreamTypeAVC) {
350 es_parser.reset(
351 new EsParserH264(
352 base::Bind(&Mpeg2TsStreamParser::OnVideoConfigChanged,
353 base::Unretained(this),
354 pes_pid),
355 base::Bind(&Mpeg2TsStreamParser::OnEmitVideoBuffer,
356 base::Unretained(this),
357 pes_pid)));
358 } else if (stream_type == kStreamTypeAAC) {
359 es_parser.reset(
360 new EsParserAdts(
361 base::Bind(&Mpeg2TsStreamParser::OnAudioConfigChanged,
362 base::Unretained(this),
363 pes_pid),
364 base::Bind(&Mpeg2TsStreamParser::OnEmitAudioBuffer,
365 base::Unretained(this),
366 pes_pid)));
367 is_audio = true;
368 } else {
369 return;
370 }
371
372 // Create the PES state here.
373 DVLOG(1) << "Create a new PES state";
374 scoped_ptr<Mpeg2TsSectionParser> pes_section_parser(
375 new Mpeg2TsPesParser(es_parser.Pass()));
376 PidState::PidType pid_type =
377 is_audio ? PidState::kPidAudioPes : PidState::kPidVideoPes;
378 scoped_ptr<PidState> pes_pid_state(
379 new PidState(pes_pid, pid_type, pes_section_parser.Pass()));
380 pids_.insert(std::pair<int, PidState*>(pes_pid, pes_pid_state.release()));
381
382 // The pid filter must be updated.
383 UpdatePidFilter();
384 }
385
386 void Mpeg2TsStreamParser::UpdatePidFilter() {
387 // Applies the HLS rule to select the default audio/video PIDs:
388 // select the pid with the lowest PID.
389 // TODO(damienv): this can be changed when the StreamParser interface
390 // supports multiple audio/video streams.
391 std::map<int, PidState*>::iterator lowest_audio_pid = pids_.end();
392 std::map<int, PidState*>::iterator lowest_video_pid = pids_.end();
393 for (std::map<int, PidState*>::iterator it = pids_.begin();
394 it != pids_.end(); ++it) {
395 int pid = it->first;
396 PidState* pid_state = it->second;
397 if (pid_state->pid_type() == PidState::kPidAudioPes &&
398 ((lowest_audio_pid == pids_.end() || pid < lowest_audio_pid->first)))
399 lowest_audio_pid = it;
400 if (pid_state->pid_type() == PidState::kPidVideoPes &&
401 ((lowest_video_pid == pids_.end() || pid < lowest_video_pid->first)))
402 lowest_video_pid = it;
403 }
404
405 // Enable both the lowest audio and video PIDs.
406 if (lowest_audio_pid != pids_.end()) {
407 DVLOG(1) << "Enable audio pid: " << lowest_audio_pid->first;
408 lowest_audio_pid->second->Enable();
409 selected_audio_pid_ = lowest_audio_pid->first;
410 }
411 if (lowest_video_pid != pids_.end()) {
412 DVLOG(1) << "Enable video pid: " << lowest_audio_pid->first;
413 lowest_video_pid->second->Enable();
414 selected_video_pid_ = lowest_video_pid->first;
415 }
416
417 // Disable all the other audio and video PIDs.
418 for (std::map<int, PidState*>::iterator it = pids_.begin();
419 it != pids_.end(); ++it) {
420 PidState* pid_state = it->second;
421 if (it != lowest_audio_pid && it != lowest_video_pid &&
422 (pid_state->pid_type() == PidState::kPidAudioPes ||
423 pid_state->pid_type() == PidState::kPidVideoPes))
424 pid_state->Disable();
425 }
426 }
427
428 void Mpeg2TsStreamParser::OnVideoConfigChanged(
429 int pes_pid,
430 const VideoDecoderConfig& video_decoder_config) {
431 DVLOG(1) << "OnVideoConfigChanged for pid=" << pes_pid;
432 DCHECK_EQ(pes_pid, selected_video_pid_);
433
434 video_config_ = video_decoder_config;
435 FinishInitializationIfNeeded();
436 }
437
438 void Mpeg2TsStreamParser::OnAudioConfigChanged(
439 int pes_pid,
440 const AudioDecoderConfig& audio_decoder_config) {
441 DVLOG(1) << "OnAudioConfigChanged for pid=" << pes_pid;
442 DCHECK_EQ(pes_pid, selected_audio_pid_);
443
444 audio_config_ = audio_decoder_config;
445 FinishInitializationIfNeeded();
446 }
447
448 void Mpeg2TsStreamParser::FinishInitializationIfNeeded() {
449 // Nothing to be done if already initialized.
450 if (is_initialized_)
451 return;
452
453 // Initialization is done when both the audio decoder config
454 // and the video decoder config are known
455 // (for a stream with both audio and video).
456 if (selected_audio_pid_ > 0 && !audio_config_.IsValidConfig())
457 return;
458 if (selected_video_pid_ > 0 && !video_config_.IsValidConfig())
459 return;
460
461 // The audio and video decoder configs passed in the callback
462 // are the latest audio and video decoder configs.
463 // This might be different from the configs of the first audio and video
464 // buffer if we have a sequence like this one in the Mpeg2 TS stream:
465 // VConfigA VBuffer0 VBuffer1 VConfigB VBuffer2 AConfigA ABuffer0
466 // In this case, |audio_config_| corresponds to AConfigA
467 // and |video_config_| corresponds to VConfigB and not VConfigA.
468 // This does not matter since the callback will be invoked later before
469 // emitting any buffers and will thus overwrite the audio/video config.
470 config_cb_.Run(audio_config_, video_config_);
471
472 // For Mpeg2 TS, the duration is not known.
473 DVLOG(1) << "Mpeg2TS stream parser initialization done";
474 init_cb_.Run(true, kInfiniteDuration());
475 is_initialized_ = true;
476 }
477
478 void Mpeg2TsStreamParser::OnEmitAudioBuffer(
479 int pes_pid,
480 scoped_refptr<StreamParserBuffer> stream_parser_buffer) {
481 DCHECK_EQ(pes_pid, selected_audio_pid_);
482
483 DVLOG(LOG_LEVEL_ES)
484 << "OnEmitAudioBuffer: "
485 << " size="
486 << stream_parser_buffer->data_size()
487 << " dts="
488 << stream_parser_buffer->GetDecodeTimestamp().InMilliseconds()
489 << " pts="
490 << stream_parser_buffer->timestamp().InMilliseconds();
491 stream_parser_buffer->set_timestamp(
492 stream_parser_buffer->timestamp() - time_offset_);
493 stream_parser_buffer->SetDecodeTimestamp(
494 stream_parser_buffer->GetDecodeTimestamp() - time_offset_);
495
496 AudioBufferWithConfig audio_buffer_with_config;
497 audio_buffer_with_config.buffer = stream_parser_buffer;
498 audio_buffer_with_config.config = audio_config_;
499 audio_buffer_queue_.push_back(audio_buffer_with_config);
500 }
501
502 void Mpeg2TsStreamParser::OnEmitVideoBuffer(
503 int pes_pid,
504 scoped_refptr<StreamParserBuffer> stream_parser_buffer) {
505 DCHECK_EQ(pes_pid, selected_video_pid_);
506
507 DVLOG(LOG_LEVEL_ES)
508 << "OnEmitVideoBuffer"
509 << " size="
510 << stream_parser_buffer->data_size()
511 << " dts="
512 << stream_parser_buffer->GetDecodeTimestamp().InMilliseconds()
513 << " pts="
514 << stream_parser_buffer->timestamp().InMilliseconds()
515 << " IsKeyframe="
516 << stream_parser_buffer->IsKeyframe();
517 stream_parser_buffer->set_timestamp(
518 stream_parser_buffer->timestamp() - time_offset_);
519 stream_parser_buffer->SetDecodeTimestamp(
520 stream_parser_buffer->GetDecodeTimestamp() - time_offset_);
521
522 VideoBufferWithConfig video_buffer_with_config;
523 video_buffer_with_config.buffer = stream_parser_buffer;
524 video_buffer_with_config.config = video_config_;
525 video_buffer_queue_.push_back(video_buffer_with_config);
526 }
527
528 void Mpeg2TsStreamParser::EmitRemainingBuffers() {
529 DVLOG(LOG_LEVEL_ES) << "Mpeg2TsStreamParser::EmitRemainingBuffers";
530 if (!is_initialized_)
531 return;
532
533 while (!audio_buffer_queue_.empty())
534 EmitAudioBuffers();
damienv1 2013/09/10 21:03:48 Emitting audio and video buffers in 2 different pa
535
536 while (!video_buffer_queue_.empty())
537 EmitVideoBuffers();
538 }
539
540 void Mpeg2TsStreamParser::EmitAudioBuffers() {
541 DCHECK(!audio_buffer_queue_.empty());
542
543 AudioDecoderConfig audio_config = audio_buffer_queue_.front().config;
544
545 if (!segment_started_) {
546 DVLOG(1) << "Starting a new segment";
547 segment_started_ = true;
548 new_segment_cb_.Run();
549 }
550
551 if (!audio_config.Matches(last_audio_config_)) {
552 last_audio_config_ = audio_config;
553 config_cb_.Run(last_audio_config_, last_video_config_);
554 }
555
556 StreamParser::BufferQueue audio_queue;
557 StreamParser::BufferQueue video_queue;
558 while (!audio_buffer_queue_.empty() &&
559 audio_buffer_queue_.front().config.Matches(last_audio_config_)) {
560 audio_queue.push_back(audio_buffer_queue_.front().buffer);
561 audio_buffer_queue_.pop_front();
562 }
563 new_buffers_cb_.Run(audio_queue, video_queue);
564 }
565
566 void Mpeg2TsStreamParser::EmitVideoBuffers() {
567 DCHECK(!video_buffer_queue_.empty());
568
569 VideoDecoderConfig video_config = video_buffer_queue_.front().config;
570
571 if (!segment_started_) {
572 DVLOG(1) << "Starting a new segment";
573 segment_started_ = true;
574 new_segment_cb_.Run();
575 }
576
577 if (!video_config.Matches(last_video_config_)) {
578 last_video_config_ = video_config;
579 config_cb_.Run(last_audio_config_, last_video_config_);
580 }
581
582 StreamParser::BufferQueue audio_queue;
583 StreamParser::BufferQueue video_queue;
584 while (!video_buffer_queue_.empty() &&
585 video_buffer_queue_.front().config.Matches(last_video_config_)) {
586 video_queue.push_back(video_buffer_queue_.front().buffer);
587 video_buffer_queue_.pop_front();
588 }
589 new_buffers_cb_.Run(audio_queue, video_queue);
590 }
591
592 } // namespace mpeg2ts
593 } // namespace media
594
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698