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 "media/formats/mp2t/mp2t_stream_parser.h" | 5 #include "media/formats/mp2t/mp2t_stream_parser.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 #include <stdint.h> | 8 #include <stdint.h> |
9 | 9 |
10 #include <algorithm> | 10 #include <algorithm> |
11 #include <memory> | 11 #include <memory> |
12 #include <string> | 12 #include <string> |
13 | 13 |
14 #include "base/bind.h" | 14 #include "base/bind.h" |
15 #include "base/bind_helpers.h" | 15 #include "base/bind_helpers.h" |
16 #include "base/logging.h" | 16 #include "base/logging.h" |
17 #include "base/memory/ref_counted.h" | 17 #include "base/memory/ref_counted.h" |
| 18 #include "base/strings/string_number_conversions.h" |
18 #include "base/time/time.h" | 19 #include "base/time/time.h" |
19 #include "media/base/audio_decoder_config.h" | 20 #include "media/base/audio_decoder_config.h" |
20 #include "media/base/decoder_buffer.h" | 21 #include "media/base/decoder_buffer.h" |
21 #include "media/base/media_track.h" | 22 #include "media/base/media_track.h" |
22 #include "media/base/media_tracks.h" | 23 #include "media/base/media_tracks.h" |
23 #include "media/base/stream_parser_buffer.h" | 24 #include "media/base/stream_parser_buffer.h" |
24 #include "media/base/test_data_util.h" | 25 #include "media/base/test_data_util.h" |
25 #include "media/base/text_track_config.h" | 26 #include "media/base/text_track_config.h" |
26 #include "media/base/video_decoder_config.h" | 27 #include "media/base/video_decoder_config.h" |
| 28 #include "media/media_features.h" |
27 #include "testing/gtest/include/gtest/gtest.h" | 29 #include "testing/gtest/include/gtest/gtest.h" |
28 | 30 |
| 31 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES) |
| 32 #include <openssl/aes.h> |
| 33 #include <openssl/evp.h> |
| 34 #include "crypto/openssl_util.h" |
| 35 #endif |
| 36 |
29 namespace media { | 37 namespace media { |
30 namespace mp2t { | 38 namespace mp2t { |
31 | 39 |
32 namespace { | 40 namespace { |
33 | 41 |
34 bool IsMonotonic(const StreamParser::BufferQueue& buffers) { | 42 bool IsMonotonic(const StreamParser::BufferQueue& buffers) { |
35 if (buffers.empty()) | 43 if (buffers.empty()) |
36 return true; | 44 return true; |
37 | 45 |
38 StreamParser::BufferQueue::const_iterator it1 = buffers.begin(); | 46 StreamParser::BufferQueue::const_iterator it1 = buffers.begin(); |
39 StreamParser::BufferQueue::const_iterator it2 = ++it1; | 47 StreamParser::BufferQueue::const_iterator it2 = ++it1; |
40 for ( ; it2 != buffers.end(); ++it1, ++it2) { | 48 for ( ; it2 != buffers.end(); ++it1, ++it2) { |
41 if ((*it2)->GetDecodeTimestamp() < (*it1)->GetDecodeTimestamp()) | 49 if ((*it2)->GetDecodeTimestamp() < (*it1)->GetDecodeTimestamp()) |
42 return false; | 50 return false; |
43 } | 51 } |
44 return true; | 52 return true; |
45 } | 53 } |
46 | 54 |
47 bool IsAlmostEqual(DecodeTimestamp t0, DecodeTimestamp t1) { | 55 bool IsAlmostEqual(DecodeTimestamp t0, DecodeTimestamp t1) { |
48 base::TimeDelta kMaxDeviation = base::TimeDelta::FromMilliseconds(5); | 56 base::TimeDelta kMaxDeviation = base::TimeDelta::FromMilliseconds(5); |
49 base::TimeDelta diff = t1 - t0; | 57 base::TimeDelta diff = t1 - t0; |
50 return (diff >= -kMaxDeviation && diff <= kMaxDeviation); | 58 return (diff >= -kMaxDeviation && diff <= kMaxDeviation); |
51 } | 59 } |
52 | 60 |
| 61 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES) |
| 62 class ScopedCipherCTX { |
| 63 public: |
| 64 explicit ScopedCipherCTX() { EVP_CIPHER_CTX_init(&ctx_); } |
| 65 ~ScopedCipherCTX() { |
| 66 EVP_CIPHER_CTX_cleanup(&ctx_); |
| 67 crypto::ClearOpenSSLERRStack(FROM_HERE); |
| 68 } |
| 69 EVP_CIPHER_CTX* get() { return &ctx_; } |
| 70 |
| 71 private: |
| 72 EVP_CIPHER_CTX ctx_; |
| 73 }; |
| 74 |
| 75 std::string DecryptSampleAES(const std::string& key, |
| 76 const std::string& iv, |
| 77 const uint8_t* input, |
| 78 int input_size, |
| 79 bool has_pattern) { |
| 80 DCHECK(input); |
| 81 EXPECT_EQ(input_size % 16, 0); |
| 82 crypto::EnsureOpenSSLInit(); |
| 83 std::string result; |
| 84 const EVP_CIPHER* cipher = EVP_aes_128_cbc(); |
| 85 ScopedCipherCTX ctx; |
| 86 EXPECT_EQ(EVP_CipherInit_ex(ctx.get(), cipher, NULL, |
| 87 reinterpret_cast<const uint8_t*>(key.data()), |
| 88 reinterpret_cast<const uint8_t*>(iv.data()), 0), |
| 89 1); |
| 90 EVP_CIPHER_CTX_set_padding(ctx.get(), 0); |
| 91 const size_t output_size = input_size; |
| 92 std::unique_ptr<char[]> output(new char[output_size]); |
| 93 uint8_t* in_ptr = const_cast<uint8_t*>(input); |
| 94 uint8_t* out_ptr = reinterpret_cast<uint8_t*>(output.get()); |
| 95 size_t bytes_remaining = output_size; |
| 96 |
| 97 while (bytes_remaining) { |
| 98 int unused; |
| 99 size_t amount_to_decrypt = has_pattern ? 16UL : bytes_remaining; |
| 100 EXPECT_EQ(EVP_CipherUpdate(ctx.get(), out_ptr, &unused, in_ptr, |
| 101 amount_to_decrypt), |
| 102 1); |
| 103 bytes_remaining -= amount_to_decrypt; |
| 104 if (bytes_remaining) { |
| 105 out_ptr += amount_to_decrypt; |
| 106 in_ptr += amount_to_decrypt; |
| 107 size_t amount_to_skip = 144UL; |
| 108 if (amount_to_skip > bytes_remaining) |
| 109 amount_to_skip = bytes_remaining; |
| 110 memcpy(out_ptr, in_ptr, amount_to_skip); |
| 111 out_ptr += amount_to_skip; |
| 112 in_ptr += amount_to_skip; |
| 113 bytes_remaining -= amount_to_skip; |
| 114 } |
| 115 } |
| 116 |
| 117 result.assign(output.get(), output_size); |
| 118 return result; |
| 119 } |
| 120 |
| 121 // We only support AES-CBC at this time. |
| 122 // For the purpose of these tests, the key id is also used as the actual key. |
| 123 std::string DecryptBuffer(const StreamParserBuffer& buffer, |
| 124 const EncryptionScheme& scheme) { |
| 125 EXPECT_TRUE(scheme.is_encrypted()); |
| 126 EXPECT_TRUE(scheme.mode() == EncryptionScheme::CIPHER_MODE_AES_CBC); |
| 127 bool has_pattern = scheme.pattern().IsInEffect(); |
| 128 EXPECT_TRUE(!has_pattern || |
| 129 scheme.pattern().Matches(EncryptionScheme::Pattern(1, 9))); |
| 130 |
| 131 std::string key; |
| 132 EXPECT_TRUE( |
| 133 LookupTestKeyString(buffer.decrypt_config()->key_id(), false, &key)); |
| 134 std::string iv = buffer.decrypt_config()->iv(); |
| 135 EXPECT_EQ(key.size(), 16UL); |
| 136 EXPECT_EQ(iv.size(), 16UL); |
| 137 std::string result; |
| 138 uint8_t* in_ptr = const_cast<uint8_t*>(buffer.data()); |
| 139 const DecryptConfig* decrypt_config = buffer.decrypt_config(); |
| 140 for (const auto& subsample : decrypt_config->subsamples()) { |
| 141 std::string clear(reinterpret_cast<char*>(in_ptr), subsample.clear_bytes); |
| 142 result += clear; |
| 143 in_ptr += subsample.clear_bytes; |
| 144 result += |
| 145 DecryptSampleAES(key, iv, in_ptr, subsample.cypher_bytes, has_pattern); |
| 146 in_ptr += subsample.cypher_bytes; |
| 147 } |
| 148 return result; |
| 149 } |
| 150 #endif |
| 151 |
53 } // namespace | 152 } // namespace |
54 | 153 |
55 class Mp2tStreamParserTest : public testing::Test { | 154 class Mp2tStreamParserTest : public testing::Test { |
56 public: | 155 public: |
57 Mp2tStreamParserTest() | 156 Mp2tStreamParserTest() |
58 : segment_count_(0), | 157 : segment_count_(0), |
59 config_count_(0), | 158 config_count_(0), |
60 audio_frame_count_(0), | 159 audio_frame_count_(0), |
61 video_frame_count_(0), | 160 video_frame_count_(0), |
62 has_video_(true), | 161 has_video_(true), |
63 audio_min_dts_(kNoDecodeTimestamp()), | 162 audio_min_dts_(kNoDecodeTimestamp()), |
64 audio_max_dts_(kNoDecodeTimestamp()), | 163 audio_max_dts_(kNoDecodeTimestamp()), |
65 video_min_dts_(kNoDecodeTimestamp()), | 164 video_min_dts_(kNoDecodeTimestamp()), |
66 video_max_dts_(kNoDecodeTimestamp()) { | 165 video_max_dts_(kNoDecodeTimestamp()), |
| 166 current_audio_config_(), |
| 167 current_video_config_(), |
| 168 capture_buffers(false) { |
67 bool has_sbr = false; | 169 bool has_sbr = false; |
68 parser_.reset(new Mp2tStreamParser(has_sbr)); | 170 parser_.reset(new Mp2tStreamParser(has_sbr)); |
69 } | 171 } |
70 | 172 |
71 protected: | 173 protected: |
72 std::unique_ptr<Mp2tStreamParser> parser_; | 174 std::unique_ptr<Mp2tStreamParser> parser_; |
73 int segment_count_; | 175 int segment_count_; |
74 int config_count_; | 176 int config_count_; |
75 int audio_frame_count_; | 177 int audio_frame_count_; |
76 int video_frame_count_; | 178 int video_frame_count_; |
77 bool has_video_; | 179 bool has_video_; |
78 DecodeTimestamp audio_min_dts_; | 180 DecodeTimestamp audio_min_dts_; |
79 DecodeTimestamp audio_max_dts_; | 181 DecodeTimestamp audio_max_dts_; |
80 DecodeTimestamp video_min_dts_; | 182 DecodeTimestamp video_min_dts_; |
81 DecodeTimestamp video_max_dts_; | 183 DecodeTimestamp video_max_dts_; |
82 | 184 |
| 185 AudioDecoderConfig current_audio_config_; |
| 186 VideoDecoderConfig current_video_config_; |
| 187 std::vector<scoped_refptr<StreamParserBuffer>> audio_buffer_capture_; |
| 188 std::vector<scoped_refptr<StreamParserBuffer>> video_buffer_capture_; |
| 189 bool capture_buffers; |
| 190 |
83 void ResetStats() { | 191 void ResetStats() { |
84 segment_count_ = 0; | 192 segment_count_ = 0; |
85 config_count_ = 0; | 193 config_count_ = 0; |
86 audio_frame_count_ = 0; | 194 audio_frame_count_ = 0; |
87 video_frame_count_ = 0; | 195 video_frame_count_ = 0; |
88 audio_min_dts_ = kNoDecodeTimestamp(); | 196 audio_min_dts_ = kNoDecodeTimestamp(); |
89 audio_max_dts_ = kNoDecodeTimestamp(); | 197 audio_max_dts_ = kNoDecodeTimestamp(); |
90 video_min_dts_ = kNoDecodeTimestamp(); | 198 video_min_dts_ = kNoDecodeTimestamp(); |
91 video_max_dts_ = kNoDecodeTimestamp(); | 199 video_max_dts_ = kNoDecodeTimestamp(); |
92 } | 200 } |
(...skipping 24 matching lines...) Expand all Loading... |
117 | 225 |
118 bool OnNewConfig(std::unique_ptr<MediaTracks> tracks, | 226 bool OnNewConfig(std::unique_ptr<MediaTracks> tracks, |
119 const StreamParser::TextTrackConfigMap& tc) { | 227 const StreamParser::TextTrackConfigMap& tc) { |
120 const AudioDecoderConfig& ac = tracks->getFirstAudioConfig(); | 228 const AudioDecoderConfig& ac = tracks->getFirstAudioConfig(); |
121 const VideoDecoderConfig& vc = tracks->getFirstVideoConfig(); | 229 const VideoDecoderConfig& vc = tracks->getFirstVideoConfig(); |
122 DVLOG(1) << "OnNewConfig: media tracks count=" << tracks->tracks().size() | 230 DVLOG(1) << "OnNewConfig: media tracks count=" << tracks->tracks().size() |
123 << ", audio=" << ac.IsValidConfig() | 231 << ", audio=" << ac.IsValidConfig() |
124 << ", video=" << vc.IsValidConfig(); | 232 << ", video=" << vc.IsValidConfig(); |
125 config_count_++; | 233 config_count_++; |
126 EXPECT_TRUE(ac.IsValidConfig()); | 234 EXPECT_TRUE(ac.IsValidConfig()); |
| 235 current_audio_config_ = ac; |
127 EXPECT_EQ(vc.IsValidConfig(), has_video_); | 236 EXPECT_EQ(vc.IsValidConfig(), has_video_); |
| 237 current_video_config_ = vc; |
128 return true; | 238 return true; |
129 } | 239 } |
130 | 240 |
| 241 void CaptureVideoBuffers(const StreamParser::BufferQueue& video_buffers) { |
| 242 for (const auto& buffer : video_buffers) { |
| 243 video_buffer_capture_.push_back(buffer); |
| 244 } |
| 245 } |
| 246 |
| 247 void CaptureAudioBuffers(const StreamParser::BufferQueue& audio_buffers) { |
| 248 for (const auto& buffer : audio_buffers) { |
| 249 audio_buffer_capture_.push_back(buffer); |
| 250 } |
| 251 } |
131 | 252 |
132 void DumpBuffers(const std::string& label, | 253 void DumpBuffers(const std::string& label, |
133 const StreamParser::BufferQueue& buffers) { | 254 const StreamParser::BufferQueue& buffers) { |
134 DVLOG(2) << "DumpBuffers: " << label << " size " << buffers.size(); | 255 DVLOG(2) << "DumpBuffers: " << label << " size " << buffers.size(); |
135 for (StreamParser::BufferQueue::const_iterator buf = buffers.begin(); | 256 for (StreamParser::BufferQueue::const_iterator buf = buffers.begin(); |
136 buf != buffers.end(); buf++) { | 257 buf != buffers.end(); buf++) { |
137 DVLOG(3) << " n=" << buf - buffers.begin() | 258 DVLOG(3) << " n=" << buf - buffers.begin() |
138 << ", size=" << (*buf)->data_size() | 259 << ", size=" << (*buf)->data_size() |
139 << ", dur=" << (*buf)->duration().InMilliseconds(); | 260 << ", dur=" << (*buf)->duration().InMilliseconds(); |
140 } | 261 } |
141 } | 262 } |
142 | 263 |
143 bool OnNewBuffers(const StreamParser::BufferQueue& audio_buffers, | 264 bool OnNewBuffers(const StreamParser::BufferQueue& audio_buffers, |
144 const StreamParser::BufferQueue& video_buffers, | 265 const StreamParser::BufferQueue& video_buffers, |
145 const StreamParser::TextBufferQueueMap& text_map) { | 266 const StreamParser::TextBufferQueueMap& text_map) { |
146 EXPECT_GT(config_count_, 0); | 267 EXPECT_GT(config_count_, 0); |
147 DumpBuffers("audio_buffers", audio_buffers); | 268 DumpBuffers("audio_buffers", audio_buffers); |
148 DumpBuffers("video_buffers", video_buffers); | 269 DumpBuffers("video_buffers", video_buffers); |
| 270 if (capture_buffers) { |
| 271 CaptureVideoBuffers(video_buffers); |
| 272 CaptureAudioBuffers(audio_buffers); |
| 273 } |
149 | 274 |
150 // TODO(wolenetz/acolwell): Add text track support to more MSE parsers. See | 275 // TODO(wolenetz/acolwell): Add text track support to more MSE parsers. See |
151 // http://crbug.com/336926. | 276 // http://crbug.com/336926. |
152 if (!text_map.empty()) | 277 if (!text_map.empty()) |
153 return false; | 278 return false; |
154 | 279 |
155 // Verify monotonicity. | 280 // Verify monotonicity. |
156 if (!IsMonotonic(video_buffers)) | 281 if (!IsMonotonic(video_buffers)) |
157 return false; | 282 return false; |
158 if (!IsMonotonic(audio_buffers)) | 283 if (!IsMonotonic(audio_buffers)) |
(...skipping 18 matching lines...) Expand all Loading... |
177 audio_max_dts_ = last_dts; | 302 audio_max_dts_ = last_dts; |
178 } | 303 } |
179 | 304 |
180 audio_frame_count_ += audio_buffers.size(); | 305 audio_frame_count_ += audio_buffers.size(); |
181 video_frame_count_ += video_buffers.size(); | 306 video_frame_count_ += video_buffers.size(); |
182 return true; | 307 return true; |
183 } | 308 } |
184 | 309 |
185 void OnKeyNeeded(EmeInitDataType type, | 310 void OnKeyNeeded(EmeInitDataType type, |
186 const std::vector<uint8_t>& init_data) { | 311 const std::vector<uint8_t>& init_data) { |
| 312 #if !BUILDFLAG(ENABLE_HLS_SAMPLE_AES) |
187 NOTREACHED() << "OnKeyNeeded not expected in the Mpeg2 TS parser"; | 313 NOTREACHED() << "OnKeyNeeded not expected in the Mpeg2 TS parser"; |
| 314 #endif |
188 } | 315 } |
189 | 316 |
190 void OnNewSegment() { | 317 void OnNewSegment() { |
191 DVLOG(1) << "OnNewSegment"; | 318 DVLOG(1) << "OnNewSegment"; |
192 segment_count_++; | 319 segment_count_++; |
193 } | 320 } |
194 | 321 |
195 void OnEndOfSegment() { | 322 void OnEndOfSegment() { |
196 NOTREACHED() << "OnEndOfSegment not expected in the Mpeg2 TS parser"; | 323 NOTREACHED() << "OnEndOfSegment not expected in the Mpeg2 TS parser"; |
197 } | 324 } |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
294 has_video_ = false; | 421 has_video_ = false; |
295 ParseMpeg2TsFile("bear_adts_in_private_stream_1.ts", 512); | 422 ParseMpeg2TsFile("bear_adts_in_private_stream_1.ts", 512); |
296 parser_->Flush(); | 423 parser_->Flush(); |
297 EXPECT_EQ(audio_frame_count_, 40); | 424 EXPECT_EQ(audio_frame_count_, 40); |
298 EXPECT_EQ(video_frame_count_, 0); | 425 EXPECT_EQ(video_frame_count_, 0); |
299 // This stream has no mid-stream configuration change. | 426 // This stream has no mid-stream configuration change. |
300 EXPECT_EQ(config_count_, 1); | 427 EXPECT_EQ(config_count_, 1); |
301 EXPECT_EQ(segment_count_, 1); | 428 EXPECT_EQ(segment_count_, 1); |
302 } | 429 } |
303 | 430 |
| 431 #if BUILDFLAG(ENABLE_HLS_SAMPLE_AES) |
| 432 TEST_F(Mp2tStreamParserTest, HLSSampleAES) { |
| 433 std::vector<std::string> decrypted_video_buffers; |
| 434 std::vector<std::string> decrypted_audio_buffers; |
| 435 InitializeParser(); |
| 436 capture_buffers = true; |
| 437 ParseMpeg2TsFile("bear-1280x720-hls-sample-aes.ts", 2048); |
| 438 parser_->Flush(); |
| 439 EncryptionScheme video_encryption_scheme = |
| 440 current_video_config_.encryption_scheme(); |
| 441 EXPECT_TRUE(video_encryption_scheme.is_encrypted()); |
| 442 for (const auto& buffer : video_buffer_capture_) { |
| 443 std::string decrypted_video_buffer = |
| 444 DecryptBuffer(*buffer.get(), video_encryption_scheme); |
| 445 decrypted_video_buffers.push_back(decrypted_video_buffer); |
| 446 } |
| 447 EncryptionScheme audio_encryption_scheme = |
| 448 current_audio_config_.encryption_scheme(); |
| 449 EXPECT_TRUE(audio_encryption_scheme.is_encrypted()); |
| 450 for (const auto& buffer : audio_buffer_capture_) { |
| 451 std::string decrypted_audio_buffer = |
| 452 DecryptBuffer(*buffer.get(), audio_encryption_scheme); |
| 453 decrypted_audio_buffers.push_back(decrypted_audio_buffer); |
| 454 } |
| 455 |
| 456 parser_.reset(new Mp2tStreamParser(false)); |
| 457 ResetStats(); |
| 458 InitializeParser(); |
| 459 video_buffer_capture_.clear(); |
| 460 audio_buffer_capture_.clear(); |
| 461 ParseMpeg2TsFile("bear-1280x720-hls.ts", 2048); |
| 462 parser_->Flush(); |
| 463 video_encryption_scheme = current_video_config_.encryption_scheme(); |
| 464 EXPECT_FALSE(video_encryption_scheme.is_encrypted()); |
| 465 // Skip the last buffer, which may be truncated. |
| 466 for (size_t i = 0; i + 1 < video_buffer_capture_.size(); i++) { |
| 467 const auto& buffer = video_buffer_capture_[i]; |
| 468 std::string unencrypted_video_buffer( |
| 469 reinterpret_cast<const char*>(buffer->data()), buffer->data_size()); |
| 470 EXPECT_EQ(decrypted_video_buffers[i], unencrypted_video_buffer); |
| 471 } |
| 472 audio_encryption_scheme = current_audio_config_.encryption_scheme(); |
| 473 EXPECT_FALSE(audio_encryption_scheme.is_encrypted()); |
| 474 for (size_t i = 0; i + 1 < audio_buffer_capture_.size(); i++) { |
| 475 const auto& buffer = audio_buffer_capture_[i]; |
| 476 std::string unencrypted_audio_buffer( |
| 477 reinterpret_cast<const char*>(buffer->data()), buffer->data_size()); |
| 478 EXPECT_EQ(decrypted_audio_buffers[i], unencrypted_audio_buffer); |
| 479 } |
| 480 } |
| 481 #endif |
| 482 |
304 } // namespace mp2t | 483 } // namespace mp2t |
305 } // namespace media | 484 } // namespace media |
OLD | NEW |