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

Side by Side Diff: media/formats/mp2t/mp2t_stream_parser_unittest.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: tidying up prior to review Created 5 years 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 <algorithm> 5 #include <algorithm>
6 #include <string> 6 #include <string>
7 7
8 #ifdef ENABLE_HLS_SAMPLE_AES
9 #include <openssl/aes.h>
ddorwin 2015/12/10 20:10:59 The Clear Key implementation supports AES via cryp
dougsteed 2015/12/14 22:51:46 See comment on my change to BUILD.gn.
10 #include <openssl/evp.h>
11 #endif
12
8 #include "base/bind.h" 13 #include "base/bind.h"
9 #include "base/bind_helpers.h" 14 #include "base/bind_helpers.h"
10 #include "base/logging.h" 15 #include "base/logging.h"
11 #include "base/memory/ref_counted.h" 16 #include "base/memory/ref_counted.h"
17 #include "base/strings/string_number_conversions.h"
12 #include "base/time/time.h" 18 #include "base/time/time.h"
19 #ifdef ENABLE_HLS_SAMPLE_AES
20 #include "crypto/openssl_util.h"
21 #endif
13 #include "media/base/audio_decoder_config.h" 22 #include "media/base/audio_decoder_config.h"
14 #include "media/base/decoder_buffer.h" 23 #include "media/base/decoder_buffer.h"
15 #include "media/base/stream_parser_buffer.h" 24 #include "media/base/stream_parser_buffer.h"
16 #include "media/base/test_data_util.h" 25 #include "media/base/test_data_util.h"
17 #include "media/base/text_track_config.h" 26 #include "media/base/text_track_config.h"
18 #include "media/base/video_decoder_config.h" 27 #include "media/base/video_decoder_config.h"
19 #include "media/formats/mp2t/mp2t_stream_parser.h" 28 #include "media/formats/mp2t/mp2t_stream_parser.h"
20 #include "testing/gtest/include/gtest/gtest.h" 29 #include "testing/gtest/include/gtest/gtest.h"
21 30
22 namespace media { 31 namespace media {
(...skipping 13 matching lines...) Expand all
36 } 45 }
37 return true; 46 return true;
38 } 47 }
39 48
40 bool IsAlmostEqual(DecodeTimestamp t0, DecodeTimestamp t1) { 49 bool IsAlmostEqual(DecodeTimestamp t0, DecodeTimestamp t1) {
41 base::TimeDelta kMaxDeviation = base::TimeDelta::FromMilliseconds(5); 50 base::TimeDelta kMaxDeviation = base::TimeDelta::FromMilliseconds(5);
42 base::TimeDelta diff = t1 - t0; 51 base::TimeDelta diff = t1 - t0;
43 return (diff >= -kMaxDeviation && diff <= kMaxDeviation); 52 return (diff >= -kMaxDeviation && diff <= kMaxDeviation);
44 } 53 }
45 54
55 #ifdef ENABLE_HLS_SAMPLE_AES
56 class ScopedCipherCTX {
57 public:
58 explicit ScopedCipherCTX() { EVP_CIPHER_CTX_init(&ctx_); }
59 ~ScopedCipherCTX() {
60 EVP_CIPHER_CTX_cleanup(&ctx_);
61 crypto::ClearOpenSSLERRStack(FROM_HERE);
62 }
63 EVP_CIPHER_CTX* get() { return &ctx_; }
64
65 private:
66 EVP_CIPHER_CTX ctx_;
67 };
68
69 std::string DecryptSampleAES(const std::string& key,
70 const std::string& iv,
71 const uint8* input,
72 int input_size,
73 bool has_pattern) {
74 EXPECT_EQ(input_size % 16, 0);
75 crypto::EnsureOpenSSLInit();
76 std::string result;
77 const EVP_CIPHER* cipher = EVP_aes_128_cbc();
78 ScopedCipherCTX ctx;
79 EXPECT_EQ(EVP_CipherInit_ex(ctx.get(), cipher, NULL,
80 reinterpret_cast<const uint8*>(key.data()),
81 reinterpret_cast<const uint8*>(iv.data()), 0),
82 1);
83 EVP_CIPHER_CTX_set_padding(ctx.get(), 0);
84 const size_t output_size = input_size;
85 scoped_ptr<char[]> output(new char[output_size]);
86 uint8* in_ptr = const_cast<uint8*>(input);
87 uint8* out_ptr = reinterpret_cast<uint8*>(output.get());
88 size_t bytes_remaining = output_size;
89
90 while (bytes_remaining) {
91 int unused;
92 size_t amount_to_decrypt = has_pattern ? 16UL : bytes_remaining;
93 EXPECT_EQ(EVP_CipherUpdate(ctx.get(), out_ptr, &unused, in_ptr,
94 amount_to_decrypt),
95 1);
96 bytes_remaining -= amount_to_decrypt;
97 if (bytes_remaining) {
98 out_ptr += amount_to_decrypt;
99 in_ptr += amount_to_decrypt;
100 size_t amount_to_skip = 144UL;
101 if (amount_to_skip > bytes_remaining)
102 amount_to_skip = bytes_remaining;
103 memcpy(out_ptr, in_ptr, amount_to_skip);
104 out_ptr += amount_to_skip;
105 in_ptr += amount_to_skip;
106 bytes_remaining -= amount_to_skip;
107 }
108 }
109
110 result.assign(output.get(), output_size);
111 return result;
112 }
113
114 // We only support AES-CBC at this time.
115 // For the purpose of these tests, the key id is also used as the actual key.
116 std::string DecryptBuffer(const StreamParserBuffer& buffer,
117 const EncryptionScheme& scheme) {
118 EXPECT_TRUE(scheme.is_encrypted());
119 EXPECT_TRUE(scheme.mode() == kCipherModeAesCbc);
120 bool has_pattern = scheme.pattern().in_effect();
121 EXPECT_TRUE(!has_pattern ||
122 scheme.pattern().Matches(EncryptionScheme::PatternSpec(1, 9)));
123 std::string key = buffer.decrypt_config()->key_id();
124 std::string iv = buffer.decrypt_config()->iv();
125 EXPECT_EQ(key.size(), 16UL);
126 EXPECT_EQ(iv.size(), 16UL);
127 std::string result;
128 uint8* in_ptr = const_cast<uint8*>(buffer.data());
129 const DecryptConfig* decrypt_config = buffer.decrypt_config();
130 for (const auto& subsample : decrypt_config->subsamples()) {
131 std::string clear(reinterpret_cast<char*>(in_ptr), subsample.clear_bytes);
132 result += clear;
133 in_ptr += subsample.clear_bytes;
134 result +=
135 DecryptSampleAES(key, iv, in_ptr, subsample.cypher_bytes, has_pattern);
136 in_ptr += subsample.cypher_bytes;
137 }
138 return result;
139 }
140 #endif
141
46 } // namespace 142 } // namespace
47 143
48 class Mp2tStreamParserTest : public testing::Test { 144 class Mp2tStreamParserTest : public testing::Test {
49 public: 145 public:
50 Mp2tStreamParserTest() 146 Mp2tStreamParserTest()
51 : segment_count_(0), 147 : segment_count_(0),
52 config_count_(0), 148 config_count_(0),
53 audio_frame_count_(0), 149 audio_frame_count_(0),
54 video_frame_count_(0), 150 video_frame_count_(0),
55 has_video_(true), 151 has_video_(true),
56 audio_min_dts_(kNoDecodeTimestamp()), 152 audio_min_dts_(kNoDecodeTimestamp()),
57 audio_max_dts_(kNoDecodeTimestamp()), 153 audio_max_dts_(kNoDecodeTimestamp()),
58 video_min_dts_(kNoDecodeTimestamp()), 154 video_min_dts_(kNoDecodeTimestamp()),
59 video_max_dts_(kNoDecodeTimestamp()) { 155 video_max_dts_(kNoDecodeTimestamp()),
156 current_audio_config_(),
157 current_video_config_(),
158 audio_buffer_capture_index_(-1),
159 video_buffer_capture_index_(-1) {
60 bool has_sbr = false; 160 bool has_sbr = false;
61 parser_.reset(new Mp2tStreamParser(has_sbr)); 161 parser_.reset(new Mp2tStreamParser(has_sbr));
62 } 162 }
63 163
64 protected: 164 protected:
65 scoped_ptr<Mp2tStreamParser> parser_; 165 scoped_ptr<Mp2tStreamParser> parser_;
66 int segment_count_; 166 int segment_count_;
67 int config_count_; 167 int config_count_;
68 int audio_frame_count_; 168 int audio_frame_count_;
69 int video_frame_count_; 169 int video_frame_count_;
70 bool has_video_; 170 bool has_video_;
71 DecodeTimestamp audio_min_dts_; 171 DecodeTimestamp audio_min_dts_;
72 DecodeTimestamp audio_max_dts_; 172 DecodeTimestamp audio_max_dts_;
73 DecodeTimestamp video_min_dts_; 173 DecodeTimestamp video_min_dts_;
74 DecodeTimestamp video_max_dts_; 174 DecodeTimestamp video_max_dts_;
75 175
176 AudioDecoderConfig current_audio_config_;
177 VideoDecoderConfig current_video_config_;
178 int audio_buffer_capture_index_;
179 int video_buffer_capture_index_;
180 scoped_refptr<StreamParserBuffer> audio_buffer_capture_;
181 scoped_refptr<StreamParserBuffer> video_buffer_capture_;
182
76 void ResetStats() { 183 void ResetStats() {
77 segment_count_ = 0; 184 segment_count_ = 0;
78 config_count_ = 0; 185 config_count_ = 0;
79 audio_frame_count_ = 0; 186 audio_frame_count_ = 0;
80 video_frame_count_ = 0; 187 video_frame_count_ = 0;
81 audio_min_dts_ = kNoDecodeTimestamp(); 188 audio_min_dts_ = kNoDecodeTimestamp();
82 audio_max_dts_ = kNoDecodeTimestamp(); 189 audio_max_dts_ = kNoDecodeTimestamp();
83 video_min_dts_ = kNoDecodeTimestamp(); 190 video_min_dts_ = kNoDecodeTimestamp();
84 video_max_dts_ = kNoDecodeTimestamp(); 191 video_max_dts_ = kNoDecodeTimestamp();
85 } 192 }
(...skipping 20 matching lines...) Expand all
106 << ", autoTimestampOffset=" << params.auto_update_timestamp_offset; 213 << ", autoTimestampOffset=" << params.auto_update_timestamp_offset;
107 } 214 }
108 215
109 bool OnNewConfig(const AudioDecoderConfig& ac, 216 bool OnNewConfig(const AudioDecoderConfig& ac,
110 const VideoDecoderConfig& vc, 217 const VideoDecoderConfig& vc,
111 const StreamParser::TextTrackConfigMap& tc) { 218 const StreamParser::TextTrackConfigMap& tc) {
112 DVLOG(1) << "OnNewConfig: audio=" << ac.IsValidConfig() 219 DVLOG(1) << "OnNewConfig: audio=" << ac.IsValidConfig()
113 << ", video=" << vc.IsValidConfig(); 220 << ", video=" << vc.IsValidConfig();
114 config_count_++; 221 config_count_++;
115 EXPECT_TRUE(ac.IsValidConfig()); 222 EXPECT_TRUE(ac.IsValidConfig());
223 current_audio_config_ = ac;
116 EXPECT_EQ(vc.IsValidConfig(), has_video_); 224 EXPECT_EQ(vc.IsValidConfig(), has_video_);
225 current_video_config_ = vc;
117 return true; 226 return true;
118 } 227 }
119 228
229 void CaptureVideoBuffer(const StreamParser::BufferQueue& video_buffers) {
230 for (const auto& buffer : video_buffers) {
231 if (video_buffer_capture_index_ < 0)
232 return;
233 else if (video_buffer_capture_index_ == 0)
234 video_buffer_capture_ = buffer;
235 video_buffer_capture_index_--;
236 }
237 }
238
239 void CaptureAudioBuffer(const StreamParser::BufferQueue& audio_buffers) {
240 for (const auto& buffer : audio_buffers) {
241 if (audio_buffer_capture_index_ < 0)
242 return;
243 else if (audio_buffer_capture_index_ == 0)
244 audio_buffer_capture_ = buffer;
245 audio_buffer_capture_index_--;
246 }
247 }
120 248
121 void DumpBuffers(const std::string& label, 249 void DumpBuffers(const std::string& label,
122 const StreamParser::BufferQueue& buffers) { 250 const StreamParser::BufferQueue& buffers) {
123 DVLOG(2) << "DumpBuffers: " << label << " size " << buffers.size(); 251 DVLOG(2) << "DumpBuffers: " << label << " size " << buffers.size();
124 for (StreamParser::BufferQueue::const_iterator buf = buffers.begin(); 252 for (StreamParser::BufferQueue::const_iterator buf = buffers.begin();
125 buf != buffers.end(); buf++) { 253 buf != buffers.end(); buf++) {
126 DVLOG(3) << " n=" << buf - buffers.begin() 254 DVLOG(3) << " n=" << buf - buffers.begin()
127 << ", size=" << (*buf)->data_size() 255 << ", size=" << (*buf)->data_size()
128 << ", dur=" << (*buf)->duration().InMilliseconds(); 256 << ", dur=" << (*buf)->duration().InMilliseconds();
129 } 257 }
130 } 258 }
131 259
132 bool OnNewBuffers(const StreamParser::BufferQueue& audio_buffers, 260 bool OnNewBuffers(const StreamParser::BufferQueue& audio_buffers,
133 const StreamParser::BufferQueue& video_buffers, 261 const StreamParser::BufferQueue& video_buffers,
134 const StreamParser::TextBufferQueueMap& text_map) { 262 const StreamParser::TextBufferQueueMap& text_map) {
135 EXPECT_GT(config_count_, 0); 263 EXPECT_GT(config_count_, 0);
136 DumpBuffers("audio_buffers", audio_buffers); 264 DumpBuffers("audio_buffers", audio_buffers);
137 DumpBuffers("video_buffers", video_buffers); 265 DumpBuffers("video_buffers", video_buffers);
266 CaptureVideoBuffer(video_buffers);
267 CaptureAudioBuffer(audio_buffers);
138 268
139 // TODO(wolenetz/acolwell): Add text track support to more MSE parsers. See 269 // TODO(wolenetz/acolwell): Add text track support to more MSE parsers. See
140 // http://crbug.com/336926. 270 // http://crbug.com/336926.
141 if (!text_map.empty()) 271 if (!text_map.empty())
142 return false; 272 return false;
143 273
144 // Verify monotonicity. 274 // Verify monotonicity.
145 if (!IsMonotonic(video_buffers)) 275 if (!IsMonotonic(video_buffers))
146 return false; 276 return false;
147 if (!IsMonotonic(audio_buffers)) 277 if (!IsMonotonic(audio_buffers))
(...skipping 17 matching lines...) Expand all
165 audio_min_dts_ = first_dts; 295 audio_min_dts_ = first_dts;
166 audio_max_dts_ = last_dts; 296 audio_max_dts_ = last_dts;
167 } 297 }
168 298
169 audio_frame_count_ += audio_buffers.size(); 299 audio_frame_count_ += audio_buffers.size();
170 video_frame_count_ += video_buffers.size(); 300 video_frame_count_ += video_buffers.size();
171 return true; 301 return true;
172 } 302 }
173 303
174 void OnKeyNeeded(EmeInitDataType type, const std::vector<uint8>& init_data) { 304 void OnKeyNeeded(EmeInitDataType type, const std::vector<uint8>& init_data) {
175 NOTREACHED() << "OnKeyNeeded not expected in the Mpeg2 TS parser";
176 } 305 }
177 306
178 void OnNewSegment() { 307 void OnNewSegment() {
179 DVLOG(1) << "OnNewSegment"; 308 DVLOG(1) << "OnNewSegment";
180 segment_count_++; 309 segment_count_++;
181 } 310 }
182 311
183 void OnEndOfSegment() { 312 void OnEndOfSegment() {
184 NOTREACHED() << "OnEndOfSegment not expected in the Mpeg2 TS parser"; 313 NOTREACHED() << "OnEndOfSegment not expected in the Mpeg2 TS parser";
185 } 314 }
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
282 has_video_ = false; 411 has_video_ = false;
283 ParseMpeg2TsFile("bear_adts_in_private_stream_1.ts", 512); 412 ParseMpeg2TsFile("bear_adts_in_private_stream_1.ts", 512);
284 parser_->Flush(); 413 parser_->Flush();
285 EXPECT_EQ(audio_frame_count_, 40); 414 EXPECT_EQ(audio_frame_count_, 40);
286 EXPECT_EQ(video_frame_count_, 0); 415 EXPECT_EQ(video_frame_count_, 0);
287 // This stream has no mid-stream configuration change. 416 // This stream has no mid-stream configuration change.
288 EXPECT_EQ(config_count_, 1); 417 EXPECT_EQ(config_count_, 1);
289 EXPECT_EQ(segment_count_, 1); 418 EXPECT_EQ(segment_count_, 1);
290 } 419 }
291 420
421 #ifdef ENABLE_HLS_SAMPLE_AES
422 TEST_F(Mp2tStreamParserTest, HLSSampleAES) {
423 InitializeParser();
424 audio_buffer_capture_index_ = 0;
425 video_buffer_capture_index_ = 0;
426 ParseMpeg2TsFile("main.ts", 2048);
427 parser_->Flush();
428 EncryptionScheme video_encryption_scheme =
429 current_video_config_.encryption_scheme();
430 EXPECT_TRUE(video_encryption_scheme.is_encrypted());
431 std::string decrypted_video_buffer =
432 DecryptBuffer(*video_buffer_capture_.get(), video_encryption_scheme);
433 EncryptionScheme audio_encryption_scheme =
434 current_audio_config_.encryption_scheme();
435 EXPECT_TRUE(audio_encryption_scheme.is_encrypted());
436 std::string decrypted_audio_buffer =
437 DecryptBuffer(*audio_buffer_capture_.get(), audio_encryption_scheme);
438
439 parser_.reset(new Mp2tStreamParser(false));
440 ResetStats();
441 InitializeParser();
442 audio_buffer_capture_index_ = 0;
443 video_buffer_capture_index_ = 0;
444 ParseMpeg2TsFile("unencrypted.ts", 2048);
445 parser_->Flush();
446 video_encryption_scheme = current_video_config_.encryption_scheme();
447 EXPECT_FALSE(video_encryption_scheme.is_encrypted());
448 std::string unencrypted_video_buffer(
449 reinterpret_cast<const char*>(video_buffer_capture_->data()),
450 video_buffer_capture_->data_size());
451 audio_encryption_scheme = current_audio_config_.encryption_scheme();
452 EXPECT_FALSE(audio_encryption_scheme.is_encrypted());
453 std::string unencrypted_audio_buffer(
454 reinterpret_cast<const char*>(audio_buffer_capture_->data()),
455 audio_buffer_capture_->data_size());
456
457 EXPECT_EQ(decrypted_video_buffer, unencrypted_video_buffer);
458 EXPECT_EQ(decrypted_audio_buffer, unencrypted_audio_buffer);
459 }
460 #endif
461
292 } // namespace mp2t 462 } // namespace mp2t
293 } // namespace media 463 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698