Chromium Code Reviews| 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 <memory> |
| 8 #include <utility> | 8 #include <utility> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| 11 #include "base/callback_helpers.h" | 11 #include "base/callback_helpers.h" |
| 12 #include "media/base/media_tracks.h" | 12 #include "media/base/media_tracks.h" |
| 13 #include "media/base/stream_parser_buffer.h" | 13 #include "media/base/stream_parser_buffer.h" |
| 14 #include "media/base/text_track_config.h" | 14 #include "media/base/text_track_config.h" |
| 15 #include "media/base/timestamp_constants.h" | 15 #include "media/base/timestamp_constants.h" |
| 16 #include "media/formats/mp2t/descriptors.h" | |
| 16 #include "media/formats/mp2t/es_parser.h" | 17 #include "media/formats/mp2t/es_parser.h" |
| 17 #include "media/formats/mp2t/es_parser_adts.h" | 18 #include "media/formats/mp2t/es_parser_adts.h" |
| 18 #include "media/formats/mp2t/es_parser_h264.h" | 19 #include "media/formats/mp2t/es_parser_h264.h" |
| 19 #include "media/formats/mp2t/es_parser_mpeg1audio.h" | 20 #include "media/formats/mp2t/es_parser_mpeg1audio.h" |
| 20 #include "media/formats/mp2t/mp2t_common.h" | 21 #include "media/formats/mp2t/mp2t_common.h" |
| 21 #include "media/formats/mp2t/ts_packet.h" | 22 #include "media/formats/mp2t/ts_packet.h" |
| 22 #include "media/formats/mp2t/ts_section.h" | 23 #include "media/formats/mp2t/ts_section.h" |
| 23 #include "media/formats/mp2t/ts_section_pat.h" | 24 #include "media/formats/mp2t/ts_section_pat.h" |
| 24 #include "media/formats/mp2t/ts_section_pes.h" | 25 #include "media/formats/mp2t/ts_section_pes.h" |
| 25 #include "media/formats/mp2t/ts_section_pmt.h" | 26 #include "media/formats/mp2t/ts_section_pmt.h" |
| (...skipping 335 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 361 PidState* pid_state = pid_pair.second.get(); | 362 PidState* pid_state = pid_pair.second.get(); |
| 362 if (pid_state->pid_type() == PidState::kPidPmt) { | 363 if (pid_state->pid_type() == PidState::kPidPmt) { |
| 363 DVLOG_IF(1, pmt_pid != pid_pair.first) | 364 DVLOG_IF(1, pmt_pid != pid_pair.first) |
| 364 << "More than one program is defined"; | 365 << "More than one program is defined"; |
| 365 return; | 366 return; |
| 366 } | 367 } |
| 367 } | 368 } |
| 368 | 369 |
| 369 // Create the PMT state here if needed. | 370 // Create the PMT state here if needed. |
| 370 DVLOG(1) << "Create a new PMT parser"; | 371 DVLOG(1) << "Create a new PMT parser"; |
| 371 std::unique_ptr<TsSection> pmt_section_parser(new TsSectionPmt(base::Bind( | 372 std::unique_ptr<TsSection> pmt_section_parser(new TsSectionPmt( |
| 372 &Mp2tStreamParser::RegisterPes, base::Unretained(this), pmt_pid))); | 373 base::Bind(&Mp2tStreamParser::RegisterPes, base::Unretained(this)))); |
| 373 std::unique_ptr<PidState> pmt_pid_state( | 374 std::unique_ptr<PidState> pmt_pid_state( |
| 374 new PidState(pmt_pid, PidState::kPidPmt, std::move(pmt_section_parser))); | 375 new PidState(pmt_pid, PidState::kPidPmt, std::move(pmt_section_parser))); |
| 375 pmt_pid_state->Enable(); | 376 pmt_pid_state->Enable(); |
| 376 pids_.insert(std::make_pair(pmt_pid, std::move(pmt_pid_state))); | 377 pids_.insert(std::make_pair(pmt_pid, std::move(pmt_pid_state))); |
| 377 | 378 |
| 378 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES) | 379 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES) |
| 379 // Take the opportunity to clean up any PIDs that were involved in importing | 380 // Take the opportunity to clean up any PIDs that were involved in importing |
| 380 // encryption metadata for HLS with SampleAES. This prevents the possibility | 381 // encryption metadata for HLS with SampleAES. This prevents the possibility |
| 381 // of interference with actual PIDs that might be declared in the PMT. | 382 // of interference with actual PIDs that might be declared in the PMT. |
| 382 // TODO(dougsteed): if in the future the appropriate PIDs are embedded in the | 383 // TODO(dougsteed): if in the future the appropriate PIDs are embedded in the |
| 383 // source stream, this will not be necessary. | 384 // source stream, this will not be necessary. |
| 384 UnregisterCat(); | 385 UnregisterCat(); |
| 385 UnregisterCencPids(); | 386 UnregisterCencPids(); |
| 386 #endif | 387 #endif |
| 387 } | 388 } |
| 388 | 389 |
| 389 void Mp2tStreamParser::RegisterPes(int pmt_pid, | 390 std::unique_ptr<EsParser> Mp2tStreamParser::MaybeCreateH264Parser( |
| 390 int pes_pid, | 391 int pes_pid, |
| 392 int stream_type, | |
| 393 const Descriptors& descriptors) { | |
| 394 bool is_h264 = stream_type == kStreamTypeAVC; | |
| 395 bool enable_hls = false; | |
| 396 std::unique_ptr<EsParser> es_parser; | |
| 397 | |
| 398 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES) | |
| 399 if (is_h264 && !is_initialized_ && initial_scheme_.is_encrypted()) { | |
| 400 enable_hls = true; | |
| 401 } else if (stream_type == kStreamTypeAVCWithSampleAES && | |
| 402 descriptors.HasPrivateDataIndicator( | |
| 403 kSampleAESPrivateDataIndicatorAVC)) { | |
| 404 is_h264 = true; | |
| 405 enable_hls = true; | |
| 406 } | |
| 407 #endif | |
| 408 | |
| 409 if (!is_h264) | |
| 410 return nullptr; | |
| 411 | |
| 412 auto on_video_config_changed = base::Bind( | |
| 413 &Mp2tStreamParser::OnVideoConfigChanged, base::Unretained(this), pes_pid); | |
| 414 auto on_emit_video_buffer = base::Bind(&Mp2tStreamParser::OnEmitVideoBuffer, | |
| 415 base::Unretained(this), pes_pid); | |
| 416 if (!enable_hls) { | |
| 417 es_parser.reset( | |
| 418 new EsParserH264(on_video_config_changed, on_emit_video_buffer)); | |
| 419 } | |
| 420 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES) | |
| 421 else { | |
| 422 auto get_decrypt_config = | |
| 423 base::Bind(&Mp2tStreamParser::GetDecryptConfig, base::Unretained(this)); | |
| 424 es_parser.reset(new EsParserH264(on_video_config_changed, | |
| 425 on_emit_video_buffer, true, | |
| 426 get_decrypt_config)); | |
| 427 } | |
| 428 #endif | |
| 429 return es_parser; | |
| 430 } | |
| 431 | |
| 432 std::unique_ptr<EsParser> Mp2tStreamParser::MaybeCreateAACParser( | |
| 433 int pes_pid, | |
| 434 int stream_type, | |
| 435 const Descriptors& descriptors) { | |
| 436 bool is_AAC = stream_type == kStreamTypeAAC; | |
| 437 bool enable_hls = false; | |
| 438 std::unique_ptr<EsParser> es_parser; | |
| 439 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES) | |
| 440 if (is_AAC && !is_initialized_ && initial_scheme_.is_encrypted()) { | |
|
kqyang
2017/04/07 23:33:43
I understand that Marvel does not support switchin
dougsteed
2017/04/26 20:34:14
On this platform, we always need to initialize a d
kqyang
2017/05/06 00:06:12
Is this a limitation of Chromium media pipeline or
| |
| 441 enable_hls = true; | |
| 442 } else if (stream_type == kStreamTypeAACWithSampleAES && | |
| 443 descriptors.HasPrivateDataIndicator( | |
| 444 kSampleAESPrivateDataIndicatorAAC)) { | |
| 445 is_AAC = true; | |
| 446 enable_hls = true; | |
| 447 } | |
| 448 #endif | |
| 449 | |
| 450 if (!is_AAC) | |
| 451 return nullptr; | |
| 452 | |
| 453 auto on_audio_config_changed = base::Bind( | |
| 454 &Mp2tStreamParser::OnAudioConfigChanged, base::Unretained(this), pes_pid); | |
| 455 auto on_emit_audio_buffer = base::Bind(&Mp2tStreamParser::OnEmitAudioBuffer, | |
| 456 base::Unretained(this), pes_pid); | |
| 457 if (!enable_hls) { | |
| 458 es_parser.reset(new EsParserAdts(on_audio_config_changed, | |
| 459 on_emit_audio_buffer, sbr_in_mimetype_)); | |
| 460 } | |
| 461 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES) | |
| 462 else { | |
| 463 auto get_decrypt_config = | |
| 464 base::Bind(&Mp2tStreamParser::GetDecryptConfig, base::Unretained(this)); | |
| 465 es_parser.reset(new EsParserAdts(on_audio_config_changed, | |
| 466 on_emit_audio_buffer, get_decrypt_config, | |
| 467 true, sbr_in_mimetype_)); | |
| 468 } | |
| 469 #endif | |
| 470 return es_parser; | |
| 471 } | |
| 472 | |
| 473 std::unique_ptr<EsParser> Mp2tStreamParser::MaybeCreateMpeg1AudioParser( | |
| 474 int pes_pid, | |
| 475 int stream_type) { | |
| 476 if (stream_type != kStreamTypeMpeg1Audio) | |
|
servolk
2017/04/04 20:08:18
&& stream_type != kStreamTypeMpeg2Audio
dougsteed
2017/04/26 20:34:14
Done.
| |
| 477 return nullptr; | |
| 478 auto on_audio_config_changed = base::Bind( | |
| 479 &Mp2tStreamParser::OnAudioConfigChanged, base::Unretained(this), pes_pid); | |
| 480 auto on_emit_audio_buffer = base::Bind(&Mp2tStreamParser::OnEmitAudioBuffer, | |
| 481 base::Unretained(this), pes_pid); | |
| 482 std::unique_ptr<EsParser> es_parser(new EsParserMpeg1Audio( | |
| 483 on_audio_config_changed, on_emit_audio_buffer, media_log_)); | |
| 484 return es_parser; | |
| 485 } | |
| 486 | |
| 487 void Mp2tStreamParser::RegisterPes(int pes_pid, | |
| 391 int stream_type, | 488 int stream_type, |
| 392 const Descriptors& descriptors) { | 489 const Descriptors& descriptors) { |
| 393 // TODO(damienv): check there is no mismatch if the entry already exists. | 490 // TODO(damienv): check there is no mismatch if the entry already exists. |
| 394 DVLOG(1) << "RegisterPes:" | 491 DVLOG(1) << "RegisterPes:" |
| 395 << " pes_pid=" << pes_pid | 492 << " pes_pid=" << pes_pid |
| 396 << " stream_type=" << std::hex << stream_type << std::dec; | 493 << " stream_type=" << std::hex << stream_type << std::dec; |
| 397 auto it = pids_.find(pes_pid); | 494 auto it = pids_.find(pes_pid); |
| 398 if (it != pids_.end()) | 495 if (it != pids_.end()) |
| 399 return; | 496 return; |
| 400 | 497 |
| 401 // Create a stream parser corresponding to the stream type. | 498 // Create a stream parser corresponding to the stream type. |
| 402 bool is_audio = false; | 499 bool is_audio = true; |
| 403 std::unique_ptr<EsParser> es_parser; | 500 std::unique_ptr<EsParser> es_parser; |
| 404 if (stream_type == kStreamTypeAVC) { | 501 |
| 405 es_parser.reset( | 502 es_parser = MaybeCreateH264Parser(pes_pid, stream_type, descriptors); |
| 406 new EsParserH264( | 503 if (es_parser) |
| 407 base::Bind(&Mp2tStreamParser::OnVideoConfigChanged, | 504 is_audio = false; |
| 408 base::Unretained(this), | 505 else |
| 409 pes_pid), | 506 es_parser = MaybeCreateAACParser(pes_pid, stream_type, descriptors); |
| 410 base::Bind(&Mp2tStreamParser::OnEmitVideoBuffer, | 507 |
| 411 base::Unretained(this), | 508 if (!es_parser) |
| 412 pes_pid))); | 509 es_parser = MaybeCreateMpeg1AudioParser(pes_pid, stream_type); |
| 413 } else if (stream_type == kStreamTypeAAC) { | 510 |
| 414 es_parser.reset( | 511 if (!es_parser) |
|
kqyang
2017/04/07 23:33:43
I think the code would be cleaner and easier to ma
dougsteed
2017/04/26 20:34:14
Adopted this general approach, but with some varia
| |
| 415 new EsParserAdts( | |
| 416 base::Bind(&Mp2tStreamParser::OnAudioConfigChanged, | |
| 417 base::Unretained(this), | |
| 418 pes_pid), | |
| 419 base::Bind(&Mp2tStreamParser::OnEmitAudioBuffer, | |
| 420 base::Unretained(this), | |
| 421 pes_pid), | |
| 422 sbr_in_mimetype_)); | |
| 423 is_audio = true; | |
| 424 } else if (stream_type == kStreamTypeMpeg1Audio) { | |
| 425 es_parser.reset(new EsParserMpeg1Audio( | |
| 426 base::Bind(&Mp2tStreamParser::OnAudioConfigChanged, | |
| 427 base::Unretained(this), pes_pid), | |
| 428 base::Bind(&Mp2tStreamParser::OnEmitAudioBuffer, base::Unretained(this), | |
| 429 pes_pid), | |
| 430 media_log_)); | |
| 431 is_audio = true; | |
| 432 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES) | |
| 433 } else if (stream_type == kStreamTypeAVCWithSampleAES && | |
| 434 descriptors.HasPrivateDataIndicator( | |
| 435 kSampleAESPrivateDataIndicatorAVC)) { | |
| 436 es_parser.reset( | |
| 437 new EsParserH264(base::Bind(&Mp2tStreamParser::OnVideoConfigChanged, | |
| 438 base::Unretained(this), pes_pid), | |
| 439 base::Bind(&Mp2tStreamParser::OnEmitVideoBuffer, | |
| 440 base::Unretained(this), pes_pid), | |
| 441 true, base::Bind(&Mp2tStreamParser::GetDecryptConfig, | |
| 442 base::Unretained(this)))); | |
| 443 } else if (stream_type == kStreamTypeAACWithSampleAES && | |
| 444 descriptors.HasPrivateDataIndicator( | |
| 445 kSampleAESPrivateDataIndicatorAAC)) { | |
| 446 es_parser.reset(new EsParserAdts( | |
| 447 base::Bind(&Mp2tStreamParser::OnAudioConfigChanged, | |
| 448 base::Unretained(this), pes_pid), | |
| 449 base::Bind(&Mp2tStreamParser::OnEmitAudioBuffer, base::Unretained(this), | |
| 450 pes_pid), | |
| 451 base::Bind(&Mp2tStreamParser::GetDecryptConfig, base::Unretained(this)), | |
| 452 true, sbr_in_mimetype_)); | |
| 453 is_audio = true; | |
| 454 #endif | |
| 455 } else { | |
| 456 return; | 512 return; |
| 457 } | |
| 458 | 513 |
| 459 // Create the PES state here. | 514 // Create the PES state here. |
| 460 DVLOG(1) << "Create a new PES state"; | 515 DVLOG(1) << "Create a new PES state"; |
| 461 std::unique_ptr<TsSection> pes_section_parser( | 516 std::unique_ptr<TsSection> pes_section_parser( |
| 462 new TsSectionPes(std::move(es_parser), ×tamp_unroller_)); | 517 new TsSectionPes(std::move(es_parser), ×tamp_unroller_)); |
| 463 PidState::PidType pid_type = | 518 PidState::PidType pid_type = |
| 464 is_audio ? PidState::kPidAudioPes : PidState::kPidVideoPes; | 519 is_audio ? PidState::kPidAudioPes : PidState::kPidVideoPes; |
| 465 std::unique_ptr<PidState> pes_pid_state( | 520 std::unique_ptr<PidState> pes_pid_state( |
| 466 new PidState(pes_pid, pid_type, std::move(pes_section_parser))); | 521 new PidState(pes_pid, pid_type, std::move(pes_section_parser))); |
| 467 pids_.insert(std::make_pair(pes_pid, std::move(pes_pid_state))); | 522 pids_.insert(std::make_pair(pes_pid, std::move(pes_pid_state))); |
| (...skipping 277 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 745 BufferQueueWithConfig queue_with_config( | 800 BufferQueueWithConfig queue_with_config( |
| 746 true, last_audio_config, last_video_config); | 801 true, last_audio_config, last_video_config); |
| 747 buffer_queue_chain_.push_back(queue_with_config); | 802 buffer_queue_chain_.push_back(queue_with_config); |
| 748 | 803 |
| 749 return true; | 804 return true; |
| 750 } | 805 } |
| 751 | 806 |
| 752 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES) | 807 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES) |
| 753 std::unique_ptr<PidState> Mp2tStreamParser::MakeCatPidState() { | 808 std::unique_ptr<PidState> Mp2tStreamParser::MakeCatPidState() { |
| 754 std::unique_ptr<TsSection> cat_section_parser(new TsSectionCat( | 809 std::unique_ptr<TsSection> cat_section_parser(new TsSectionCat( |
| 755 base::Bind(&Mp2tStreamParser::RegisterCencPids, base::Unretained(this)))); | 810 base::Bind(&Mp2tStreamParser::RegisterCencPids, base::Unretained(this)), |
| 811 base::Bind(&Mp2tStreamParser::RegisterEncryptionScheme, | |
| 812 base::Unretained(this)))); | |
| 756 std::unique_ptr<PidState> cat_pid_state(new PidState( | 813 std::unique_ptr<PidState> cat_pid_state(new PidState( |
| 757 TsSection::kPidCat, PidState::kPidCat, std::move(cat_section_parser))); | 814 TsSection::kPidCat, PidState::kPidCat, std::move(cat_section_parser))); |
| 758 cat_pid_state->Enable(); | 815 cat_pid_state->Enable(); |
| 759 return cat_pid_state; | 816 return cat_pid_state; |
| 760 } | 817 } |
| 761 | 818 |
| 762 void Mp2tStreamParser::UnregisterCat() { | 819 void Mp2tStreamParser::UnregisterCat() { |
| 763 for (auto& pid : pids_) { | 820 for (auto& pid : pids_) { |
| 764 if (pid.second->pid_type() == PidState::kPidCat) { | 821 if (pid.second->pid_type() == PidState::kPidCat) { |
| 765 pids_.erase(pid.first); | 822 pids_.erase(pid.first); |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 793 } | 850 } |
| 794 } | 851 } |
| 795 for (auto& pid : pids_) { | 852 for (auto& pid : pids_) { |
| 796 if (pid.second->pid_type() == PidState::kPidCetsPssh) { | 853 if (pid.second->pid_type() == PidState::kPidCetsPssh) { |
| 797 pids_.erase(pid.first); | 854 pids_.erase(pid.first); |
| 798 break; | 855 break; |
| 799 } | 856 } |
| 800 } | 857 } |
| 801 } | 858 } |
| 802 | 859 |
| 860 void Mp2tStreamParser::RegisterEncryptionScheme( | |
| 861 const EncryptionScheme& scheme) { | |
| 862 // We only need to record this for the initial decoder config. | |
| 863 if (!is_initialized_) { | |
| 864 initial_scheme_ = scheme; | |
| 865 } | |
| 866 // Reset the DecryptConfig, so that unless and until a CENC-ECM (containing | |
| 867 // key id and IV) is seen, media data will be considered unencrypted. This is | |
| 868 // similar to the way clear leaders can occur in MP4 containers. | |
| 869 decrypt_config_.reset(nullptr); | |
| 870 } | |
| 871 | |
| 803 void Mp2tStreamParser::RegisterDecryptConfig(const DecryptConfig& config) { | 872 void Mp2tStreamParser::RegisterDecryptConfig(const DecryptConfig& config) { |
| 804 decrypt_config_.reset( | 873 decrypt_config_.reset( |
| 805 new DecryptConfig(config.key_id(), config.iv(), config.subsamples())); | 874 new DecryptConfig(config.key_id(), config.iv(), config.subsamples())); |
| 806 } | 875 } |
| 807 | 876 |
| 808 void Mp2tStreamParser::RegisterPsshBoxes( | 877 void Mp2tStreamParser::RegisterPsshBoxes( |
| 809 const std::vector<uint8_t>& init_data) { | 878 const std::vector<uint8_t>& init_data) { |
| 810 encrypted_media_init_data_cb_.Run(EmeInitDataType::CENC, init_data); | 879 encrypted_media_init_data_cb_.Run(EmeInitDataType::CENC, init_data); |
| 811 } | 880 } |
| 812 | 881 |
| 813 #endif | 882 #endif |
| 814 | 883 |
| 815 } // namespace mp2t | 884 } // namespace mp2t |
| 816 } // namespace media | 885 } // namespace media |
| OLD | NEW |