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

Unified 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: rebase Created 4 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « media/formats/mp2t/mp2t_stream_parser.cc ('k') | media/formats/mp2t/ts_section_cat.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: media/formats/mp2t/mp2t_stream_parser_unittest.cc
diff --git a/media/formats/mp2t/mp2t_stream_parser_unittest.cc b/media/formats/mp2t/mp2t_stream_parser_unittest.cc
index 0989b74c935669a1dc6b2b51a3d8aeee5ed4f9a7..933a5de626f65c0218725fada0a8c6f7469e7662 100644
--- a/media/formats/mp2t/mp2t_stream_parser_unittest.cc
+++ b/media/formats/mp2t/mp2t_stream_parser_unittest.cc
@@ -15,6 +15,7 @@
#include "base/bind_helpers.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
+#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/decoder_buffer.h"
@@ -25,8 +26,15 @@
#include "media/base/test_data_util.h"
#include "media/base/text_track_config.h"
#include "media/base/video_decoder_config.h"
+#include "media/media_features.h"
#include "testing/gtest/include/gtest/gtest.h"
+#if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
+#include <openssl/aes.h>
+#include <openssl/evp.h>
+#include "crypto/openssl_util.h"
+#endif
+
namespace media {
namespace mp2t {
@@ -51,6 +59,97 @@ bool IsAlmostEqual(DecodeTimestamp t0, DecodeTimestamp t1) {
return (diff >= -kMaxDeviation && diff <= kMaxDeviation);
}
+#if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
+class ScopedCipherCTX {
+ public:
+ explicit ScopedCipherCTX() { EVP_CIPHER_CTX_init(&ctx_); }
+ ~ScopedCipherCTX() {
+ EVP_CIPHER_CTX_cleanup(&ctx_);
+ crypto::ClearOpenSSLERRStack(FROM_HERE);
+ }
+ EVP_CIPHER_CTX* get() { return &ctx_; }
+
+ private:
+ EVP_CIPHER_CTX ctx_;
+};
+
+std::string DecryptSampleAES(const std::string& key,
+ const std::string& iv,
+ const uint8_t* input,
+ int input_size,
+ bool has_pattern) {
+ DCHECK(input);
+ EXPECT_EQ(input_size % 16, 0);
+ crypto::EnsureOpenSSLInit();
+ std::string result;
+ const EVP_CIPHER* cipher = EVP_aes_128_cbc();
+ ScopedCipherCTX ctx;
+ EXPECT_EQ(EVP_CipherInit_ex(ctx.get(), cipher, NULL,
+ reinterpret_cast<const uint8_t*>(key.data()),
+ reinterpret_cast<const uint8_t*>(iv.data()), 0),
+ 1);
+ EVP_CIPHER_CTX_set_padding(ctx.get(), 0);
+ const size_t output_size = input_size;
+ std::unique_ptr<char[]> output(new char[output_size]);
+ uint8_t* in_ptr = const_cast<uint8_t*>(input);
+ uint8_t* out_ptr = reinterpret_cast<uint8_t*>(output.get());
+ size_t bytes_remaining = output_size;
+
+ while (bytes_remaining) {
+ int unused;
+ size_t amount_to_decrypt = has_pattern ? 16UL : bytes_remaining;
+ EXPECT_EQ(EVP_CipherUpdate(ctx.get(), out_ptr, &unused, in_ptr,
+ amount_to_decrypt),
+ 1);
+ bytes_remaining -= amount_to_decrypt;
+ if (bytes_remaining) {
+ out_ptr += amount_to_decrypt;
+ in_ptr += amount_to_decrypt;
+ size_t amount_to_skip = 144UL;
+ if (amount_to_skip > bytes_remaining)
+ amount_to_skip = bytes_remaining;
+ memcpy(out_ptr, in_ptr, amount_to_skip);
+ out_ptr += amount_to_skip;
+ in_ptr += amount_to_skip;
+ bytes_remaining -= amount_to_skip;
+ }
+ }
+
+ result.assign(output.get(), output_size);
+ return result;
+}
+
+// We only support AES-CBC at this time.
+// For the purpose of these tests, the key id is also used as the actual key.
+std::string DecryptBuffer(const StreamParserBuffer& buffer,
+ const EncryptionScheme& scheme) {
+ EXPECT_TRUE(scheme.is_encrypted());
+ EXPECT_TRUE(scheme.mode() == EncryptionScheme::CIPHER_MODE_AES_CBC);
+ bool has_pattern = scheme.pattern().IsInEffect();
+ EXPECT_TRUE(!has_pattern ||
+ scheme.pattern().Matches(EncryptionScheme::Pattern(1, 9)));
+
+ std::string key;
+ EXPECT_TRUE(
+ LookupTestKeyString(buffer.decrypt_config()->key_id(), false, &key));
+ std::string iv = buffer.decrypt_config()->iv();
+ EXPECT_EQ(key.size(), 16UL);
+ EXPECT_EQ(iv.size(), 16UL);
+ std::string result;
+ uint8_t* in_ptr = const_cast<uint8_t*>(buffer.data());
+ const DecryptConfig* decrypt_config = buffer.decrypt_config();
+ for (const auto& subsample : decrypt_config->subsamples()) {
+ std::string clear(reinterpret_cast<char*>(in_ptr), subsample.clear_bytes);
+ result += clear;
+ in_ptr += subsample.clear_bytes;
+ result +=
+ DecryptSampleAES(key, iv, in_ptr, subsample.cypher_bytes, has_pattern);
+ in_ptr += subsample.cypher_bytes;
+ }
+ return result;
+}
+#endif
+
} // namespace
class Mp2tStreamParserTest : public testing::Test {
@@ -66,7 +165,10 @@ class Mp2tStreamParserTest : public testing::Test {
video_min_dts_(kNoDecodeTimestamp()),
video_max_dts_(kNoDecodeTimestamp()),
audio_track_id_(0),
- video_track_id_(0) {
+ video_track_id_(0),
+ current_audio_config_(),
+ current_video_config_(),
+ capture_buffers(false) {
bool has_sbr = false;
parser_.reset(new Mp2tStreamParser(has_sbr));
}
@@ -85,6 +187,12 @@ class Mp2tStreamParserTest : public testing::Test {
StreamParser::TrackId audio_track_id_;
StreamParser::TrackId video_track_id_;
+ AudioDecoderConfig current_audio_config_;
+ VideoDecoderConfig current_video_config_;
+ std::vector<scoped_refptr<StreamParserBuffer>> audio_buffer_capture_;
+ std::vector<scoped_refptr<StreamParserBuffer>> video_buffer_capture_;
+ bool capture_buffers;
+
void ResetStats() {
segment_count_ = 0;
config_count_ = 0;
@@ -131,10 +239,12 @@ class Mp2tStreamParserTest : public testing::Test {
audio_track_id_ = track_id;
found_audio_track = true;
EXPECT_TRUE(tracks->getAudioConfig(track_id).IsValidConfig());
+ current_audio_config_ = tracks->getAudioConfig(track_id);
} else if (track->type() == MediaTrack::Video) {
video_track_id_ = track_id;
found_video_track = true;
EXPECT_TRUE(tracks->getVideoConfig(track_id).IsValidConfig());
+ current_video_config_ = tracks->getVideoConfig(track_id);
} else {
// Unexpected track type.
LOG(ERROR) << "Unexpected track type " << track->type();
@@ -147,6 +257,18 @@ class Mp2tStreamParserTest : public testing::Test {
return true;
}
+ void CaptureVideoBuffers(const StreamParser::BufferQueue& video_buffers) {
+ for (const auto& buffer : video_buffers) {
+ video_buffer_capture_.push_back(buffer);
+ }
+ }
+
+ void CaptureAudioBuffers(const StreamParser::BufferQueue& audio_buffers) {
+ for (const auto& buffer : audio_buffers) {
+ audio_buffer_capture_.push_back(buffer);
+ }
+ }
+
bool OnNewBuffers(const StreamParser::BufferQueueMap& buffer_queue_map) {
EXPECT_GT(config_count_, 0);
// Ensure that track ids are properly assigned on all emitted buffers.
@@ -173,6 +295,11 @@ class Mp2tStreamParserTest : public testing::Test {
(itr_video == buffer_queue_map.end()) ? empty_buffers
: itr_video->second;
+ if (capture_buffers) {
+ CaptureVideoBuffers(video_buffers);
+ CaptureAudioBuffers(audio_buffers);
+ }
+
// Verify monotonicity.
if (!IsMonotonic(video_buffers))
return false;
@@ -205,8 +332,10 @@ class Mp2tStreamParserTest : public testing::Test {
void OnKeyNeeded(EmeInitDataType type,
const std::vector<uint8_t>& init_data) {
+#if !BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
LOG(ERROR) << "OnKeyNeeded not expected in the Mpeg2 TS parser";
EXPECT_TRUE(false);
+#endif
}
void OnNewSegment() {
@@ -324,5 +453,57 @@ TEST_F(Mp2tStreamParserTest, AudioInPrivateStream1) {
EXPECT_EQ(segment_count_, 1);
}
+#if BUILDFLAG(ENABLE_HLS_SAMPLE_AES)
+TEST_F(Mp2tStreamParserTest, HLSSampleAES) {
+ std::vector<std::string> decrypted_video_buffers;
+ std::vector<std::string> decrypted_audio_buffers;
+ InitializeParser();
+ capture_buffers = true;
+ ParseMpeg2TsFile("bear-1280x720-hls-sample-aes.ts", 2048);
+ parser_->Flush();
+ EncryptionScheme video_encryption_scheme =
+ current_video_config_.encryption_scheme();
+ EXPECT_TRUE(video_encryption_scheme.is_encrypted());
+ for (const auto& buffer : video_buffer_capture_) {
+ std::string decrypted_video_buffer =
+ DecryptBuffer(*buffer.get(), video_encryption_scheme);
+ decrypted_video_buffers.push_back(decrypted_video_buffer);
+ }
+ EncryptionScheme audio_encryption_scheme =
+ current_audio_config_.encryption_scheme();
+ EXPECT_TRUE(audio_encryption_scheme.is_encrypted());
+ for (const auto& buffer : audio_buffer_capture_) {
+ std::string decrypted_audio_buffer =
+ DecryptBuffer(*buffer.get(), audio_encryption_scheme);
+ decrypted_audio_buffers.push_back(decrypted_audio_buffer);
+ }
+
+ parser_.reset(new Mp2tStreamParser(false));
+ ResetStats();
+ InitializeParser();
+ video_buffer_capture_.clear();
+ audio_buffer_capture_.clear();
+ ParseMpeg2TsFile("bear-1280x720-hls.ts", 2048);
+ parser_->Flush();
+ video_encryption_scheme = current_video_config_.encryption_scheme();
+ EXPECT_FALSE(video_encryption_scheme.is_encrypted());
+ // Skip the last buffer, which may be truncated.
+ for (size_t i = 0; i + 1 < video_buffer_capture_.size(); i++) {
+ const auto& buffer = video_buffer_capture_[i];
+ std::string unencrypted_video_buffer(
+ reinterpret_cast<const char*>(buffer->data()), buffer->data_size());
+ EXPECT_EQ(decrypted_video_buffers[i], unencrypted_video_buffer);
+ }
+ audio_encryption_scheme = current_audio_config_.encryption_scheme();
+ EXPECT_FALSE(audio_encryption_scheme.is_encrypted());
+ for (size_t i = 0; i + 1 < audio_buffer_capture_.size(); i++) {
+ const auto& buffer = audio_buffer_capture_[i];
+ std::string unencrypted_audio_buffer(
+ reinterpret_cast<const char*>(buffer->data()), buffer->data_size());
+ EXPECT_EQ(decrypted_audio_buffers[i], unencrypted_audio_buffer);
+ }
+}
+#endif
+
} // namespace mp2t
} // namespace media
« no previous file with comments | « media/formats/mp2t/mp2t_stream_parser.cc ('k') | media/formats/mp2t/ts_section_cat.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698