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 "media/formats/mp2t/mp2t_stream_parser.h" 6 7 #include "base/bind.h" 8 #include "base/callback_helpers.h" 9 #include "base/memory/scoped_ptr.h" 10 #include "base/stl_util.h" 11 #include "media/base/audio_decoder_config.h" 12 #include "media/base/buffers.h" 13 #include "media/base/stream_parser_buffer.h" 14 #include "media/base/text_track_config.h" 15 #include "media/base/video_decoder_config.h" 16 #include "media/formats/mp2t/es_parser.h" 17 #include "media/formats/mp2t/es_parser_adts.h" 18 #include "media/formats/mp2t/es_parser_h264.h" 19 #include "media/formats/mp2t/mp2t_common.h" 20 #include "media/formats/mp2t/ts_packet.h" 21 #include "media/formats/mp2t/ts_section.h" 22 #include "media/formats/mp2t/ts_section_pat.h" 23 #include "media/formats/mp2t/ts_section_pes.h" 24 #include "media/formats/mp2t/ts_section_pmt.h" 25 26 namespace media { 27 namespace mp2t { 28 29 enum StreamType { 30 // ISO-13818.1 / ITU H.222 Table 2.34 "Stream type assignments" 31 kStreamTypeMpeg1Audio = 0x3, 32 kStreamTypeAAC = 0xf, 33 kStreamTypeAVC = 0x1b, 34 }; 35 36 class PidState { 37 public: 38 enum PidType { 39 kPidPat, 40 kPidPmt, 41 kPidAudioPes, 42 kPidVideoPes, 43 }; 44 45 PidState(int pid, PidType pid_tyoe, 46 scoped_ptr<TsSection> section_parser); 47 48 // Extract the content of the TS packet and parse it. 49 // Return true if successful. 50 bool PushTsPacket(const TsPacket& ts_packet); 51 52 // Flush the PID state (possibly emitting some pending frames) 53 // and reset its state. 54 void Flush(); 55 56 // Enable/disable the PID. 57 // Disabling a PID will reset its state and ignore any further incoming TS 58 // packets. 59 void Enable(); 60 void Disable(); 61 bool IsEnabled() const; 62 63 PidType pid_type() const { return pid_type_; } 64 65 private: 66 void ResetState(); 67 68 int pid_; 69 PidType pid_type_; 70 scoped_ptr<TsSection> section_parser_; 71 72 bool enable_; 73 74 int continuity_counter_; 75 }; 76 77 PidState::PidState(int pid, PidType pid_type, 78 scoped_ptr<TsSection> section_parser) 79 : pid_(pid), 80 pid_type_(pid_type), 81 section_parser_(section_parser.Pass()), 82 enable_(false), 83 continuity_counter_(-1) { 84 DCHECK(section_parser_); 85 } 86 87 bool PidState::PushTsPacket(const TsPacket& ts_packet) { 88 DCHECK_EQ(ts_packet.pid(), pid_); 89 90 // The current PID is not part of the PID filter, 91 // just discard the incoming TS packet. 92 if (!enable_) 93 return true; 94 95 int expected_continuity_counter = (continuity_counter_ + 1) % 16; 96 if (continuity_counter_ >= 0 && 97 ts_packet.continuity_counter() != expected_continuity_counter) { 98 DVLOG(1) << "TS discontinuity detected for pid: " << pid_; 99 return false; 100 } 101 102 bool status = section_parser_->Parse( 103 ts_packet.payload_unit_start_indicator(), 104 ts_packet.payload(), 105 ts_packet.payload_size()); 106 107 // At the minimum, when parsing failed, auto reset the section parser. 108 // Components that use the StreamParser can take further action if needed. 109 if (!status) { 110 DVLOG(1) << "Parsing failed for pid = " << pid_; 111 ResetState(); 112 } 113 114 return status; 115 } 116 117 void PidState::Flush() { 118 section_parser_->Flush(); 119 ResetState(); 120 } 121 122 void PidState::Enable() { 123 enable_ = true; 124 } 125 126 void PidState::Disable() { 127 if (!enable_) 128 return; 129 130 ResetState(); 131 enable_ = false; 132 } 133 134 bool PidState::IsEnabled() const { 135 return enable_; 136 } 137 138 void PidState::ResetState() { 139 section_parser_->Reset(); 140 continuity_counter_ = -1; 141 } 142 143 Mp2tStreamParser::BufferQueueWithConfig::BufferQueueWithConfig( 144 bool is_cfg_sent, 145 const AudioDecoderConfig& audio_cfg, 146 const VideoDecoderConfig& video_cfg) 147 : is_config_sent(is_cfg_sent), 148 audio_config(audio_cfg), 149 video_config(video_cfg) { 150 } 151 152 Mp2tStreamParser::BufferQueueWithConfig::~BufferQueueWithConfig() { 153 } 154 155 Mp2tStreamParser::Mp2tStreamParser(bool sbr_in_mimetype) 156 : sbr_in_mimetype_(sbr_in_mimetype), 157 selected_audio_pid_(-1), 158 selected_video_pid_(-1), 159 is_initialized_(false), 160 segment_started_(false), 161 first_video_frame_in_segment_(true) { 162 } 163 164 Mp2tStreamParser::~Mp2tStreamParser() { 165 STLDeleteValues(&pids_); 166 } 167 168 void Mp2tStreamParser::Init( 169 const InitCB& init_cb, 170 const NewConfigCB& config_cb, 171 const NewBuffersCB& new_buffers_cb, 172 bool /* ignore_text_tracks */ , 173 const NeedKeyCB& need_key_cb, 174 const NewMediaSegmentCB& new_segment_cb, 175 const base::Closure& end_of_segment_cb, 176 const LogCB& log_cb) { 177 DCHECK(!is_initialized_); 178 DCHECK(init_cb_.is_null()); 179 DCHECK(!init_cb.is_null()); 180 DCHECK(!config_cb.is_null()); 181 DCHECK(!new_buffers_cb.is_null()); 182 DCHECK(!need_key_cb.is_null()); 183 DCHECK(!end_of_segment_cb.is_null()); 184 185 init_cb_ = init_cb; 186 config_cb_ = config_cb; 187 new_buffers_cb_ = new_buffers_cb; 188 need_key_cb_ = need_key_cb; 189 new_segment_cb_ = new_segment_cb; 190 end_of_segment_cb_ = end_of_segment_cb; 191 log_cb_ = log_cb; 192 } 193 194 void Mp2tStreamParser::Flush() { 195 DVLOG(1) << "Mp2tStreamParser::Flush"; 196 197 // Flush the buffers and reset the pids. 198 for (std::map<int, PidState*>::iterator it = pids_.begin(); 199 it != pids_.end(); ++it) { 200 DVLOG(1) << "Flushing PID: " << it->first; 201 PidState* pid_state = it->second; 202 pid_state->Flush(); 203 delete pid_state; 204 } 205 pids_.clear(); 206 EmitRemainingBuffers(); 207 buffer_queue_chain_.clear(); 208 209 // End of the segment. 210 // Note: does not need to invoke |end_of_segment_cb_| since flushing the 211 // stream parser already involves the end of the current segment. 212 segment_started_ = false; 213 first_video_frame_in_segment_ = true; 214 discarded_frames_dts_.clear(); 215 216 // Remove any bytes left in the TS buffer. 217 // (i.e. any partial TS packet => less than 188 bytes). 218 ts_byte_queue_.Reset(); 219 220 // Reset the selected PIDs. 221 selected_audio_pid_ = -1; 222 selected_video_pid_ = -1; 223 } 224 225 bool Mp2tStreamParser::Parse(const uint8* buf, int size) { 226 DVLOG(1) << "Mp2tStreamParser::Parse size=" << size; 227 228 // Add the data to the parser state. 229 ts_byte_queue_.Push(buf, size); 230 231 while (true) { 232 const uint8* ts_buffer; 233 int ts_buffer_size; 234 ts_byte_queue_.Peek(&ts_buffer, &ts_buffer_size); 235 if (ts_buffer_size < TsPacket::kPacketSize) 236 break; 237 238 // Synchronization. 239 int skipped_bytes = TsPacket::Sync(ts_buffer, ts_buffer_size); 240 if (skipped_bytes > 0) { 241 DVLOG(1) << "Packet not aligned on a TS syncword:" 242 << " skipped_bytes=" << skipped_bytes; 243 ts_byte_queue_.Pop(skipped_bytes); 244 continue; 245 } 246 247 // Parse the TS header, skipping 1 byte if the header is invalid. 248 scoped_ptr<TsPacket> ts_packet(TsPacket::Parse(ts_buffer, ts_buffer_size)); 249 if (!ts_packet) { 250 DVLOG(1) << "Error: invalid TS packet"; 251 ts_byte_queue_.Pop(1); 252 continue; 253 } 254 DVLOG(LOG_LEVEL_TS) 255 << "Processing PID=" << ts_packet->pid() 256 << " start_unit=" << ts_packet->payload_unit_start_indicator(); 257 258 // Parse the section. 259 std::map<int, PidState*>::iterator it = pids_.find(ts_packet->pid()); 260 if (it == pids_.end() && 261 ts_packet->pid() == TsSection::kPidPat) { 262 // Create the PAT state here if needed. 263 scoped_ptr<TsSection> pat_section_parser( 264 new TsSectionPat( 265 base::Bind(&Mp2tStreamParser::RegisterPmt, 266 base::Unretained(this)))); 267 scoped_ptr<PidState> pat_pid_state( 268 new PidState(ts_packet->pid(), PidState::kPidPat, 269 pat_section_parser.Pass())); 270 pat_pid_state->Enable(); 271 it = pids_.insert( 272 std::pair<int, PidState*>(ts_packet->pid(), 273 pat_pid_state.release())).first; 274 } 275 276 if (it != pids_.end()) { 277 if (!it->second->PushTsPacket(*ts_packet)) 278 return false; 279 } else { 280 DVLOG(LOG_LEVEL_TS) << "Ignoring TS packet for pid: " << ts_packet->pid(); 281 } 282 283 // Go to the next packet. 284 ts_byte_queue_.Pop(TsPacket::kPacketSize); 285 } 286 287 RCHECK(FinishInitializationIfNeeded()); 288 289 // Emit the A/V buffers that kept accumulating during TS parsing. 290 return EmitRemainingBuffers(); 291 } 292 293 void Mp2tStreamParser::RegisterPmt(int program_number, int pmt_pid) { 294 DVLOG(1) << "RegisterPmt:" 295 << " program_number=" << program_number 296 << " pmt_pid=" << pmt_pid; 297 298 // Only one TS program is allowed. Ignore the incoming program map table, 299 // if there is already one registered. 300 for (std::map<int, PidState*>::iterator it = pids_.begin(); 301 it != pids_.end(); ++it) { 302 PidState* pid_state = it->second; 303 if (pid_state->pid_type() == PidState::kPidPmt) { 304 DVLOG_IF(1, pmt_pid != it->first) << "More than one program is defined"; 305 return; 306 } 307 } 308 309 // Create the PMT state here if needed. 310 DVLOG(1) << "Create a new PMT parser"; 311 scoped_ptr<TsSection> pmt_section_parser( 312 new TsSectionPmt( 313 base::Bind(&Mp2tStreamParser::RegisterPes, 314 base::Unretained(this), pmt_pid))); 315 scoped_ptr<PidState> pmt_pid_state( 316 new PidState(pmt_pid, PidState::kPidPmt, pmt_section_parser.Pass())); 317 pmt_pid_state->Enable(); 318 pids_.insert(std::pair<int, PidState*>(pmt_pid, pmt_pid_state.release())); 319 } 320 321 void Mp2tStreamParser::RegisterPes(int pmt_pid, 322 int pes_pid, 323 int stream_type) { 324 // TODO(damienv): check there is no mismatch if the entry already exists. 325 DVLOG(1) << "RegisterPes:" 326 << " pes_pid=" << pes_pid 327 << " stream_type=" << std::hex << stream_type << std::dec; 328 std::map<int, PidState*>::iterator it = pids_.find(pes_pid); 329 if (it != pids_.end()) 330 return; 331 332 // Create a stream parser corresponding to the stream type. 333 bool is_audio = false; 334 scoped_ptr<EsParser> es_parser; 335 if (stream_type == kStreamTypeAVC) { 336 es_parser.reset( 337 new EsParserH264( 338 base::Bind(&Mp2tStreamParser::OnVideoConfigChanged, 339 base::Unretained(this), 340 pes_pid), 341 base::Bind(&Mp2tStreamParser::OnEmitVideoBuffer, 342 base::Unretained(this), 343 pes_pid))); 344 } else if (stream_type == kStreamTypeAAC) { 345 es_parser.reset( 346 new EsParserAdts( 347 base::Bind(&Mp2tStreamParser::OnAudioConfigChanged, 348 base::Unretained(this), 349 pes_pid), 350 base::Bind(&Mp2tStreamParser::OnEmitAudioBuffer, 351 base::Unretained(this), 352 pes_pid), 353 sbr_in_mimetype_)); 354 is_audio = true; 355 } else { 356 return; 357 } 358 359 // Create the PES state here. 360 DVLOG(1) << "Create a new PES state"; 361 scoped_ptr<TsSection> pes_section_parser( 362 new TsSectionPes(es_parser.Pass())); 363 PidState::PidType pid_type = 364 is_audio ? PidState::kPidAudioPes : PidState::kPidVideoPes; 365 scoped_ptr<PidState> pes_pid_state( 366 new PidState(pes_pid, pid_type, pes_section_parser.Pass())); 367 pids_.insert(std::pair<int, PidState*>(pes_pid, pes_pid_state.release())); 368 369 // A new PES pid has been added, the PID filter might change. 370 UpdatePidFilter(); 371 } 372 373 void Mp2tStreamParser::UpdatePidFilter() { 374 // Applies the HLS rule to select the default audio/video PIDs: 375 // select the audio/video streams with the lowest PID. 376 // TODO(damienv): this can be changed when the StreamParser interface 377 // supports multiple audio/video streams. 378 PidMap::iterator lowest_audio_pid = pids_.end(); 379 PidMap::iterator lowest_video_pid = pids_.end(); 380 for (PidMap::iterator it = pids_.begin(); it != pids_.end(); ++it) { 381 int pid = it->first; 382 PidState* pid_state = it->second; 383 if (pid_state->pid_type() == PidState::kPidAudioPes && 384 (lowest_audio_pid == pids_.end() || pid < lowest_audio_pid->first)) 385 lowest_audio_pid = it; 386 if (pid_state->pid_type() == PidState::kPidVideoPes && 387 (lowest_video_pid == pids_.end() || pid < lowest_video_pid->first)) 388 lowest_video_pid = it; 389 } 390 391 // Enable both the lowest audio and video PIDs. 392 if (lowest_audio_pid != pids_.end()) { 393 DVLOG(1) << "Enable audio pid: " << lowest_audio_pid->first; 394 lowest_audio_pid->second->Enable(); 395 selected_audio_pid_ = lowest_audio_pid->first; 396 } 397 if (lowest_video_pid != pids_.end()) { 398 DVLOG(1) << "Enable video pid: " << lowest_video_pid->first; 399 lowest_video_pid->second->Enable(); 400 selected_video_pid_ = lowest_video_pid->first; 401 } 402 403 // Disable all the other audio and video PIDs. 404 for (PidMap::iterator it = pids_.begin(); it != pids_.end(); ++it) { 405 PidState* pid_state = it->second; 406 if (it != lowest_audio_pid && it != lowest_video_pid && 407 (pid_state->pid_type() == PidState::kPidAudioPes || 408 pid_state->pid_type() == PidState::kPidVideoPes)) 409 pid_state->Disable(); 410 } 411 } 412 413 void Mp2tStreamParser::OnVideoConfigChanged( 414 int pes_pid, 415 const VideoDecoderConfig& video_decoder_config) { 416 DVLOG(1) << "OnVideoConfigChanged for pid=" << pes_pid; 417 DCHECK_EQ(pes_pid, selected_video_pid_); 418 DCHECK(video_decoder_config.IsValidConfig()); 419 420 // Create a new entry in |buffer_queue_chain_| with the updated configs. 421 BufferQueueWithConfig buffer_queue_with_config( 422 false, 423 buffer_queue_chain_.empty() 424 ? AudioDecoderConfig() : buffer_queue_chain_.back().audio_config, 425 video_decoder_config); 426 buffer_queue_chain_.push_back(buffer_queue_with_config); 427 428 // Replace any non valid config with the 1st valid entry. 429 // This might happen if there was no available config before. 430 for (std::list<BufferQueueWithConfig>::iterator it = 431 buffer_queue_chain_.begin(); it != buffer_queue_chain_.end(); ++it) { 432 if (it->video_config.IsValidConfig()) 433 break; 434 it->video_config = video_decoder_config; 435 } 436 } 437 438 void Mp2tStreamParser::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 DCHECK(audio_decoder_config.IsValidConfig()); 444 445 // Create a new entry in |buffer_queue_chain_| with the updated configs. 446 BufferQueueWithConfig buffer_queue_with_config( 447 false, 448 audio_decoder_config, 449 buffer_queue_chain_.empty() 450 ? VideoDecoderConfig() : buffer_queue_chain_.back().video_config); 451 buffer_queue_chain_.push_back(buffer_queue_with_config); 452 453 // Replace any non valid config with the 1st valid entry. 454 // This might happen if there was no available config before. 455 for (std::list<BufferQueueWithConfig>::iterator it = 456 buffer_queue_chain_.begin(); it != buffer_queue_chain_.end(); ++it) { 457 if (it->audio_config.IsValidConfig()) 458 break; 459 it->audio_config = audio_decoder_config; 460 } 461 } 462 463 bool Mp2tStreamParser::FinishInitializationIfNeeded() { 464 // Nothing to be done if already initialized. 465 if (is_initialized_) 466 return true; 467 468 // Wait for more data to come to finish initialization. 469 if (buffer_queue_chain_.empty()) 470 return true; 471 472 // Wait for more data to come if one of the config is not available. 473 BufferQueueWithConfig& queue_with_config = buffer_queue_chain_.front(); 474 if (selected_audio_pid_ > 0 && 475 !queue_with_config.audio_config.IsValidConfig()) 476 return true; 477 if (selected_video_pid_ > 0 && 478 !queue_with_config.video_config.IsValidConfig()) 479 return true; 480 481 // Pass the config before invoking the initialization callback. 482 RCHECK(config_cb_.Run(queue_with_config.audio_config, 483 queue_with_config.video_config, 484 TextTrackConfigMap())); 485 queue_with_config.is_config_sent = true; 486 487 // For Mpeg2 TS, the duration is not known. 488 DVLOG(1) << "Mpeg2TS stream parser initialization done"; 489 base::ResetAndReturn(&init_cb_) 490 .Run(true, InitParameters(kInfiniteDuration())); 491 is_initialized_ = true; 492 493 return true; 494 } 495 496 void Mp2tStreamParser::OnEmitAudioBuffer( 497 int pes_pid, 498 scoped_refptr<StreamParserBuffer> stream_parser_buffer) { 499 DCHECK_EQ(pes_pid, selected_audio_pid_); 500 501 DVLOG(LOG_LEVEL_ES) 502 << "OnEmitAudioBuffer: " 503 << " size=" 504 << stream_parser_buffer->data_size() 505 << " dts=" 506 << stream_parser_buffer->GetDecodeTimestamp().InMilliseconds() 507 << " pts=" 508 << stream_parser_buffer->timestamp().InMilliseconds(); 509 stream_parser_buffer->set_timestamp( 510 stream_parser_buffer->timestamp() - time_offset_); 511 stream_parser_buffer->SetDecodeTimestamp( 512 stream_parser_buffer->GetDecodeTimestamp() - time_offset_); 513 514 // Ignore the incoming buffer if it is not associated with any config. 515 if (buffer_queue_chain_.empty()) { 516 DVLOG(1) << "Ignoring audio buffer with no corresponding audio config"; 517 return; 518 } 519 520 buffer_queue_chain_.back().audio_queue.push_back(stream_parser_buffer); 521 } 522 523 void Mp2tStreamParser::OnEmitVideoBuffer( 524 int pes_pid, 525 scoped_refptr<StreamParserBuffer> stream_parser_buffer) { 526 DCHECK_EQ(pes_pid, selected_video_pid_); 527 528 DVLOG(LOG_LEVEL_ES) 529 << "OnEmitVideoBuffer" 530 << " size=" 531 << stream_parser_buffer->data_size() 532 << " dts=" 533 << stream_parser_buffer->GetDecodeTimestamp().InMilliseconds() 534 << " pts=" 535 << stream_parser_buffer->timestamp().InMilliseconds() 536 << " IsKeyframe=" 537 << stream_parser_buffer->IsKeyframe(); 538 stream_parser_buffer->set_timestamp( 539 stream_parser_buffer->timestamp() - time_offset_); 540 stream_parser_buffer->SetDecodeTimestamp( 541 stream_parser_buffer->GetDecodeTimestamp() - time_offset_); 542 543 // Discard the incoming buffer: 544 // - if it is not associated with any config, 545 // - or if only non-key frames have been added to a new segment. 546 if (buffer_queue_chain_.empty() || 547 (first_video_frame_in_segment_ && !stream_parser_buffer->IsKeyframe())) { 548 DVLOG(1) << "Discard video buffer:" 549 << " keyframe=" << stream_parser_buffer->IsKeyframe() 550 << " dts=" 551 << stream_parser_buffer->GetDecodeTimestamp().InMilliseconds(); 552 if (discarded_frames_dts_.empty() || 553 discarded_frames_min_pts_ > stream_parser_buffer->timestamp()) { 554 discarded_frames_min_pts_ = stream_parser_buffer->timestamp(); 555 } 556 discarded_frames_dts_.push_back( 557 stream_parser_buffer->GetDecodeTimestamp()); 558 return; 559 } 560 561 // Fill the gap created by frames that have been discarded. 562 if (!discarded_frames_dts_.empty()) 563 FillVideoGap(stream_parser_buffer); 564 565 first_video_frame_in_segment_ = false; 566 buffer_queue_chain_.back().video_queue.push_back(stream_parser_buffer); 567 } 568 569 bool Mp2tStreamParser::EmitRemainingBuffers() { 570 DVLOG(LOG_LEVEL_ES) << "Mp2tStreamParser::EmitRemainingBuffers"; 571 572 // No buffer should be sent until fully initialized. 573 if (!is_initialized_) 574 return true; 575 576 if (buffer_queue_chain_.empty()) 577 return true; 578 579 // Keep track of the last audio and video config sent. 580 AudioDecoderConfig last_audio_config = 581 buffer_queue_chain_.back().audio_config; 582 VideoDecoderConfig last_video_config = 583 buffer_queue_chain_.back().video_config; 584 585 // Do not have all the configs, need more data. 586 if (selected_audio_pid_ >= 0 && !last_audio_config.IsValidConfig()) 587 return true; 588 if (selected_video_pid_ >= 0 && !last_video_config.IsValidConfig()) 589 return true; 590 591 // Buffer emission. 592 while (!buffer_queue_chain_.empty()) { 593 // Start a segment if needed. 594 if (!segment_started_) { 595 DVLOG(1) << "Starting a new segment"; 596 segment_started_ = true; 597 new_segment_cb_.Run(); 598 } 599 600 // Update the audio and video config if needed. 601 BufferQueueWithConfig& queue_with_config = buffer_queue_chain_.front(); 602 if (!queue_with_config.is_config_sent) { 603 if (!config_cb_.Run(queue_with_config.audio_config, 604 queue_with_config.video_config, 605 TextTrackConfigMap())) 606 return false; 607 queue_with_config.is_config_sent = true; 608 } 609 610 // Add buffers. 611 TextBufferQueueMap empty_text_map; 612 if (!queue_with_config.audio_queue.empty() || 613 !queue_with_config.video_queue.empty()) { 614 if (!new_buffers_cb_.Run(queue_with_config.audio_queue, 615 queue_with_config.video_queue, 616 empty_text_map)) { 617 return false; 618 } 619 } 620 621 buffer_queue_chain_.pop_front(); 622 } 623 624 // Push an empty queue with the last audio/video config 625 // so that buffers with the same config can be added later on. 626 BufferQueueWithConfig queue_with_config( 627 true, last_audio_config, last_video_config); 628 buffer_queue_chain_.push_back(queue_with_config); 629 630 return true; 631 } 632 633 void Mp2tStreamParser::FillVideoGap( 634 const scoped_refptr<StreamParserBuffer>& stream_parser_buffer) { 635 DCHECK(!buffer_queue_chain_.empty()); 636 DCHECK(!discarded_frames_dts_.empty()); 637 DCHECK(stream_parser_buffer->IsKeyframe()); 638 639 // PTS is interpolated between the min PTS of discarded frames 640 // and the PTS of the first valid buffer. 641 base::TimeDelta pts = discarded_frames_min_pts_; 642 base::TimeDelta pts_delta = 643 (stream_parser_buffer->timestamp() - pts) / discarded_frames_dts_.size(); 644 645 while (!discarded_frames_dts_.empty()) { 646 scoped_refptr<StreamParserBuffer> frame = 647 StreamParserBuffer::CopyFrom( 648 stream_parser_buffer->data(), 649 stream_parser_buffer->data_size(), 650 stream_parser_buffer->IsKeyframe(), 651 stream_parser_buffer->type(), 652 stream_parser_buffer->track_id()); 653 frame->SetDecodeTimestamp(discarded_frames_dts_.front()); 654 frame->set_timestamp(pts); 655 buffer_queue_chain_.back().video_queue.push_back(frame); 656 pts += pts_delta; 657 discarded_frames_dts_.pop_front(); 658 } 659 } 660 661 } // namespace mp2t 662 } // namespace media 663