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

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

Powered by Google App Engine
This is Rietveld 408576698