| 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 <algorithm> | 5 #include <algorithm> |
| 6 #include <string> | 6 #include <string> |
| 7 | 7 |
| 8 #ifdef ENABLE_HLS_SAMPLE_AES |
| 9 #include <openssl/aes.h> |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 |
| OLD | NEW |