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

Side by Side Diff: media/formats/mp2t/es_parser_h264.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, 7 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/es_parser_h264.h" 5 #include "media/formats/mp2t/es_parser_h264.h"
6 6
7 #include <limits> 7 #include <limits>
8 8
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/numerics/safe_conversions.h" 10 #include "base/numerics/safe_conversions.h"
11 #include "media/base/decrypt_config.h"
11 #include "media/base/encryption_scheme.h" 12 #include "media/base/encryption_scheme.h"
12 #include "media/base/media_util.h" 13 #include "media/base/media_util.h"
13 #include "media/base/stream_parser_buffer.h" 14 #include "media/base/stream_parser_buffer.h"
14 #include "media/base/timestamp_constants.h" 15 #include "media/base/timestamp_constants.h"
15 #include "media/base/video_frame.h" 16 #include "media/base/video_frame.h"
16 #include "media/filters/h264_parser.h" 17 #include "media/filters/h264_parser.h"
17 #include "media/formats/common/offset_byte_queue.h" 18 #include "media/formats/common/offset_byte_queue.h"
18 #include "media/formats/mp2t/mp2t_common.h" 19 #include "media/formats/mp2t/mp2t_common.h"
19 #include "ui/gfx/geometry/rect.h" 20 #include "ui/gfx/geometry/rect.h"
20 #include "ui/gfx/geometry/size.h" 21 #include "ui/gfx/geometry/size.h"
(...skipping 23 matching lines...) Expand all
44 return H264PROFILE_SCALABLEHIGH; 45 return H264PROFILE_SCALABLEHIGH;
45 case H264SPS::kProfileIDStereoHigh: 46 case H264SPS::kProfileIDStereoHigh:
46 return H264PROFILE_STEREOHIGH; 47 return H264PROFILE_STEREOHIGH;
47 case H264SPS::kProfileIDSMultiviewHigh: 48 case H264SPS::kProfileIDSMultiviewHigh:
48 return H264PROFILE_MULTIVIEWHIGH; 49 return H264PROFILE_MULTIVIEWHIGH;
49 } 50 }
50 NOTREACHED() << "unknown video profile: " << profile_idc; 51 NOTREACHED() << "unknown video profile: " << profile_idc;
51 return VIDEO_CODEC_PROFILE_UNKNOWN; 52 return VIDEO_CODEC_PROFILE_UNKNOWN;
52 } 53 }
53 54
55 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
56
57 const int kSampleAESMaxUnprotectedNALULength = 48;
58 const int kSampleAESClearLeaderSize = 32;
59 const int kSampleAESEncryptBlocks = 1;
60 const int kSampleAESSkipBlocks = 9;
61 const int kSampleAESPatternUnit =
62 (kSampleAESEncryptBlocks + kSampleAESSkipBlocks) * 16;
63
64 // Attempts to find the first or only EP3B (emulation prevention 3 byte) in
65 // the part of the |buffer| between |start_pos| and |end_pos|. Returns the
66 // position of the EP3B, or 0 if there are none.
67 // Note: the EP3B always follows two zero bytes, so the value 0 can never be a
68 // valid position.
69 int FindEP3B(const uint8_t* buffer, int start_pos, int end_pos) {
70 const uint8_t* data = buffer + start_pos;
71 int data_size = end_pos - start_pos;
72 DCHECK_GE(data_size, 0);
73 int bytes_left = data_size;
74
75 while (bytes_left >= 4) {
76 if (data[0] == 0x00 && data[1] == 0x00 && data[2] == 0x03 &&
77 data[3] <= 0x03) {
78 return (data - buffer) + 2;
79 }
80 ++data;
81 --bytes_left;
82 }
83 return 0;
84 }
85
86 // Remove the byte at |pos| in the |buffer| and close up the gap, moving all the
87 // bytes from [pos + 1, end_pos) to [pos, end_pos - 1).
88 void RemoveByte(uint8_t* buffer, int pos, int end_pos) {
89 memmove(&buffer[pos], &buffer[pos + 1], end_pos - pos - 1);
90 }
91
92 // Given an Access Unit pointed to by |au| of size |au_size|, removes emulation
93 // prevention 3 bytes (EP3B) from within the |protected_blocks|. Also computes
94 // the |subsamples| vector describing the resulting AU.
95 // Returns the allocated buffer holding the adjusted copy, or NULL if no size
96 // adjustment was necessary.
97 std::unique_ptr<uint8_t[]> AdjustAUForSampleAES(
98 const uint8_t* au,
99 int* au_size,
100 const Ranges<int>& protected_blocks,
101 std::vector<SubsampleEntry>* subsamples) {
102 DCHECK(subsamples);
103 DCHECK(au_size);
104 std::unique_ptr<uint8_t[]> result;
105 int& au_end_pos = *au_size;
106
107 // 1. Considering each protected block in turn, find any emulation prevention
108 // 3 bytes (EP3B) within it, keeping track of their positions. While doing so,
109 // produce a revised Ranges<int> reflecting the new protected block positions
110 // that will apply after we have removed the EP3Bs.
111 Ranges<int> adjusted_protected_blocks;
112 std::vector<int> epbs;
113 int adjustment = 0;
114 for (size_t i = 0; i < protected_blocks.size(); i++) {
115 int start_pos = protected_blocks.start(i) - adjustment;
116 int end_pos = protected_blocks.end(i) - adjustment;
117 int search_pos = start_pos;
118 int epb_pos;
119 int block_adjustment = 0;
120 while ((epb_pos = FindEP3B(au, search_pos, end_pos))) {
121 epbs.push_back(epb_pos);
122 block_adjustment++;
123 search_pos = epb_pos + 2;
124 }
125 end_pos -= block_adjustment;
126 adjustment += block_adjustment;
127 adjusted_protected_blocks.Add(start_pos, end_pos);
128 }
129
130 // 2. If we actually found any EP3Bs, make a copy of the AU and then remove
131 // the EP3Bs in the copy (we can't modify the original).
132 if (adjustment) {
133 result.reset(new uint8_t[au_end_pos]);
134 uint8_t* temp = result.get();
135 memcpy(temp, au, au_end_pos);
136 for (auto epb_pos = epbs.rbegin(); epb_pos != epbs.rend(); ++epb_pos) {
137 RemoveByte(temp, *epb_pos, au_end_pos);
138 au_end_pos--;
139 }
140 au = temp;
141 }
142
143 // We now have either the original AU, or a copy with the EP3Bs removed.
144 // We also have an updated Ranges<int> indicating the protected blocks.
145 // Also au_end_pos has been adjusted to indicate the new au_size.
146
147 // 3. Use a new Ranges<int> to collect all the clear ranges. They will
148 // automatically be coalesced to minimize the number of (disjoint) ranges.
149 Ranges<int> clear_ranges;
150 int previous_pos = 0;
151 for (size_t i = 0; i < adjusted_protected_blocks.size(); i++) {
152 int start_pos = adjusted_protected_blocks.start(i);
153 int end_pos = adjusted_protected_blocks.end(i);
154 // Add the clear range prior to this protected block.
155 clear_ranges.Add(previous_pos, start_pos);
156 int block_size = end_pos - start_pos;
157 DCHECK_GT(block_size, kSampleAESMaxUnprotectedNALULength);
158 // Add the clear leader.
159 clear_ranges.Add(start_pos, start_pos + kSampleAESClearLeaderSize);
160 block_size -= kSampleAESClearLeaderSize;
161 // The bytes beyond an integral multiple of AES blocks (16 bytes) are to be
162 // left clear. Also, if the last 16 bytes would be the only block in a
163 // pattern unit (160 bytes), they are also left clear.
164 int residual_bytes = block_size % kSampleAESPatternUnit;
165 if (residual_bytes > 16)
166 residual_bytes = residual_bytes % 16;
167 clear_ranges.Add(end_pos - residual_bytes, end_pos);
168 previous_pos = end_pos;
169 }
170 // Add the trailing bytes, if any, beyond the last protected block.
171 clear_ranges.Add(previous_pos, au_end_pos);
172
173 // 4. Convert the disjoint set of clear ranges into subsample entries. Each
174 // subsample entry is a count of clear bytes followed by a count of protected
175 // bytes.
176 subsamples->clear();
177 for (size_t i = 0; i < clear_ranges.size(); i++) {
178 int start_pos = clear_ranges.start(i);
179 int end_pos = clear_ranges.end(i);
180 int clear_size = end_pos - start_pos;
181 int encrypt_end_pos = au_end_pos;
182
183 if (i + 1 < clear_ranges.size())
184 encrypt_end_pos = clear_ranges.start(i + 1);
185 SubsampleEntry subsample(clear_size, encrypt_end_pos - end_pos);
186 subsamples->push_back(subsample);
187 }
188 return result;
189 }
190
191 #endif // BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
54 } // namespace 192 } // namespace
55 193
56 // An AUD NALU is at least 4 bytes: 194 // An AUD NALU is at least 4 bytes:
57 // 3 bytes for the start code + 1 byte for the NALU type. 195 // 3 bytes for the start code + 1 byte for the NALU type.
58 const int kMinAUDSize = 4; 196 const int kMinAUDSize = 4;
59 197
60 EsParserH264::EsParserH264( 198 EsParserH264::EsParserH264(const NewVideoConfigCB& new_video_config_cb,
61 const NewVideoConfigCB& new_video_config_cb, 199 const EmitBufferCB& emit_buffer_cb)
62 const EmitBufferCB& emit_buffer_cb)
63 : es_adapter_(new_video_config_cb, emit_buffer_cb), 200 : es_adapter_(new_video_config_cb, emit_buffer_cb),
64 h264_parser_(new H264Parser()), 201 h264_parser_(new H264Parser()),
65 current_access_unit_pos_(0), 202 current_access_unit_pos_(0),
66 next_access_unit_pos_(0) { 203 next_access_unit_pos_(0)
204 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
205 ,
206 use_hls_sample_aes_(false),
207 get_decrypt_config_cb_()
208 #endif
209 {
67 } 210 }
68 211
212 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
213 EsParserH264::EsParserH264(const NewVideoConfigCB& new_video_config_cb,
214 const EmitBufferCB& emit_buffer_cb,
215 bool use_hls_sample_aes,
216 const GetDecryptConfigCB& get_decrypt_config_cb)
217 : es_adapter_(new_video_config_cb, emit_buffer_cb),
218 h264_parser_(new H264Parser()),
219 current_access_unit_pos_(0),
220 next_access_unit_pos_(0),
221 use_hls_sample_aes_(use_hls_sample_aes),
222 get_decrypt_config_cb_(get_decrypt_config_cb) {
223 DCHECK_EQ(!get_decrypt_config_cb_.is_null(), use_hls_sample_aes_);
224 }
225 #endif
226
69 EsParserH264::~EsParserH264() { 227 EsParserH264::~EsParserH264() {
70 } 228 }
71 229
72 void EsParserH264::Flush() { 230 void EsParserH264::Flush() {
73 DVLOG(1) << __FUNCTION__; 231 DVLOG(1) << __FUNCTION__;
74 if (!FindAUD(&current_access_unit_pos_)) 232 if (!FindAUD(&current_access_unit_pos_))
75 return; 233 return;
76 234
77 // Simulate an additional AUD to force emitting the last access unit 235 // Simulate an additional AUD to force emitting the last access unit
78 // which is assumed to be complete at this point. 236 // which is assumed to be complete at this point.
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after
206 if (h264_parser_->ParseSliceHeader(nalu, &shdr) != H264Parser::kOk) { 364 if (h264_parser_->ParseSliceHeader(nalu, &shdr) != H264Parser::kOk) {
207 // Only accept an invalid SPS/PPS at the beginning when the stream 365 // Only accept an invalid SPS/PPS at the beginning when the stream
208 // does not necessarily start with an SPS/PPS/IDR. 366 // does not necessarily start with an SPS/PPS/IDR.
209 // TODO(damienv): Should be able to differentiate a missing SPS/PPS 367 // TODO(damienv): Should be able to differentiate a missing SPS/PPS
210 // from a slice header parsing error. 368 // from a slice header parsing error.
211 if (last_video_decoder_config_.IsValidConfig()) 369 if (last_video_decoder_config_.IsValidConfig())
212 return false; 370 return false;
213 } else { 371 } else {
214 pps_id_for_access_unit = shdr.pic_parameter_set_id; 372 pps_id_for_access_unit = shdr.pic_parameter_set_id;
215 } 373 }
374 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
375 // With HLS SampleAES, protected blocks in H.264 consist of IDR and non-
376 // IDR slices that are more than 48 bytes in length.
377 if (use_hls_sample_aes_ &&
378 nalu.size > kSampleAESMaxUnprotectedNALULength) {
379 int64_t nal_begin = nalu.data - es;
380 protected_blocks_.Add(nal_begin, nal_begin + nalu.size);
381 }
382 #endif
216 break; 383 break;
217 } 384 }
218 default: { 385 default: {
219 DVLOG(LOG_LEVEL_ES) << "NALU: " << nalu.nal_unit_type; 386 DVLOG(LOG_LEVEL_ES) << "NALU: " << nalu.nal_unit_type;
220 } 387 }
221 } 388 }
222 } 389 }
223 390
224 // Emit a frame and move the stream to the next AUD position. 391 // Emit a frame and move the stream to the next AUD position.
225 RCHECK(EmitFrame(current_access_unit_pos_, access_unit_size, 392 RCHECK(EmitFrame(current_access_unit_pos_, access_unit_size,
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
259 // does not necessarily start with an SPS/PPS/IDR. 426 // does not necessarily start with an SPS/PPS/IDR.
260 // In this case, the initial frames are conveyed to the upper layer with 427 // In this case, the initial frames are conveyed to the upper layer with
261 // an invalid VideoDecoderConfig and it's up to the upper layer 428 // an invalid VideoDecoderConfig and it's up to the upper layer
262 // to process this kind of frame accordingly. 429 // to process this kind of frame accordingly.
263 if (last_video_decoder_config_.IsValidConfig()) 430 if (last_video_decoder_config_.IsValidConfig())
264 return false; 431 return false;
265 } else { 432 } else {
266 const H264SPS* sps = h264_parser_->GetSPS(pps->seq_parameter_set_id); 433 const H264SPS* sps = h264_parser_->GetSPS(pps->seq_parameter_set_id);
267 if (!sps) 434 if (!sps)
268 return false; 435 return false;
269 RCHECK(UpdateVideoDecoderConfig(sps, Unencrypted())); 436 EncryptionScheme scheme = Unencrypted();
437 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
438 if (use_hls_sample_aes_) {
439 // Note that for SampleAES the (encrypt,skip) pattern is constant.
440 scheme =
441 EncryptionScheme(EncryptionScheme::CIPHER_MODE_AES_CBC,
442 EncryptionScheme::Pattern(kSampleAESEncryptBlocks,
443 kSampleAESSkipBlocks));
444 }
445 #endif
446 RCHECK(UpdateVideoDecoderConfig(sps, scheme));
270 } 447 }
271 448
272 // Emit a frame. 449 // Emit a frame.
273 DVLOG(LOG_LEVEL_ES) << "Emit frame: stream_pos=" << current_access_unit_pos_ 450 DVLOG(LOG_LEVEL_ES) << "Emit frame: stream_pos=" << current_access_unit_pos_
274 << " size=" << access_unit_size; 451 << " size=" << access_unit_size;
275 int es_size; 452 int es_size;
276 const uint8_t* es; 453 const uint8_t* es;
277 es_queue_->PeekAt(current_access_unit_pos_, &es, &es_size); 454 es_queue_->PeekAt(current_access_unit_pos_, &es, &es_size);
278 CHECK_GE(es_size, access_unit_size); 455 CHECK_GE(es_size, access_unit_size);
279 456
457 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
458 std::unique_ptr<uint8_t[]> adjusted_au;
459 std::vector<SubsampleEntry> subsamples;
460 if (use_hls_sample_aes_) {
461 adjusted_au = AdjustAUForSampleAES(es, &access_unit_size, protected_blocks_,
462 &subsamples);
463 protected_blocks_.clear();
464 if (adjusted_au)
465 es = adjusted_au.get();
466 }
467 #endif
468
280 // TODO(wolenetz/acolwell): Validate and use a common cross-parser TrackId 469 // TODO(wolenetz/acolwell): Validate and use a common cross-parser TrackId
281 // type and allow multiple video tracks. See https://crbug.com/341581. 470 // type and allow multiple video tracks. See https://crbug.com/341581.
282 scoped_refptr<StreamParserBuffer> stream_parser_buffer = 471 scoped_refptr<StreamParserBuffer> stream_parser_buffer =
283 StreamParserBuffer::CopyFrom( 472 StreamParserBuffer::CopyFrom(
284 es, 473 es,
285 access_unit_size, 474 access_unit_size,
286 is_key_frame, 475 is_key_frame,
287 DemuxerStream::VIDEO, 476 DemuxerStream::VIDEO,
288 0); 477 0);
289 stream_parser_buffer->SetDecodeTimestamp(current_timing_desc.dts); 478 stream_parser_buffer->SetDecodeTimestamp(current_timing_desc.dts);
290 stream_parser_buffer->set_timestamp(current_timing_desc.pts); 479 stream_parser_buffer->set_timestamp(current_timing_desc.pts);
480 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
481 if (use_hls_sample_aes_) {
482 DCHECK(!get_decrypt_config_cb_.is_null());
483 const DecryptConfig* base_decrypt_config = get_decrypt_config_cb_.Run();
484 RCHECK(base_decrypt_config);
485 std::unique_ptr<DecryptConfig> decrypt_config(new DecryptConfig(
486 base_decrypt_config->key_id(), base_decrypt_config->iv(), subsamples));
487 stream_parser_buffer->set_decrypt_config(std::move(decrypt_config));
488 }
489 #endif
291 return es_adapter_.OnNewBuffer(stream_parser_buffer); 490 return es_adapter_.OnNewBuffer(stream_parser_buffer);
292 } 491 }
293 492
294 bool EsParserH264::UpdateVideoDecoderConfig(const H264SPS* sps, 493 bool EsParserH264::UpdateVideoDecoderConfig(const H264SPS* sps,
295 const EncryptionScheme& scheme) { 494 const EncryptionScheme& scheme) {
296 // Set the SAR to 1 when not specified in the H264 stream. 495 // Set the SAR to 1 when not specified in the H264 stream.
297 int sar_width = (sps->sar_width == 0) ? 1 : sps->sar_width; 496 int sar_width = (sps->sar_width == 0) ? 1 : sps->sar_width;
298 int sar_height = (sps->sar_height == 0) ? 1 : sps->sar_height; 497 int sar_height = (sps->sar_height == 0) ? 1 : sps->sar_height;
299 498
300 // TODO(damienv): a MAP unit can be either 16 or 32 pixels. 499 // TODO(damienv): a MAP unit can be either 16 or 32 pixels.
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
345 << " height=" << sps->sar_height; 544 << " height=" << sps->sar_height;
346 last_video_decoder_config_ = video_decoder_config; 545 last_video_decoder_config_ = video_decoder_config;
347 es_adapter_.OnConfigChanged(video_decoder_config); 546 es_adapter_.OnConfigChanged(video_decoder_config);
348 } 547 }
349 548
350 return true; 549 return true;
351 } 550 }
352 551
353 } // namespace mp2t 552 } // namespace mp2t
354 } // namespace media 553 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698