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

Side by Side Diff: media/formats/mp2t/mp2t_stream_parser.cc

Issue 1517473002: Support HLS MPEG2 TS with SAMPLE-AES encryption. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@encryption_scheme
Patch Set: move some gn defs Created 4 years, 8 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
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 <utility> 7 #include <utility>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/callback_helpers.h" 10 #include "base/callback_helpers.h"
11 #include "base/stl_util.h" 11 #include "base/stl_util.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/es_parser.h" 16 #include "media/formats/mp2t/es_parser.h"
17 #include "media/formats/mp2t/es_parser_adts.h" 17 #include "media/formats/mp2t/es_parser_adts.h"
18 #include "media/formats/mp2t/es_parser_h264.h" 18 #include "media/formats/mp2t/es_parser_h264.h"
19 #include "media/formats/mp2t/es_parser_mpeg1audio.h" 19 #include "media/formats/mp2t/es_parser_mpeg1audio.h"
20 #include "media/formats/mp2t/mp2t_common.h" 20 #include "media/formats/mp2t/mp2t_common.h"
21 #include "media/formats/mp2t/ts_packet.h" 21 #include "media/formats/mp2t/ts_packet.h"
22 #include "media/formats/mp2t/ts_section.h" 22 #include "media/formats/mp2t/ts_section.h"
23 #include "media/formats/mp2t/ts_section_pat.h" 23 #include "media/formats/mp2t/ts_section_pat.h"
24 #include "media/formats/mp2t/ts_section_pes.h" 24 #include "media/formats/mp2t/ts_section_pes.h"
25 #include "media/formats/mp2t/ts_section_pmt.h" 25 #include "media/formats/mp2t/ts_section_pmt.h"
26 26
27 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
28 #include "media/formats/mp2t/ts_section_cat.h"
29 #include "media/formats/mp2t/ts_section_cets_ecm.h"
30 #include "media/formats/mp2t/ts_section_cets_pssh.h"
31 #endif
32
27 namespace media { 33 namespace media {
28 namespace mp2t { 34 namespace mp2t {
29 35
36 namespace {
37
38 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
39 const int64_t kSampleAESPrivateDataIndicatorAVC = 0x7a617663;
40 const int64_t kSampleAESPrivateDataIndicatorAAC = 0x61616364;
41 // TODO(dougsteed). Consider adding support for the following:
42 // const int64_t kSampleAESPrivateDataIndicatorAC3 = 0x61633364;
43 // const int64_t kSampleAESPrivateDataIndicatorEAC3 = 0x65633364;
44 #endif
45
46 } // namespace
47
30 enum StreamType { 48 enum StreamType {
31 // ISO-13818.1 / ITU H.222 Table 2.34 "Stream type assignments" 49 // ISO-13818.1 / ITU H.222 Table 2.34 "Stream type assignments"
32 kStreamTypeMpeg1Audio = 0x3, 50 kStreamTypeMpeg1Audio = 0x3,
33 kStreamTypeAAC = 0xf, 51 kStreamTypeAAC = 0xf,
34 kStreamTypeAVC = 0x1b, 52 kStreamTypeAVC = 0x1b,
53 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
54 kStreamTypeAACWithSampleAES = 0xcf,
55 kStreamTypeAVCWithSampleAES = 0xdb,
56 // TODO(dougsteed). Consider adding support for the following:
57 // kStreamTypeAC3WithSampleAES = 0xc1,
58 // kStreamTypeEAC3WithSampleAES = 0xc2,
59 #endif
35 }; 60 };
36 61
37 class PidState { 62 class PidState {
38 public: 63 public:
39 enum PidType { 64 enum PidType {
40 kPidPat, 65 kPidPat,
41 kPidPmt, 66 kPidPmt,
42 kPidAudioPes, 67 kPidAudioPes,
43 kPidVideoPes, 68 kPidVideoPes,
69 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
70 kPidCat,
71 kPidCetsEcm,
72 kPidCetsPssh,
73 #endif
44 }; 74 };
45 75
46 PidState(int pid, PidType pid_tyoe, 76 PidState(int pid, PidType pid_tyoe,
47 scoped_ptr<TsSection> section_parser); 77 scoped_ptr<TsSection> section_parser);
48 78
49 // Extract the content of the TS packet and parse it. 79 // Extract the content of the TS packet and parse it.
50 // Return true if successful. 80 // Return true if successful.
51 bool PushTsPacket(const TsPacket& ts_packet); 81 bool PushTsPacket(const TsPacket& ts_packet);
52 82
53 // Flush the PID state (possibly emitting some pending frames) 83 // Flush the PID state (possibly emitting some pending frames)
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
190 encrypted_media_init_data_cb_ = encrypted_media_init_data_cb; 220 encrypted_media_init_data_cb_ = encrypted_media_init_data_cb;
191 new_segment_cb_ = new_segment_cb; 221 new_segment_cb_ = new_segment_cb;
192 end_of_segment_cb_ = end_of_segment_cb; 222 end_of_segment_cb_ = end_of_segment_cb;
193 media_log_ = media_log; 223 media_log_ = media_log;
194 } 224 }
195 225
196 void Mp2tStreamParser::Flush() { 226 void Mp2tStreamParser::Flush() {
197 DVLOG(1) << "Mp2tStreamParser::Flush"; 227 DVLOG(1) << "Mp2tStreamParser::Flush";
198 228
199 // Flush the buffers and reset the pids. 229 // Flush the buffers and reset the pids.
200 for (std::map<int, PidState*>::iterator it = pids_.begin(); 230 for (const auto& pid : pids_) {
201 it != pids_.end(); ++it) { 231 DVLOG(1) << "Flushing PID: " << pid.first;
202 DVLOG(1) << "Flushing PID: " << it->first; 232 PidState* pid_state = pid.second;
203 PidState* pid_state = it->second;
204 pid_state->Flush(); 233 pid_state->Flush();
205 delete pid_state; 234 delete pid_state;
206 } 235 }
207 pids_.clear(); 236 pids_.clear();
208 237
209 // Flush is invoked from SourceBuffer.abort/SourceState::ResetParserState, and 238 // Flush is invoked from SourceBuffer.abort/SourceState::ResetParserState, and
210 // MSE spec prohibits emitting new configs in ResetParserState algorithm (see 239 // MSE spec prohibits emitting new configs in ResetParserState algorithm (see
211 // https://w3c.github.io/media-source/#sourcebuffer-reset-parser-state, 240 // https://w3c.github.io/media-source/#sourcebuffer-reset-parser-state,
212 // 3.5.2 Reset Parser State states that new frames might be processed only in 241 // 3.5.2 Reset Parser State states that new frames might be processed only in
213 // PARSING_MEDIA_SEGMENT and therefore doesn't allow emitting new configs, 242 // PARSING_MEDIA_SEGMENT and therefore doesn't allow emitting new configs,
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
271 if (!ts_packet) { 300 if (!ts_packet) {
272 DVLOG(1) << "Error: invalid TS packet"; 301 DVLOG(1) << "Error: invalid TS packet";
273 ts_byte_queue_.Pop(1); 302 ts_byte_queue_.Pop(1);
274 continue; 303 continue;
275 } 304 }
276 DVLOG(LOG_LEVEL_TS) 305 DVLOG(LOG_LEVEL_TS)
277 << "Processing PID=" << ts_packet->pid() 306 << "Processing PID=" << ts_packet->pid()
278 << " start_unit=" << ts_packet->payload_unit_start_indicator(); 307 << " start_unit=" << ts_packet->payload_unit_start_indicator();
279 308
280 // Parse the section. 309 // Parse the section.
281 std::map<int, PidState*>::iterator it = pids_.find(ts_packet->pid()); 310 auto it = pids_.find(ts_packet->pid());
282 if (it == pids_.end() && 311 if (it == pids_.end() &&
283 ts_packet->pid() == TsSection::kPidPat) { 312 ts_packet->pid() == TsSection::kPidPat) {
284 // Create the PAT state here if needed. 313 // Create the PAT state here if needed.
285 scoped_ptr<TsSection> pat_section_parser( 314 scoped_ptr<TsSection> pat_section_parser(
286 new TsSectionPat( 315 new TsSectionPat(
287 base::Bind(&Mp2tStreamParser::RegisterPmt, 316 base::Bind(&Mp2tStreamParser::RegisterPmt,
288 base::Unretained(this)))); 317 base::Unretained(this))));
289 scoped_ptr<PidState> pat_pid_state(new PidState( 318 scoped_ptr<PidState> pat_pid_state(new PidState(
290 ts_packet->pid(), PidState::kPidPat, std::move(pat_section_parser))); 319 ts_packet->pid(), PidState::kPidPat, std::move(pat_section_parser)));
291 pat_pid_state->Enable(); 320 pat_pid_state->Enable();
292 it = pids_.insert( 321 it =
293 std::pair<int, PidState*>(ts_packet->pid(), 322 pids_.insert(PidMapElement(ts_packet->pid(), pat_pid_state.release()))
294 pat_pid_state.release())).first; 323 .first;
295 } 324 }
325 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
326 // We allow a CAT to appear as the first packet in the TS. This allows us to
327 // specify encryption metadata for HLS by injecting it as an extra TS packet
328 // at the front of the stream.
329 else if (it == pids_.end() && ts_packet->pid() == TsSection::kPidCat) {
330 it = RegisterCat();
331 }
332 #endif
296 333
297 if (it != pids_.end()) { 334 if (it != pids_.end()) {
298 if (!it->second->PushTsPacket(*ts_packet)) 335 if (!it->second->PushTsPacket(*ts_packet))
299 return false; 336 return false;
300 } else { 337 } else {
301 DVLOG(LOG_LEVEL_TS) << "Ignoring TS packet for pid: " << ts_packet->pid(); 338 DVLOG(LOG_LEVEL_TS) << "Ignoring TS packet for pid: " << ts_packet->pid();
302 } 339 }
303 340
304 // Go to the next packet. 341 // Go to the next packet.
305 ts_byte_queue_.Pop(TsPacket::kPacketSize); 342 ts_byte_queue_.Pop(TsPacket::kPacketSize);
306 } 343 }
307 344
308 RCHECK(FinishInitializationIfNeeded()); 345 RCHECK(FinishInitializationIfNeeded());
309 346
310 // Emit the A/V buffers that kept accumulating during TS parsing. 347 // Emit the A/V buffers that kept accumulating during TS parsing.
311 return EmitRemainingBuffers(); 348 return EmitRemainingBuffers();
312 } 349 }
313 350
314 void Mp2tStreamParser::RegisterPmt(int program_number, int pmt_pid) { 351 void Mp2tStreamParser::RegisterPmt(int program_number, int pmt_pid) {
315 DVLOG(1) << "RegisterPmt:" 352 DVLOG(1) << "RegisterPmt:"
316 << " program_number=" << program_number 353 << " program_number=" << program_number
317 << " pmt_pid=" << pmt_pid; 354 << " pmt_pid=" << pmt_pid;
318 355
319 // Only one TS program is allowed. Ignore the incoming program map table, 356 // Only one TS program is allowed. Ignore the incoming program map table,
320 // if there is already one registered. 357 // if there is already one registered.
321 for (std::map<int, PidState*>::iterator it = pids_.begin(); 358 for (const auto& pid : pids_) {
322 it != pids_.end(); ++it) { 359 PidState* pid_state = pid.second;
323 PidState* pid_state = it->second;
324 if (pid_state->pid_type() == PidState::kPidPmt) { 360 if (pid_state->pid_type() == PidState::kPidPmt) {
325 DVLOG_IF(1, pmt_pid != it->first) << "More than one program is defined"; 361 DVLOG_IF(1, pmt_pid != pid.first) << "More than one program is defined";
326 return; 362 return;
327 } 363 }
328 } 364 }
329 365
330 // Create the PMT state here if needed. 366 // Create the PMT state here if needed.
331 DVLOG(1) << "Create a new PMT parser"; 367 DVLOG(1) << "Create a new PMT parser";
332 scoped_ptr<TsSection> pmt_section_parser( 368 scoped_ptr<TsSection> pmt_section_parser(
333 new TsSectionPmt( 369 new TsSectionPmt(
334 base::Bind(&Mp2tStreamParser::RegisterPes, 370 base::Bind(&Mp2tStreamParser::RegisterPes,
335 base::Unretained(this), pmt_pid))); 371 base::Unretained(this), pmt_pid)));
336 scoped_ptr<PidState> pmt_pid_state( 372 scoped_ptr<PidState> pmt_pid_state(
337 new PidState(pmt_pid, PidState::kPidPmt, std::move(pmt_section_parser))); 373 new PidState(pmt_pid, PidState::kPidPmt, std::move(pmt_section_parser)));
338 pmt_pid_state->Enable(); 374 pmt_pid_state->Enable();
339 pids_.insert(std::pair<int, PidState*>(pmt_pid, pmt_pid_state.release())); 375 pids_.insert(std::pair<int, PidState*>(pmt_pid, pmt_pid_state.release()));
376
377 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
378 // Take the opportunity to clean up any PIDs that were involved in importing
379 // encryption metadata for HLS with SampleAES. This prevents the possibility
380 // of interference with actual PIDs that might be declared in the PMT.
381 // TODO(dougsteed): if in the future the appropriate PIDs are embedded in the
382 // source stream, this will not be necessary.
383 UnregisterCat();
384 UnregisterCencPids();
385 #endif
340 } 386 }
341 387
342 void Mp2tStreamParser::RegisterPes(int pmt_pid, 388 void Mp2tStreamParser::RegisterPes(int pmt_pid,
343 int pes_pid, 389 int pes_pid,
344 int stream_type) { 390 int stream_type,
391 const Descriptors& descriptors) {
345 // TODO(damienv): check there is no mismatch if the entry already exists. 392 // TODO(damienv): check there is no mismatch if the entry already exists.
346 DVLOG(1) << "RegisterPes:" 393 DVLOG(1) << "RegisterPes:"
347 << " pes_pid=" << pes_pid 394 << " pes_pid=" << pes_pid
348 << " stream_type=" << std::hex << stream_type << std::dec; 395 << " stream_type=" << std::hex << stream_type << std::dec;
349 std::map<int, PidState*>::iterator it = pids_.find(pes_pid); 396 std::map<int, PidState*>::iterator it = pids_.find(pes_pid);
350 if (it != pids_.end()) 397 if (it != pids_.end())
351 return; 398 return;
352 399
353 // Create a stream parser corresponding to the stream type. 400 // Create a stream parser corresponding to the stream type.
354 bool is_audio = false; 401 bool is_audio = false;
(...skipping 19 matching lines...) Expand all
374 sbr_in_mimetype_)); 421 sbr_in_mimetype_));
375 is_audio = true; 422 is_audio = true;
376 } else if (stream_type == kStreamTypeMpeg1Audio) { 423 } else if (stream_type == kStreamTypeMpeg1Audio) {
377 es_parser.reset(new EsParserMpeg1Audio( 424 es_parser.reset(new EsParserMpeg1Audio(
378 base::Bind(&Mp2tStreamParser::OnAudioConfigChanged, 425 base::Bind(&Mp2tStreamParser::OnAudioConfigChanged,
379 base::Unretained(this), pes_pid), 426 base::Unretained(this), pes_pid),
380 base::Bind(&Mp2tStreamParser::OnEmitAudioBuffer, base::Unretained(this), 427 base::Bind(&Mp2tStreamParser::OnEmitAudioBuffer, base::Unretained(this),
381 pes_pid), 428 pes_pid),
382 media_log_)); 429 media_log_));
383 is_audio = true; 430 is_audio = true;
431 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
432 } else if (stream_type == kStreamTypeAVCWithSampleAES &&
433 descriptors.HasPrivateDataIndicator(
434 kSampleAESPrivateDataIndicatorAVC)) {
435 es_parser.reset(new EsParserH264(
436 base::Bind(&Mp2tStreamParser::OnVideoConfigChanged,
437 base::Unretained(this), pes_pid),
438 base::Bind(&Mp2tStreamParser::OnEmitVideoBuffer, base::Unretained(this),
439 pes_pid),
440 base::Bind(&Mp2tStreamParser::GetDecryptConfig, base::Unretained(this)),
441 true));
442 } else if (stream_type == kStreamTypeAACWithSampleAES &&
443 descriptors.HasPrivateDataIndicator(
444 kSampleAESPrivateDataIndicatorAAC)) {
445 es_parser.reset(new EsParserAdts(
446 base::Bind(&Mp2tStreamParser::OnAudioConfigChanged,
447 base::Unretained(this), pes_pid),
448 base::Bind(&Mp2tStreamParser::OnEmitAudioBuffer, base::Unretained(this),
449 pes_pid),
450 base::Bind(&Mp2tStreamParser::GetDecryptConfig, base::Unretained(this)),
451 true, sbr_in_mimetype_));
452 is_audio = true;
453 #endif
384 } else { 454 } else {
385 return; 455 return;
386 } 456 }
387 457
388 // Create the PES state here. 458 // Create the PES state here.
389 DVLOG(1) << "Create a new PES state"; 459 DVLOG(1) << "Create a new PES state";
390 scoped_ptr<TsSection> pes_section_parser( 460 scoped_ptr<TsSection> pes_section_parser(
391 new TsSectionPes(std::move(es_parser), &timestamp_unroller_)); 461 new TsSectionPes(std::move(es_parser), &timestamp_unroller_));
392 PidState::PidType pid_type = 462 PidState::PidType pid_type =
393 is_audio ? PidState::kPidAudioPes : PidState::kPidVideoPes; 463 is_audio ? PidState::kPidAudioPes : PidState::kPidVideoPes;
(...skipping 265 matching lines...) Expand 10 before | Expand all | Expand 10 after
659 729
660 // Push an empty queue with the last audio/video config 730 // Push an empty queue with the last audio/video config
661 // so that buffers with the same config can be added later on. 731 // so that buffers with the same config can be added later on.
662 BufferQueueWithConfig queue_with_config( 732 BufferQueueWithConfig queue_with_config(
663 true, last_audio_config, last_video_config); 733 true, last_audio_config, last_video_config);
664 buffer_queue_chain_.push_back(queue_with_config); 734 buffer_queue_chain_.push_back(queue_with_config);
665 735
666 return true; 736 return true;
667 } 737 }
668 738
739 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
740 std::map<int, PidState*>::iterator Mp2tStreamParser::RegisterCat() {
741 scoped_ptr<TsSection> cat_section_parser(new TsSectionCat(
742 base::Bind(&Mp2tStreamParser::RegisterCencPids, base::Unretained(this))));
743 scoped_ptr<PidState> cat_pid_state(new PidState(
744 TsSection::kPidCat, PidState::kPidCat, std::move(cat_section_parser)));
745 cat_pid_state->Enable();
746 return pids_
747 .insert(PidMapElement(TsSection::kPidCat, cat_pid_state.release()))
748 .first;
749 }
750
751 void Mp2tStreamParser::UnregisterCat() {
752 for (auto& pid : pids_) {
753 if (pid.second->pid_type() == PidState::kPidCat) {
754 delete pid.second;
755 pids_.erase(pid.first);
756 break;
757 }
758 }
759 }
760
761 void Mp2tStreamParser::RegisterCencPids(int ca_pid, int pssh_pid) {
762 scoped_ptr<TsSectionCetsEcm> ecm_parser(new TsSectionCetsEcm(base::Bind(
763 &Mp2tStreamParser::RegisterDecryptConfig, base::Unretained(this))));
764 scoped_ptr<PidState> ecm_pid_state(
765 new PidState(ca_pid, PidState::kPidCetsEcm, std::move(ecm_parser)));
766 ecm_pid_state->Enable();
767 pids_.insert(PidMapElement(ca_pid, ecm_pid_state.release()));
768
769 scoped_ptr<TsSectionCetsPssh> pssh_parser(new TsSectionCetsPssh(base::Bind(
770 &Mp2tStreamParser::RegisterPsshBoxes, base::Unretained(this))));
771 scoped_ptr<PidState> pssh_pid_state(
772 new PidState(pssh_pid, PidState::kPidCetsPssh, std::move(pssh_parser)));
773 pssh_pid_state->Enable();
774 pids_.insert(PidMapElement(pssh_pid, pssh_pid_state.release()));
775 }
776
777 void Mp2tStreamParser::UnregisterCencPids() {
778 for (auto& pid : pids_) {
779 if (pid.second->pid_type() == PidState::kPidCetsEcm) {
780 delete pid.second;
781 pids_.erase(pid.first);
782 break;
783 }
784 }
785 for (auto& pid : pids_) {
786 if (pid.second->pid_type() == PidState::kPidCetsPssh) {
787 delete pid.second;
788 pids_.erase(pid.first);
789 break;
790 }
791 }
792 }
793
794 void Mp2tStreamParser::RegisterDecryptConfig(const DecryptConfig& config) {
795 decrypt_config_.reset(
796 new DecryptConfig(config.key_id(), config.iv(), config.subsamples()));
797 }
798
799 void Mp2tStreamParser::RegisterPsshBoxes(
800 const std::vector<uint8_t>& init_data) {
801 encrypted_media_init_data_cb_.Run(EmeInitDataType::CENC, init_data);
802 }
803
804 #endif
805
669 } // namespace mp2t 806 } // namespace mp2t
670 } // namespace media 807 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698