| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/formats/mp2t/mp2t_stream_parser.h" | 5 #include "media/formats/mp2t/mp2t_stream_parser.h" |
| 6 | 6 |
| 7 #include <memory> |
| 7 #include <utility> | 8 #include <utility> |
| 8 | 9 |
| 9 #include "base/bind.h" | 10 #include "base/bind.h" |
| 10 #include "base/callback_helpers.h" | 11 #include "base/callback_helpers.h" |
| 11 #include "base/stl_util.h" | 12 #include "base/stl_util.h" |
| 12 #include "media/base/media_tracks.h" | 13 #include "media/base/media_tracks.h" |
| 13 #include "media/base/stream_parser_buffer.h" | 14 #include "media/base/stream_parser_buffer.h" |
| 14 #include "media/base/text_track_config.h" | 15 #include "media/base/text_track_config.h" |
| 15 #include "media/base/timestamp_constants.h" | 16 #include "media/base/timestamp_constants.h" |
| 16 #include "media/formats/mp2t/es_parser.h" | 17 #include "media/formats/mp2t/es_parser.h" |
| (...skipping 19 matching lines...) Expand all Loading... |
| 36 | 37 |
| 37 class PidState { | 38 class PidState { |
| 38 public: | 39 public: |
| 39 enum PidType { | 40 enum PidType { |
| 40 kPidPat, | 41 kPidPat, |
| 41 kPidPmt, | 42 kPidPmt, |
| 42 kPidAudioPes, | 43 kPidAudioPes, |
| 43 kPidVideoPes, | 44 kPidVideoPes, |
| 44 }; | 45 }; |
| 45 | 46 |
| 46 PidState(int pid, PidType pid_tyoe, | 47 PidState(int pid, |
| 47 scoped_ptr<TsSection> section_parser); | 48 PidType pid_tyoe, |
| 49 std::unique_ptr<TsSection> section_parser); |
| 48 | 50 |
| 49 // Extract the content of the TS packet and parse it. | 51 // Extract the content of the TS packet and parse it. |
| 50 // Return true if successful. | 52 // Return true if successful. |
| 51 bool PushTsPacket(const TsPacket& ts_packet); | 53 bool PushTsPacket(const TsPacket& ts_packet); |
| 52 | 54 |
| 53 // Flush the PID state (possibly emitting some pending frames) | 55 // Flush the PID state (possibly emitting some pending frames) |
| 54 // and reset its state. | 56 // and reset its state. |
| 55 void Flush(); | 57 void Flush(); |
| 56 | 58 |
| 57 // Enable/disable the PID. | 59 // Enable/disable the PID. |
| 58 // Disabling a PID will reset its state and ignore any further incoming TS | 60 // Disabling a PID will reset its state and ignore any further incoming TS |
| 59 // packets. | 61 // packets. |
| 60 void Enable(); | 62 void Enable(); |
| 61 void Disable(); | 63 void Disable(); |
| 62 bool IsEnabled() const; | 64 bool IsEnabled() const; |
| 63 | 65 |
| 64 PidType pid_type() const { return pid_type_; } | 66 PidType pid_type() const { return pid_type_; } |
| 65 | 67 |
| 66 private: | 68 private: |
| 67 void ResetState(); | 69 void ResetState(); |
| 68 | 70 |
| 69 int pid_; | 71 int pid_; |
| 70 PidType pid_type_; | 72 PidType pid_type_; |
| 71 scoped_ptr<TsSection> section_parser_; | 73 std::unique_ptr<TsSection> section_parser_; |
| 72 | 74 |
| 73 bool enable_; | 75 bool enable_; |
| 74 | 76 |
| 75 int continuity_counter_; | 77 int continuity_counter_; |
| 76 }; | 78 }; |
| 77 | 79 |
| 78 PidState::PidState(int pid, | 80 PidState::PidState(int pid, |
| 79 PidType pid_type, | 81 PidType pid_type, |
| 80 scoped_ptr<TsSection> section_parser) | 82 std::unique_ptr<TsSection> section_parser) |
| 81 : pid_(pid), | 83 : pid_(pid), |
| 82 pid_type_(pid_type), | 84 pid_type_(pid_type), |
| 83 section_parser_(std::move(section_parser)), | 85 section_parser_(std::move(section_parser)), |
| 84 enable_(false), | 86 enable_(false), |
| 85 continuity_counter_(-1) { | 87 continuity_counter_(-1) { |
| 86 DCHECK(section_parser_); | 88 DCHECK(section_parser_); |
| 87 } | 89 } |
| 88 | 90 |
| 89 bool PidState::PushTsPacket(const TsPacket& ts_packet) { | 91 bool PidState::PushTsPacket(const TsPacket& ts_packet) { |
| 90 DCHECK_EQ(ts_packet.pid(), pid_); | 92 DCHECK_EQ(ts_packet.pid(), pid_); |
| (...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 263 // Synchronization. | 265 // Synchronization. |
| 264 int skipped_bytes = TsPacket::Sync(ts_buffer, ts_buffer_size); | 266 int skipped_bytes = TsPacket::Sync(ts_buffer, ts_buffer_size); |
| 265 if (skipped_bytes > 0) { | 267 if (skipped_bytes > 0) { |
| 266 DVLOG(1) << "Packet not aligned on a TS syncword:" | 268 DVLOG(1) << "Packet not aligned on a TS syncword:" |
| 267 << " skipped_bytes=" << skipped_bytes; | 269 << " skipped_bytes=" << skipped_bytes; |
| 268 ts_byte_queue_.Pop(skipped_bytes); | 270 ts_byte_queue_.Pop(skipped_bytes); |
| 269 continue; | 271 continue; |
| 270 } | 272 } |
| 271 | 273 |
| 272 // Parse the TS header, skipping 1 byte if the header is invalid. | 274 // Parse the TS header, skipping 1 byte if the header is invalid. |
| 273 scoped_ptr<TsPacket> ts_packet(TsPacket::Parse(ts_buffer, ts_buffer_size)); | 275 std::unique_ptr<TsPacket> ts_packet( |
| 276 TsPacket::Parse(ts_buffer, ts_buffer_size)); |
| 274 if (!ts_packet) { | 277 if (!ts_packet) { |
| 275 DVLOG(1) << "Error: invalid TS packet"; | 278 DVLOG(1) << "Error: invalid TS packet"; |
| 276 ts_byte_queue_.Pop(1); | 279 ts_byte_queue_.Pop(1); |
| 277 continue; | 280 continue; |
| 278 } | 281 } |
| 279 DVLOG(LOG_LEVEL_TS) | 282 DVLOG(LOG_LEVEL_TS) |
| 280 << "Processing PID=" << ts_packet->pid() | 283 << "Processing PID=" << ts_packet->pid() |
| 281 << " start_unit=" << ts_packet->payload_unit_start_indicator(); | 284 << " start_unit=" << ts_packet->payload_unit_start_indicator(); |
| 282 | 285 |
| 283 // Parse the section. | 286 // Parse the section. |
| 284 std::map<int, PidState*>::iterator it = pids_.find(ts_packet->pid()); | 287 std::map<int, PidState*>::iterator it = pids_.find(ts_packet->pid()); |
| 285 if (it == pids_.end() && | 288 if (it == pids_.end() && |
| 286 ts_packet->pid() == TsSection::kPidPat) { | 289 ts_packet->pid() == TsSection::kPidPat) { |
| 287 // Create the PAT state here if needed. | 290 // Create the PAT state here if needed. |
| 288 scoped_ptr<TsSection> pat_section_parser( | 291 std::unique_ptr<TsSection> pat_section_parser(new TsSectionPat( |
| 289 new TsSectionPat( | 292 base::Bind(&Mp2tStreamParser::RegisterPmt, base::Unretained(this)))); |
| 290 base::Bind(&Mp2tStreamParser::RegisterPmt, | 293 std::unique_ptr<PidState> pat_pid_state(new PidState( |
| 291 base::Unretained(this)))); | |
| 292 scoped_ptr<PidState> pat_pid_state(new PidState( | |
| 293 ts_packet->pid(), PidState::kPidPat, std::move(pat_section_parser))); | 294 ts_packet->pid(), PidState::kPidPat, std::move(pat_section_parser))); |
| 294 pat_pid_state->Enable(); | 295 pat_pid_state->Enable(); |
| 295 it = pids_.insert( | 296 it = pids_.insert( |
| 296 std::pair<int, PidState*>(ts_packet->pid(), | 297 std::pair<int, PidState*>(ts_packet->pid(), |
| 297 pat_pid_state.release())).first; | 298 pat_pid_state.release())).first; |
| 298 } | 299 } |
| 299 | 300 |
| 300 if (it != pids_.end()) { | 301 if (it != pids_.end()) { |
| 301 if (!it->second->PushTsPacket(*ts_packet)) | 302 if (!it->second->PushTsPacket(*ts_packet)) |
| 302 return false; | 303 return false; |
| (...skipping 22 matching lines...) Expand all Loading... |
| 325 it != pids_.end(); ++it) { | 326 it != pids_.end(); ++it) { |
| 326 PidState* pid_state = it->second; | 327 PidState* pid_state = it->second; |
| 327 if (pid_state->pid_type() == PidState::kPidPmt) { | 328 if (pid_state->pid_type() == PidState::kPidPmt) { |
| 328 DVLOG_IF(1, pmt_pid != it->first) << "More than one program is defined"; | 329 DVLOG_IF(1, pmt_pid != it->first) << "More than one program is defined"; |
| 329 return; | 330 return; |
| 330 } | 331 } |
| 331 } | 332 } |
| 332 | 333 |
| 333 // Create the PMT state here if needed. | 334 // Create the PMT state here if needed. |
| 334 DVLOG(1) << "Create a new PMT parser"; | 335 DVLOG(1) << "Create a new PMT parser"; |
| 335 scoped_ptr<TsSection> pmt_section_parser( | 336 std::unique_ptr<TsSection> pmt_section_parser(new TsSectionPmt(base::Bind( |
| 336 new TsSectionPmt( | 337 &Mp2tStreamParser::RegisterPes, base::Unretained(this), pmt_pid))); |
| 337 base::Bind(&Mp2tStreamParser::RegisterPes, | 338 std::unique_ptr<PidState> pmt_pid_state( |
| 338 base::Unretained(this), pmt_pid))); | |
| 339 scoped_ptr<PidState> pmt_pid_state( | |
| 340 new PidState(pmt_pid, PidState::kPidPmt, std::move(pmt_section_parser))); | 339 new PidState(pmt_pid, PidState::kPidPmt, std::move(pmt_section_parser))); |
| 341 pmt_pid_state->Enable(); | 340 pmt_pid_state->Enable(); |
| 342 pids_.insert(std::pair<int, PidState*>(pmt_pid, pmt_pid_state.release())); | 341 pids_.insert(std::pair<int, PidState*>(pmt_pid, pmt_pid_state.release())); |
| 343 } | 342 } |
| 344 | 343 |
| 345 void Mp2tStreamParser::RegisterPes(int pmt_pid, | 344 void Mp2tStreamParser::RegisterPes(int pmt_pid, |
| 346 int pes_pid, | 345 int pes_pid, |
| 347 int stream_type) { | 346 int stream_type) { |
| 348 // TODO(damienv): check there is no mismatch if the entry already exists. | 347 // TODO(damienv): check there is no mismatch if the entry already exists. |
| 349 DVLOG(1) << "RegisterPes:" | 348 DVLOG(1) << "RegisterPes:" |
| 350 << " pes_pid=" << pes_pid | 349 << " pes_pid=" << pes_pid |
| 351 << " stream_type=" << std::hex << stream_type << std::dec; | 350 << " stream_type=" << std::hex << stream_type << std::dec; |
| 352 std::map<int, PidState*>::iterator it = pids_.find(pes_pid); | 351 std::map<int, PidState*>::iterator it = pids_.find(pes_pid); |
| 353 if (it != pids_.end()) | 352 if (it != pids_.end()) |
| 354 return; | 353 return; |
| 355 | 354 |
| 356 // Create a stream parser corresponding to the stream type. | 355 // Create a stream parser corresponding to the stream type. |
| 357 bool is_audio = false; | 356 bool is_audio = false; |
| 358 scoped_ptr<EsParser> es_parser; | 357 std::unique_ptr<EsParser> es_parser; |
| 359 if (stream_type == kStreamTypeAVC) { | 358 if (stream_type == kStreamTypeAVC) { |
| 360 es_parser.reset( | 359 es_parser.reset( |
| 361 new EsParserH264( | 360 new EsParserH264( |
| 362 base::Bind(&Mp2tStreamParser::OnVideoConfigChanged, | 361 base::Bind(&Mp2tStreamParser::OnVideoConfigChanged, |
| 363 base::Unretained(this), | 362 base::Unretained(this), |
| 364 pes_pid), | 363 pes_pid), |
| 365 base::Bind(&Mp2tStreamParser::OnEmitVideoBuffer, | 364 base::Bind(&Mp2tStreamParser::OnEmitVideoBuffer, |
| 366 base::Unretained(this), | 365 base::Unretained(this), |
| 367 pes_pid))); | 366 pes_pid))); |
| 368 } else if (stream_type == kStreamTypeAAC) { | 367 } else if (stream_type == kStreamTypeAAC) { |
| (...skipping 14 matching lines...) Expand all Loading... |
| 383 base::Bind(&Mp2tStreamParser::OnEmitAudioBuffer, base::Unretained(this), | 382 base::Bind(&Mp2tStreamParser::OnEmitAudioBuffer, base::Unretained(this), |
| 384 pes_pid), | 383 pes_pid), |
| 385 media_log_)); | 384 media_log_)); |
| 386 is_audio = true; | 385 is_audio = true; |
| 387 } else { | 386 } else { |
| 388 return; | 387 return; |
| 389 } | 388 } |
| 390 | 389 |
| 391 // Create the PES state here. | 390 // Create the PES state here. |
| 392 DVLOG(1) << "Create a new PES state"; | 391 DVLOG(1) << "Create a new PES state"; |
| 393 scoped_ptr<TsSection> pes_section_parser( | 392 std::unique_ptr<TsSection> pes_section_parser( |
| 394 new TsSectionPes(std::move(es_parser), ×tamp_unroller_)); | 393 new TsSectionPes(std::move(es_parser), ×tamp_unroller_)); |
| 395 PidState::PidType pid_type = | 394 PidState::PidType pid_type = |
| 396 is_audio ? PidState::kPidAudioPes : PidState::kPidVideoPes; | 395 is_audio ? PidState::kPidAudioPes : PidState::kPidVideoPes; |
| 397 scoped_ptr<PidState> pes_pid_state( | 396 std::unique_ptr<PidState> pes_pid_state( |
| 398 new PidState(pes_pid, pid_type, std::move(pes_section_parser))); | 397 new PidState(pes_pid, pid_type, std::move(pes_section_parser))); |
| 399 pids_.insert(std::pair<int, PidState*>(pes_pid, pes_pid_state.release())); | 398 pids_.insert(std::pair<int, PidState*>(pes_pid, pes_pid_state.release())); |
| 400 | 399 |
| 401 // A new PES pid has been added, the PID filter might change. | 400 // A new PES pid has been added, the PID filter might change. |
| 402 UpdatePidFilter(); | 401 UpdatePidFilter(); |
| 403 } | 402 } |
| 404 | 403 |
| 405 void Mp2tStreamParser::UpdatePidFilter() { | 404 void Mp2tStreamParser::UpdatePidFilter() { |
| 406 // Applies the HLS rule to select the default audio/video PIDs: | 405 // Applies the HLS rule to select the default audio/video PIDs: |
| 407 // select the audio/video streams with the lowest PID. | 406 // select the audio/video streams with the lowest PID. |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 499 // Replace any non valid config with the 1st valid entry. | 498 // Replace any non valid config with the 1st valid entry. |
| 500 // This might happen if there was no available config before. | 499 // This might happen if there was no available config before. |
| 501 for (std::list<BufferQueueWithConfig>::iterator it = | 500 for (std::list<BufferQueueWithConfig>::iterator it = |
| 502 buffer_queue_chain_.begin(); it != buffer_queue_chain_.end(); ++it) { | 501 buffer_queue_chain_.begin(); it != buffer_queue_chain_.end(); ++it) { |
| 503 if (it->audio_config.IsValidConfig()) | 502 if (it->audio_config.IsValidConfig()) |
| 504 break; | 503 break; |
| 505 it->audio_config = audio_decoder_config; | 504 it->audio_config = audio_decoder_config; |
| 506 } | 505 } |
| 507 } | 506 } |
| 508 | 507 |
| 509 scoped_ptr<MediaTracks> GenerateMediaTrackInfo( | 508 std::unique_ptr<MediaTracks> GenerateMediaTrackInfo( |
| 510 const AudioDecoderConfig& audio_config, | 509 const AudioDecoderConfig& audio_config, |
| 511 const VideoDecoderConfig& video_config) { | 510 const VideoDecoderConfig& video_config) { |
| 512 scoped_ptr<MediaTracks> media_tracks(new MediaTracks()); | 511 std::unique_ptr<MediaTracks> media_tracks(new MediaTracks()); |
| 513 // TODO(servolk): Implement proper sourcing of media track info as described | 512 // TODO(servolk): Implement proper sourcing of media track info as described |
| 514 // in crbug.com/590085 | 513 // in crbug.com/590085 |
| 515 if (audio_config.IsValidConfig()) { | 514 if (audio_config.IsValidConfig()) { |
| 516 media_tracks->AddAudioTrack(audio_config, "audio", "main", "", ""); | 515 media_tracks->AddAudioTrack(audio_config, "audio", "main", "", ""); |
| 517 } | 516 } |
| 518 if (video_config.IsValidConfig()) { | 517 if (video_config.IsValidConfig()) { |
| 519 media_tracks->AddVideoTrack(video_config, "video", "main", "", ""); | 518 media_tracks->AddVideoTrack(video_config, "video", "main", "", ""); |
| 520 } | 519 } |
| 521 return media_tracks; | 520 return media_tracks; |
| 522 } | 521 } |
| (...skipping 10 matching lines...) Expand all Loading... |
| 533 // Wait for more data to come if one of the config is not available. | 532 // Wait for more data to come if one of the config is not available. |
| 534 BufferQueueWithConfig& queue_with_config = buffer_queue_chain_.front(); | 533 BufferQueueWithConfig& queue_with_config = buffer_queue_chain_.front(); |
| 535 if (selected_audio_pid_ > 0 && | 534 if (selected_audio_pid_ > 0 && |
| 536 !queue_with_config.audio_config.IsValidConfig()) | 535 !queue_with_config.audio_config.IsValidConfig()) |
| 537 return true; | 536 return true; |
| 538 if (selected_video_pid_ > 0 && | 537 if (selected_video_pid_ > 0 && |
| 539 !queue_with_config.video_config.IsValidConfig()) | 538 !queue_with_config.video_config.IsValidConfig()) |
| 540 return true; | 539 return true; |
| 541 | 540 |
| 542 // Pass the config before invoking the initialization callback. | 541 // Pass the config before invoking the initialization callback. |
| 543 scoped_ptr<MediaTracks> media_tracks = GenerateMediaTrackInfo( | 542 std::unique_ptr<MediaTracks> media_tracks = GenerateMediaTrackInfo( |
| 544 queue_with_config.audio_config, queue_with_config.video_config); | 543 queue_with_config.audio_config, queue_with_config.video_config); |
| 545 RCHECK(config_cb_.Run(std::move(media_tracks), TextTrackConfigMap())); | 544 RCHECK(config_cb_.Run(std::move(media_tracks), TextTrackConfigMap())); |
| 546 queue_with_config.is_config_sent = true; | 545 queue_with_config.is_config_sent = true; |
| 547 | 546 |
| 548 // For Mpeg2 TS, the duration is not known. | 547 // For Mpeg2 TS, the duration is not known. |
| 549 DVLOG(1) << "Mpeg2TS stream parser initialization done"; | 548 DVLOG(1) << "Mpeg2TS stream parser initialization done"; |
| 550 | 549 |
| 551 // TODO(wolenetz): If possible, detect and report track counts by type more | 550 // TODO(wolenetz): If possible, detect and report track counts by type more |
| 552 // accurately here. Currently, capped at max 1 each for audio and video, with | 551 // accurately here. Currently, capped at max 1 each for audio and video, with |
| 553 // assumption of 0 text tracks. | 552 // assumption of 0 text tracks. |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 641 // Start a segment if needed. | 640 // Start a segment if needed. |
| 642 if (!segment_started_) { | 641 if (!segment_started_) { |
| 643 DVLOG(1) << "Starting a new segment"; | 642 DVLOG(1) << "Starting a new segment"; |
| 644 segment_started_ = true; | 643 segment_started_ = true; |
| 645 new_segment_cb_.Run(); | 644 new_segment_cb_.Run(); |
| 646 } | 645 } |
| 647 | 646 |
| 648 // Update the audio and video config if needed. | 647 // Update the audio and video config if needed. |
| 649 BufferQueueWithConfig& queue_with_config = buffer_queue_chain_.front(); | 648 BufferQueueWithConfig& queue_with_config = buffer_queue_chain_.front(); |
| 650 if (!queue_with_config.is_config_sent) { | 649 if (!queue_with_config.is_config_sent) { |
| 651 scoped_ptr<MediaTracks> media_tracks = GenerateMediaTrackInfo( | 650 std::unique_ptr<MediaTracks> media_tracks = GenerateMediaTrackInfo( |
| 652 queue_with_config.audio_config, queue_with_config.video_config); | 651 queue_with_config.audio_config, queue_with_config.video_config); |
| 653 if (!config_cb_.Run(std::move(media_tracks), TextTrackConfigMap())) | 652 if (!config_cb_.Run(std::move(media_tracks), TextTrackConfigMap())) |
| 654 return false; | 653 return false; |
| 655 queue_with_config.is_config_sent = true; | 654 queue_with_config.is_config_sent = true; |
| 656 } | 655 } |
| 657 | 656 |
| 658 // Add buffers. | 657 // Add buffers. |
| 659 TextBufferQueueMap empty_text_map; | 658 TextBufferQueueMap empty_text_map; |
| 660 if (!queue_with_config.audio_queue.empty() || | 659 if (!queue_with_config.audio_queue.empty() || |
| 661 !queue_with_config.video_queue.empty()) { | 660 !queue_with_config.video_queue.empty()) { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 673 // so that buffers with the same config can be added later on. | 672 // so that buffers with the same config can be added later on. |
| 674 BufferQueueWithConfig queue_with_config( | 673 BufferQueueWithConfig queue_with_config( |
| 675 true, last_audio_config, last_video_config); | 674 true, last_audio_config, last_video_config); |
| 676 buffer_queue_chain_.push_back(queue_with_config); | 675 buffer_queue_chain_.push_back(queue_with_config); |
| 677 | 676 |
| 678 return true; | 677 return true; |
| 679 } | 678 } |
| 680 | 679 |
| 681 } // namespace mp2t | 680 } // namespace mp2t |
| 682 } // namespace media | 681 } // namespace media |
| OLD | NEW |