Commit 10b46bd3 authored by pwestin@google.com's avatar pwestin@google.com

Cast: Added support for AES-CTR crypto.

Full audio and video frames can now be encrypted decrypted within cast.
Since AES-CTR mode is used there is no need for padding.

BUG=315931

Review URL: https://codereview.chromium.org/62843002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@235864 0039d316-1c4b-4281-b951-d872f2087c98
parent 220454d1
include_rules = [ include_rules = [
"+net", "+crypto",
"+net",
"+third_party/webrtc", "+third_party/webrtc",
"+third_party/libyuv", "+third_party/libyuv",
] ]
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop.h"
#include "crypto/encryptor.h"
#include "crypto/symmetric_key.h"
#include "media/cast/audio_receiver/audio_decoder.h" #include "media/cast/audio_receiver/audio_decoder.h"
#include "media/cast/framer/framer.h" #include "media/cast/framer/framer.h"
#include "media/cast/rtcp/rtcp.h" #include "media/cast/rtcp/rtcp.h"
...@@ -97,6 +99,18 @@ AudioReceiver::AudioReceiver(scoped_refptr<CastEnvironment> cast_environment, ...@@ -97,6 +99,18 @@ AudioReceiver::AudioReceiver(scoped_refptr<CastEnvironment> cast_environment,
} else { } else {
audio_decoder_ = new AudioDecoder(audio_config); audio_decoder_ = new AudioDecoder(audio_config);
} }
if (audio_config.aes_iv_mask.size() == kAesKeySize &&
audio_config.aes_key.size() == kAesKeySize) {
iv_mask_ = audio_config.aes_iv_mask;
crypto::SymmetricKey* key = crypto::SymmetricKey::Import(
crypto::SymmetricKey::AES, audio_config.aes_key);
decryptor_.reset(new crypto::Encryptor());
decryptor_->Init(key, crypto::Encryptor::CTR, std::string());
} else if (audio_config.aes_iv_mask.size() != 0 ||
audio_config.aes_key.size() != 0) {
DCHECK(false) << "Invalid crypto configuration";
}
rtp_receiver_.reset(new RtpReceiver(cast_environment->Clock(), rtp_receiver_.reset(new RtpReceiver(cast_environment->Clock(),
&audio_config, &audio_config,
NULL, NULL,
...@@ -138,8 +152,23 @@ void AudioReceiver::IncomingParsedRtpPacket(const uint8* payload_data, ...@@ -138,8 +152,23 @@ void AudioReceiver::IncomingParsedRtpPacket(const uint8* payload_data,
if (audio_decoder_) { if (audio_decoder_) {
DCHECK(!audio_buffer_) << "Invalid internal state"; DCHECK(!audio_buffer_) << "Invalid internal state";
audio_decoder_->IncomingParsedRtpPacket(payload_data, payload_size, std::string plaintext(reinterpret_cast<const char*>(payload_data),
rtp_header); payload_size);
if (decryptor_) {
plaintext.clear();
if (!decryptor_->SetCounter(GetAesNonce(rtp_header.frame_id, iv_mask_))) {
NOTREACHED() << "Failed to set counter";
return;
}
if (!decryptor_->Decrypt(base::StringPiece(reinterpret_cast<const char*>(
payload_data), payload_size), &plaintext)) {
VLOG(0) << "Decryption error";
return;
}
}
audio_decoder_->IncomingParsedRtpPacket(
reinterpret_cast<const uint8*>(plaintext.data()), plaintext.size(),
rtp_header);
return; return;
} }
DCHECK(audio_buffer_) << "Invalid internal state"; DCHECK(audio_buffer_) << "Invalid internal state";
...@@ -214,6 +243,12 @@ void AudioReceiver::PlayoutTimeout() { ...@@ -214,6 +243,12 @@ void AudioReceiver::PlayoutTimeout() {
VLOG(1) << "Failed to retrieved a complete frame at this point in time"; VLOG(1) << "Failed to retrieved a complete frame at this point in time";
return; return;
} }
if (decryptor_ && !DecryptAudioFrame(&encoded_frame)) {
// Logging already done.
return;
}
if (PostEncodedAudioFrame(queued_encoded_callbacks_.front(), rtp_timestamp, if (PostEncodedAudioFrame(queued_encoded_callbacks_.front(), rtp_timestamp,
next_frame, &encoded_frame)) { next_frame, &encoded_frame)) {
// Call succeed remove callback from list. // Call succeed remove callback from list.
...@@ -237,6 +272,11 @@ void AudioReceiver::GetEncodedAudioFrame( ...@@ -237,6 +272,11 @@ void AudioReceiver::GetEncodedAudioFrame(
queued_encoded_callbacks_.push_back(callback); queued_encoded_callbacks_.push_back(callback);
return; return;
} }
if (decryptor_ && !DecryptAudioFrame(&encoded_frame)) {
// Logging already done.
queued_encoded_callbacks_.push_back(callback);
return;
}
if (!PostEncodedAudioFrame(callback, rtp_timestamp, next_frame, if (!PostEncodedAudioFrame(callback, rtp_timestamp, next_frame,
&encoded_frame)) { &encoded_frame)) {
// We have an audio frame; however we are missing packets and we have time // We have an audio frame; however we are missing packets and we have time
...@@ -324,6 +364,26 @@ base::TimeTicks AudioReceiver::GetPlayoutTime(base::TimeTicks now, ...@@ -324,6 +364,26 @@ base::TimeTicks AudioReceiver::GetPlayoutTime(base::TimeTicks now,
now; now;
} }
bool AudioReceiver::DecryptAudioFrame(
scoped_ptr<EncodedAudioFrame>* audio_frame) {
DCHECK(decryptor_) << "Invalid state";
if (!decryptor_->SetCounter(GetAesNonce((*audio_frame)->frame_id,
iv_mask_))) {
NOTREACHED() << "Failed to set counter";
return false;
}
std::string decrypted_audio_data;
if (!decryptor_->Decrypt((*audio_frame)->data, &decrypted_audio_data)) {
VLOG(0) << "Decryption error";
// Give up on this frame, release it from jitter buffer.
audio_buffer_->ReleaseFrame((*audio_frame)->frame_id);
return false;
}
(*audio_frame)->data.swap(decrypted_audio_data);
return true;
}
void AudioReceiver::ScheduleNextRtcpReport() { void AudioReceiver::ScheduleNextRtcpReport() {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
base::TimeDelta time_to_send = rtcp_->TimeToSendNextRtcpReport() - base::TimeDelta time_to_send = rtcp_->TimeToSendNextRtcpReport() -
......
...@@ -8,8 +8,8 @@ ...@@ -8,8 +8,8 @@
'target_name': 'cast_audio_receiver', 'target_name': 'cast_audio_receiver',
'type': 'static_library', 'type': 'static_library',
'include_dirs': [ 'include_dirs': [
'<(DEPTH)/', '<(DEPTH)/',
'<(DEPTH)/third_party/', '<(DEPTH)/third_party/',
'<(DEPTH)/third_party/webrtc/', '<(DEPTH)/third_party/webrtc/',
], ],
'sources': [ 'sources': [
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
'audio_receiver.cc', 'audio_receiver.cc',
], # source ], # source
'dependencies': [ 'dependencies': [
'<(DEPTH)/crypto/crypto.gyp:crypto',
'<(DEPTH)/media/cast/rtcp/rtcp.gyp:cast_rtcp', '<(DEPTH)/media/cast/rtcp/rtcp.gyp:cast_rtcp',
'<(DEPTH)/media/cast/rtp_receiver/rtp_receiver.gyp:*', '<(DEPTH)/media/cast/rtp_receiver/rtp_receiver.gyp:*',
'<(DEPTH)/third_party/webrtc/webrtc.gyp:webrtc', '<(DEPTH)/third_party/webrtc/webrtc.gyp:webrtc',
......
...@@ -19,6 +19,10 @@ ...@@ -19,6 +19,10 @@
#include "media/cast/rtcp/rtcp.h" // RtcpCastMessage #include "media/cast/rtcp/rtcp.h" // RtcpCastMessage
#include "media/cast/rtp_common/rtp_defines.h" // RtpCastHeader #include "media/cast/rtp_common/rtp_defines.h" // RtpCastHeader
namespace crypto {
class Encryptor;
}
namespace media { namespace media {
namespace cast { namespace cast {
...@@ -83,6 +87,10 @@ class AudioReceiver : public base::NonThreadSafe, ...@@ -83,6 +87,10 @@ class AudioReceiver : public base::NonThreadSafe,
void InitializeTimers(); void InitializeTimers();
// Decrypts the data within the |audio_frame| and replaces the data with the
// decrypted string.
bool DecryptAudioFrame(scoped_ptr<EncodedAudioFrame>* audio_frame);
// Schedule the next RTCP report. // Schedule the next RTCP report.
void ScheduleNextRtcpReport(); void ScheduleNextRtcpReport();
...@@ -111,6 +119,8 @@ class AudioReceiver : public base::NonThreadSafe, ...@@ -111,6 +119,8 @@ class AudioReceiver : public base::NonThreadSafe,
base::TimeDelta time_offset_; base::TimeDelta time_offset_;
base::TimeTicks time_first_incoming_packet_; base::TimeTicks time_first_incoming_packet_;
uint32 first_incoming_rtp_timestamp_; uint32 first_incoming_rtp_timestamp_;
scoped_ptr<crypto::Encryptor> decryptor_;
std::string iv_mask_;
std::list<AudioFrameEncodedCallback> queued_encoded_callbacks_; std::list<AudioFrameEncodedCallback> queued_encoded_callbacks_;
}; };
......
...@@ -94,7 +94,7 @@ class AudioEncoder::ImplBase { ...@@ -94,7 +94,7 @@ class AudioEncoder::ImplBase {
int source_offset, int source_offset,
int buffer_fill_offset, int buffer_fill_offset,
int num_samples) = 0; int num_samples) = 0;
virtual bool EncodeFromFilledBuffer(std::vector<uint8>* out) = 0; virtual bool EncodeFromFilledBuffer(std::string* out) = 0;
CastEnvironment* const cast_environment_; CastEnvironment* const cast_environment_;
const AudioCodec codec_; const AudioCodec codec_;
...@@ -156,11 +156,11 @@ class AudioEncoder::OpusImpl : public AudioEncoder::ImplBase { ...@@ -156,11 +156,11 @@ class AudioEncoder::OpusImpl : public AudioEncoder::ImplBase {
} }
} }
virtual bool EncodeFromFilledBuffer(std::vector<uint8>* out) OVERRIDE { virtual bool EncodeFromFilledBuffer(std::string* out) OVERRIDE {
out->resize(kOpusMaxPayloadSize); out->resize(kOpusMaxPayloadSize);
const opus_int32 result = opus_encode_float( const opus_int32 result = opus_encode_float(
opus_encoder_, buffer_.get(), samples_per_10ms_, &out->front(), opus_encoder_, buffer_.get(), samples_per_10ms_,
kOpusMaxPayloadSize); reinterpret_cast<uint8*>(&out->at(0)), kOpusMaxPayloadSize);
if (result > 1) { if (result > 1) {
out->resize(result); out->resize(result);
return true; return true;
...@@ -210,12 +210,12 @@ class AudioEncoder::Pcm16Impl : public AudioEncoder::ImplBase { ...@@ -210,12 +210,12 @@ class AudioEncoder::Pcm16Impl : public AudioEncoder::ImplBase {
buffer_.get() + buffer_fill_offset * num_channels_); buffer_.get() + buffer_fill_offset * num_channels_);
} }
virtual bool EncodeFromFilledBuffer(std::vector<uint8>* out) OVERRIDE { virtual bool EncodeFromFilledBuffer(std::string* out) OVERRIDE {
// Output 16-bit PCM integers in big-endian byte order. // Output 16-bit PCM integers in big-endian byte order.
out->resize(num_channels_ * samples_per_10ms_ * sizeof(int16)); out->resize(num_channels_ * samples_per_10ms_ * sizeof(int16));
const int16* src = buffer_.get(); const int16* src = buffer_.get();
const int16* const src_end = src + num_channels_ * samples_per_10ms_; const int16* const src_end = src + num_channels_ * samples_per_10ms_;
uint16* dest = reinterpret_cast<uint16*>(&out->front()); uint16* dest = reinterpret_cast<uint16*>(&out->at(0));
for (; src < src_end; ++src, ++dest) for (; src < src_end; ++src, ++dest)
*dest = base::HostToNet16(*src); *dest = base::HostToNet16(*src);
return true; return true;
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop.h"
#include "crypto/encryptor.h"
#include "crypto/symmetric_key.h"
#include "media/cast/audio_sender/audio_encoder.h" #include "media/cast/audio_sender/audio_encoder.h"
#include "media/cast/rtcp/rtcp.h" #include "media/cast/rtcp/rtcp.h"
#include "media/cast/rtp_sender/rtp_sender.h" #include "media/cast/rtp_sender/rtp_sender.h"
...@@ -71,6 +73,17 @@ AudioSender::AudioSender(scoped_refptr<CastEnvironment> cast_environment, ...@@ -71,6 +73,17 @@ AudioSender::AudioSender(scoped_refptr<CastEnvironment> cast_environment,
audio_config.rtcp_c_name), audio_config.rtcp_c_name),
initialized_(false), initialized_(false),
weak_factory_(this) { weak_factory_(this) {
if (audio_config.aes_iv_mask.size() == kAesKeySize &&
audio_config.aes_key.size() == kAesKeySize) {
iv_mask_ = audio_config.aes_iv_mask;
crypto::SymmetricKey* key = crypto::SymmetricKey::Import(
crypto::SymmetricKey::AES, audio_config.aes_key);
encryptor_.reset(new crypto::Encryptor());
encryptor_->Init(key, crypto::Encryptor::CTR, std::string());
} else if (audio_config.aes_iv_mask.size() != 0 ||
audio_config.aes_key.size() != 0) {
DCHECK(false) << "Invalid crypto configuration";
}
if (!audio_config.use_external_encoder) { if (!audio_config.use_external_encoder) {
audio_encoder_ = new AudioEncoder( audio_encoder_ = new AudioEncoder(
cast_environment, audio_config, cast_environment, audio_config,
...@@ -102,7 +115,17 @@ void AudioSender::InsertCodedAudioFrame(const EncodedAudioFrame* audio_frame, ...@@ -102,7 +115,17 @@ void AudioSender::InsertCodedAudioFrame(const EncodedAudioFrame* audio_frame,
const base::Closure callback) { const base::Closure callback) {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
DCHECK(audio_encoder_.get() == NULL) << "Invalid internal state"; DCHECK(audio_encoder_.get() == NULL) << "Invalid internal state";
rtp_sender_.IncomingEncodedAudioFrame(audio_frame, recorded_time);
if (encryptor_) {
EncodedAudioFrame encrypted_frame;
if (!EncryptAudioFrame(*audio_frame, &encrypted_frame)) {
// Logging already done.
return;
}
rtp_sender_.IncomingEncodedAudioFrame(&encrypted_frame, recorded_time);
} else {
rtp_sender_.IncomingEncodedAudioFrame(audio_frame, recorded_time);
}
callback.Run(); callback.Run();
} }
...@@ -111,7 +134,34 @@ void AudioSender::SendEncodedAudioFrame( ...@@ -111,7 +134,34 @@ void AudioSender::SendEncodedAudioFrame(
const base::TimeTicks& recorded_time) { const base::TimeTicks& recorded_time) {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
InitializeTimers(); InitializeTimers();
rtp_sender_.IncomingEncodedAudioFrame(audio_frame.get(), recorded_time); if (encryptor_) {
EncodedAudioFrame encrypted_frame;
if (!EncryptAudioFrame(*audio_frame.get(), &encrypted_frame)) {
// Logging already done.
return;
}
rtp_sender_.IncomingEncodedAudioFrame(&encrypted_frame, recorded_time);
} else {
rtp_sender_.IncomingEncodedAudioFrame(audio_frame.get(), recorded_time);
}
}
bool AudioSender::EncryptAudioFrame(const EncodedAudioFrame& audio_frame,
EncodedAudioFrame* encrypted_frame) {
DCHECK(encryptor_) << "Invalid state";
if (!encryptor_->SetCounter(GetAesNonce(audio_frame.frame_id, iv_mask_))) {
NOTREACHED() << "Failed to set counter";
return false;
}
if (!encryptor_->Encrypt(audio_frame.data, &encrypted_frame->data)) {
NOTREACHED() << "Encrypt error";
return false;
}
encrypted_frame->codec = audio_frame.codec;
encrypted_frame->frame_id = audio_frame.frame_id;
encrypted_frame->samples = audio_frame.samples;
return true;
} }
void AudioSender::ResendPackets( void AudioSender::ResendPackets(
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
'audio_sender.cc', 'audio_sender.cc',
], # source ], # source
'dependencies': [ 'dependencies': [
'<(DEPTH)/crypto/crypto.gyp:crypto',
'<(DEPTH)/media/media.gyp:media', '<(DEPTH)/media/media.gyp:media',
'<(DEPTH)/media/media.gyp:shared_memory_support', '<(DEPTH)/media/media.gyp:shared_memory_support',
'<(DEPTH)/media/cast/rtcp/rtcp.gyp:cast_rtcp', '<(DEPTH)/media/cast/rtcp/rtcp.gyp:cast_rtcp',
......
...@@ -17,6 +17,10 @@ ...@@ -17,6 +17,10 @@
#include "media/cast/rtcp/rtcp.h" #include "media/cast/rtcp/rtcp.h"
#include "media/cast/rtp_sender/rtp_sender.h" #include "media/cast/rtp_sender/rtp_sender.h"
namespace crypto {
class Encryptor;
}
namespace media { namespace media {
class AudioBus; class AudioBus;
} }
...@@ -70,6 +74,11 @@ class AudioSender : public base::NonThreadSafe, ...@@ -70,6 +74,11 @@ class AudioSender : public base::NonThreadSafe,
void ResendPackets( void ResendPackets(
const MissingFramesAndPacketsMap& missing_frames_and_packets); const MissingFramesAndPacketsMap& missing_frames_and_packets);
// Caller must allocate the destination |encrypted_frame|. The data member
// will be resized to hold the encrypted size.
bool EncryptAudioFrame(const EncodedAudioFrame& audio_frame,
EncodedAudioFrame* encrypted_frame);
void ScheduleNextRtcpReport(); void ScheduleNextRtcpReport();
void SendRtcpReport(); void SendRtcpReport();
...@@ -84,6 +93,8 @@ class AudioSender : public base::NonThreadSafe, ...@@ -84,6 +93,8 @@ class AudioSender : public base::NonThreadSafe,
scoped_ptr<LocalRtcpAudioSenderFeedback> rtcp_feedback_; scoped_ptr<LocalRtcpAudioSenderFeedback> rtcp_feedback_;
Rtcp rtcp_; Rtcp rtcp_;
bool initialized_; bool initialized_;
scoped_ptr<crypto::Encryptor> encryptor_;
std::string iv_mask_;
DISALLOW_COPY_AND_ASSIGN(AudioSender); DISALLOW_COPY_AND_ASSIGN(AudioSender);
}; };
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
'logging/logging.gyp:cast_logging', 'logging/logging.gyp:cast_logging',
'test/utility/utility.gyp:cast_test_utility', 'test/utility/utility.gyp:cast_test_utility',
'<(DEPTH)/base/base.gyp:run_all_unittests', '<(DEPTH)/base/base.gyp:run_all_unittests',
'<(DEPTH)/crypto/crypto.gyp:crypto',
'<(DEPTH)/net/net.gyp:net', '<(DEPTH)/net/net.gyp:net',
'<(DEPTH)/testing/gmock.gyp:gmock', '<(DEPTH)/testing/gmock.gyp:gmock',
'<(DEPTH)/testing/gtest.gyp:gtest', '<(DEPTH)/testing/gtest.gyp:gtest',
...@@ -72,6 +73,8 @@ ...@@ -72,6 +73,8 @@
'rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc', 'rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc',
'rtp_sender/rtp_packetizer/test/rtp_header_parser.cc', 'rtp_sender/rtp_packetizer/test/rtp_header_parser.cc',
'rtp_sender/rtp_packetizer/test/rtp_header_parser.h', 'rtp_sender/rtp_packetizer/test/rtp_header_parser.h',
'test/crypto_utility.cc',
'test/crypto_utility.h',
'test/encode_decode_test.cc', 'test/encode_decode_test.cc',
'test/end2end_unittest.cc', 'test/end2end_unittest.cc',
'video_receiver/video_decoder_unittest.cc', 'video_receiver/video_decoder_unittest.cc',
......
...@@ -53,6 +53,9 @@ struct AudioSenderConfig { ...@@ -53,6 +53,9 @@ struct AudioSenderConfig {
int channels; int channels;
int bitrate; // Set to <= 0 for "auto variable bitrate" (libopus knows best). int bitrate; // Set to <= 0 for "auto variable bitrate" (libopus knows best).
AudioCodec codec; AudioCodec codec;
std::string aes_key; // Binary string of size kAesKeySize.
std::string aes_iv_mask; // Binary string of size kAesKeySize.
}; };
struct VideoSenderConfig { struct VideoSenderConfig {
...@@ -83,6 +86,9 @@ struct VideoSenderConfig { ...@@ -83,6 +86,9 @@ struct VideoSenderConfig {
int max_number_of_video_buffers_used; // Max value depend on codec. int max_number_of_video_buffers_used; // Max value depend on codec.
VideoCodec codec; VideoCodec codec;
int number_of_cores; int number_of_cores;
std::string aes_key; // Binary string of size kAesKeySize.
std::string aes_iv_mask; // Binary string of size kAesKeySize.
}; };
struct AudioReceiverConfig { struct AudioReceiverConfig {
...@@ -103,6 +109,9 @@ struct AudioReceiverConfig { ...@@ -103,6 +109,9 @@ struct AudioReceiverConfig {
int frequency; int frequency;
int channels; int channels;
AudioCodec codec; AudioCodec codec;
std::string aes_key; // Binary string of size kAesKeySize.
std::string aes_iv_mask; // Binary string of size kAesKeySize.
}; };
struct VideoReceiverConfig { struct VideoReceiverConfig {
...@@ -126,6 +135,9 @@ struct VideoReceiverConfig { ...@@ -126,6 +135,9 @@ struct VideoReceiverConfig {
// from catching up after a glitch. // from catching up after a glitch.
bool decoder_faster_than_max_frame_rate; bool decoder_faster_than_max_frame_rate;
VideoCodec codec; VideoCodec codec;
std::string aes_key; // Binary string of size kAesKeySize.
std::string aes_iv_mask; // Binary string of size kAesKeySize.
}; };
// DEPRECATED: Do not use in new code. Please migrate existing code to use // DEPRECATED: Do not use in new code. Please migrate existing code to use
...@@ -154,7 +166,7 @@ struct EncodedVideoFrame { ...@@ -154,7 +166,7 @@ struct EncodedVideoFrame {
bool key_frame; bool key_frame;
uint32 frame_id; uint32 frame_id;
uint32 last_referenced_frame_id; uint32 last_referenced_frame_id;
std::vector<uint8> data; std::string data;
}; };
// DEPRECATED: Do not use in new code. Please migrate existing code to use // DEPRECATED: Do not use in new code. Please migrate existing code to use
...@@ -176,10 +188,9 @@ struct EncodedAudioFrame { ...@@ -176,10 +188,9 @@ struct EncodedAudioFrame {
uint32 frame_id; // Needed to release the frame. uint32 frame_id; // Needed to release the frame.
int samples; // Needed send side to advance the RTP timestamp. int samples; // Needed send side to advance the RTP timestamp.
// Not used receive side. // Not used receive side.
std::vector<uint8> data;
// Support for max sampling rate of 48KHz, 2 channels, 100 ms duration. // Support for max sampling rate of 48KHz, 2 channels, 100 ms duration.
static const int kMaxNumberOfSamples = 48 * 2 * 100; static const int kMaxNumberOfSamples = 48 * 2 * 100;
std::string data;
}; };
typedef std::vector<uint8> Packet; typedef std::vector<uint8> Packet;
......
...@@ -48,6 +48,9 @@ const size_t kMinLengthOfRtcp = 8; ...@@ -48,6 +48,9 @@ const size_t kMinLengthOfRtcp = 8;
// Basic RTP header + cast header. // Basic RTP header + cast header.
const size_t kMinLengthOfRtp = 12 + 6; const size_t kMinLengthOfRtp = 12 + 6;
const size_t kAesBlockSize = 16;
const size_t kAesKeySize = 16;
// Each uint16 represents one packet id within a cast frame. // Each uint16 represents one packet id within a cast frame.
typedef std::set<uint16> PacketIdSet; typedef std::set<uint16> PacketIdSet;
// Each uint8 represents one cast frame. // Each uint8 represents one cast frame.
...@@ -167,6 +170,22 @@ class FrameIdWrapHelper { ...@@ -167,6 +170,22 @@ class FrameIdWrapHelper {
uint32 frame_id_wrap_count_; uint32 frame_id_wrap_count_;
}; };
inline std::string GetAesNonce(uint32 frame_id, const std::string& iv_mask) {
std::string aes_nonce(kAesBlockSize, 0);
// Serializing frame_id in big-endian order (aes_nonce[8] is the most
// significant byte of frame_id).
aes_nonce[11] = frame_id & 0xff;
aes_nonce[10] = (frame_id >> 8) & 0xff;
aes_nonce[9] = (frame_id >> 16) & 0xff;
aes_nonce[8] = (frame_id >> 24) & 0xff;
for (size_t i = 0; i < kAesBlockSize; ++i) {
aes_nonce[i] ^= iv_mask[i];
}
return aes_nonce;
}
} // namespace cast } // namespace cast
} // namespace media } // namespace media
......
...@@ -22,11 +22,11 @@ ...@@ -22,11 +22,11 @@
'cast_receiver_impl.h', 'cast_receiver_impl.h',
], # source ], # source
'dependencies': [ 'dependencies': [
'rtp_receiver/rtp_receiver.gyp:*', '<(DEPTH)/crypto/crypto.gyp:crypto',
'cast_audio_receiver', 'cast_audio_receiver',
'cast_video_receiver', 'cast_video_receiver',
'framer/framer.gyp:cast_framer',
'pacing/paced_sender.gyp:cast_paced_sender', 'pacing/paced_sender.gyp:cast_paced_sender',
'rtp_receiver/rtp_receiver.gyp:cast_rtp_receiver',
], ],
}, },
], ],
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
'cast_sender_impl.h', 'cast_sender_impl.h',
], # source ], # source
'dependencies': [ 'dependencies': [
'<(DEPTH)/crypto/crypto.gyp:crypto',
'audio_sender', 'audio_sender',
'congestion_control', 'congestion_control',
'pacing/paced_sender.gyp:cast_paced_sender', 'pacing/paced_sender.gyp:cast_paced_sender',
......
...@@ -32,6 +32,7 @@ class FrameBuffer { ...@@ -32,6 +32,7 @@ class FrameBuffer {
uint32* rtp_timestamp) const; uint32* rtp_timestamp) const;
bool is_key_frame() const { return is_key_frame_; } bool is_key_frame() const { return is_key_frame_; }
uint32 last_referenced_frame_id() const { return last_referenced_frame_id_; } uint32 last_referenced_frame_id() const { return last_referenced_frame_id_; }
private: private:
......
...@@ -82,7 +82,7 @@ void RtpPacketizer::Cast(bool is_key, ...@@ -82,7 +82,7 @@ void RtpPacketizer::Cast(bool is_key,
uint32 frame_id, uint32 frame_id,
uint32 reference_frame_id, uint32 reference_frame_id,
uint32 timestamp, uint32 timestamp,
Packet data) { const std::string& data) {
uint16 rtp_header_length = kCommonRtpHeaderLength + kCastRtpHeaderLength; uint16 rtp_header_length = kCommonRtpHeaderLength + kCastRtpHeaderLength;
uint16 max_length = config_.max_payload_length - rtp_header_length - 1; uint16 max_length = config_.max_payload_length - rtp_header_length - 1;
...@@ -94,7 +94,7 @@ void RtpPacketizer::Cast(bool is_key, ...@@ -94,7 +94,7 @@ void RtpPacketizer::Cast(bool is_key,
PacketList packets; PacketList packets;
size_t remaining_size = data.size(); size_t remaining_size = data.size();
Packet::iterator data_iter = data.begin(); std::string::const_iterator data_iter = data.begin();
while (remaining_size > 0) { while (remaining_size > 0) {
Packet packet; Packet packet;
......
...@@ -49,10 +49,9 @@ class RtpPacketizer { ...@@ -49,10 +49,9 @@ class RtpPacketizer {
size_t send_octet_count() { return send_octet_count_; } size_t send_octet_count() { return send_octet_count_; }
private: private:
void Cast(bool is_key, uint32 frame_id, void Cast(bool is_key, uint32 frame_id, uint32 reference_frame_id,
uint32 reference_frame_id, uint32 timestamp, const std::string& data);
uint32 timestamp,
std::vector<uint8> data);
void BuildCommonRTPheader(std::vector<uint8>* packet, bool marker_bit, void BuildCommonRTPheader(std::vector<uint8>* packet, bool marker_bit,
uint32 time_stamp); uint32 time_stamp);
......
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/strings/string_number_conversions.h"
#include "media/cast/test/crypto_utility.h"
namespace media {
namespace cast {
std::string ConvertFromBase16String(const std::string base_16) {
std::string compressed;
DCHECK_EQ(base_16.size() % 2, 0u) << "Must be a multiple of 2";
compressed.reserve(base_16.size() / 2);
std::vector<uint8> v;
if (!base::HexStringToBytes(base_16, &v)) {
NOTREACHED();
}
compressed.assign(reinterpret_cast<const char*>(&v[0]), v.size());
return compressed;
}
} // namespace cast
} // namespace media
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Utility functions for crypto testing.
#include "media/cast/cast_config.h"
namespace media {
namespace cast {
// Convert to a binary string from a base 16 string.
std::string ConvertFromBase16String(const std::string base_16);
} // namespace cast
} // namespace media
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "media/cast/cast_receiver.h" #include "media/cast/cast_receiver.h"
#include "media/cast/cast_sender.h" #include "media/cast/cast_sender.h"
#include "media/cast/test/audio_utility.h" #include "media/cast/test/audio_utility.h"
#include "media/cast/test/crypto_utility.h"
#include "media/cast/test/fake_task_runner.h" #include "media/cast/test/fake_task_runner.h"
#include "media/cast/test/video_utility.h" #include "media/cast/test/video_utility.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -226,8 +227,9 @@ class TestReceiverAudioCallback : ...@@ -226,8 +227,9 @@ class TestReceiverAudioCallback :
size_t number_of_samples = audio_frame->data.size() / 2; size_t number_of_samples = audio_frame->data.size() / 2;
for (size_t i = 0; i < number_of_samples; ++i) { for (size_t i = 0; i < number_of_samples; ++i) {
uint16 sample = (audio_frame->data[1 + i * sizeof(uint16)]) + uint16 sample =
(static_cast<uint16>(audio_frame->data[i * sizeof(uint16)]) << 8); static_cast<uint8>(audio_frame->data[1 + i * sizeof(uint16)]) +
(static_cast<uint16>(audio_frame->data[i * sizeof(uint16)]) << 8);
output_audio_samples.push_back(static_cast<int16>(sample)); output_audio_samples.push_back(static_cast<int16>(sample));
} }
...@@ -783,6 +785,93 @@ TEST_F(End2EndTest, ResetReferenceFrameId) { ...@@ -783,6 +785,93 @@ TEST_F(End2EndTest, ResetReferenceFrameId) {
test_receiver_video_callback_->number_times_called()); test_receiver_video_callback_->number_times_called());
} }
TEST_F(End2EndTest, CryptoVideo) {
SetupConfig(kPcm16, 32000, false, 1);
video_sender_config_.aes_iv_mask =
ConvertFromBase16String("1234567890abcdeffedcba0987654321");
video_sender_config_.aes_key =
ConvertFromBase16String("deadbeefcafeb0b0b0b0cafedeadbeef");
video_receiver_config_.aes_iv_mask = video_sender_config_.aes_iv_mask;
video_receiver_config_.aes_key = video_sender_config_.aes_key;
Create();
int frames_counter = 0;
for (; frames_counter < 20; ++frames_counter) {
const base::TimeTicks send_time = testing_clock_.NowTicks();
SendVideoFrame(frames_counter, send_time);
test_receiver_video_callback_->AddExpectedResult(frames_counter,
video_sender_config_.width, video_sender_config_.height, send_time);
// GetRawVideoFrame will not return the frame until we are close to the
// time in which we should render the frame.
frame_receiver_->GetRawVideoFrame(
base::Bind(&TestReceiverVideoCallback::CheckVideoFrame,
test_receiver_video_callback_));
RunTasks(kFrameTimerMs);
}
RunTasks(2 * kFrameTimerMs + 1); // Empty the pipeline.
EXPECT_EQ(frames_counter,
test_receiver_video_callback_->number_times_called());
}
TEST_F(End2EndTest, CryptoAudio) {
SetupConfig(kPcm16, 32000, false, 1);
audio_sender_config_.aes_iv_mask =
ConvertFromBase16String("abcdeffedcba12345678900987654321");
audio_sender_config_.aes_key =
ConvertFromBase16String("deadbeefcafecafedeadbeefb0b0b0b0");
audio_receiver_config_.aes_iv_mask = audio_sender_config_.aes_iv_mask;
audio_receiver_config_.aes_key = audio_sender_config_.aes_key;
Create();
int frames_counter = 0;
for (; frames_counter < 20; ++frames_counter) {
int num_10ms_blocks = 2;
const base::TimeTicks send_time = testing_clock_.NowTicks();
scoped_ptr<AudioBus> audio_bus(audio_bus_factory_->NextAudioBus(
base::TimeDelta::FromMilliseconds(10) * num_10ms_blocks));
if (frames_counter != 0) {
// Due to the re-sampler and NetEq in the webrtc AudioCodingModule the
// first samples will be 0 and then slowly ramp up to its real amplitude;
// ignore the first frame.
test_receiver_audio_callback_->AddExpectedResult(
ToPcmAudioFrame(*audio_bus, audio_sender_config_.frequency),
num_10ms_blocks, send_time);
}
AudioBus* const audio_bus_ptr = audio_bus.get();
frame_input_->InsertAudio(audio_bus_ptr, send_time,
base::Bind(&OwnThatAudioBus, base::Passed(&audio_bus)));
RunTasks(num_10ms_blocks * 10);
if (frames_counter == 0) {
frame_receiver_->GetRawAudioFrame(num_10ms_blocks,
32000,
base::Bind(&TestReceiverAudioCallback::IgnoreAudioFrame,
test_receiver_audio_callback_));
} else {
frame_receiver_->GetRawAudioFrame(num_10ms_blocks,
32000,
base::Bind(&TestReceiverAudioCallback::CheckPcmAudioFrame,
test_receiver_audio_callback_));
}
}
RunTasks(2 * kFrameTimerMs + 1); // Empty the pipeline.
EXPECT_EQ(frames_counter - 1,
test_receiver_audio_callback_->number_times_called());
}
// TODO(pwestin): Add repeatable packet loss test. // TODO(pwestin): Add repeatable packet loss test.
// TODO(pwestin): Add test for misaligned send get calls. // TODO(pwestin): Add test for misaligned send get calls.
// TODO(pwestin): Add more tests that does not resample. // TODO(pwestin): Add more tests that does not resample.
......
...@@ -42,11 +42,12 @@ bool Vp8Decoder::Decode(const EncodedVideoFrame* encoded_frame, ...@@ -42,11 +42,12 @@ bool Vp8Decoder::Decode(const EncodedVideoFrame* encoded_frame,
vpx_codec_iter_t iter = NULL; vpx_codec_iter_t iter = NULL;
vpx_image_t* img; vpx_image_t* img;
if (vpx_codec_decode(decoder_.get(), if (vpx_codec_decode(
encoded_frame->data.data(), decoder_.get(),
static_cast<unsigned int>(encoded_frame->data.size()), reinterpret_cast<const uint8*>(encoded_frame->data.data()),
0, static_cast<unsigned int>(encoded_frame->data.size()),
1 /* real time*/)) { 0,
1 /* real time*/)) {
VLOG(1) << "Failed to decode VP8 frame."; VLOG(1) << "Failed to decode VP8 frame.";
return false; return false;
} }
......
...@@ -5,9 +5,12 @@ ...@@ -5,9 +5,12 @@
#include "media/cast/video_receiver/video_receiver.h" #include "media/cast/video_receiver/video_receiver.h"
#include <algorithm> #include <algorithm>
#include "base/bind.h" #include "base/bind.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop.h"
#include "crypto/encryptor.h"
#include "crypto/symmetric_key.h"
#include "media/cast/cast_defines.h" #include "media/cast/cast_defines.h"
#include "media/cast/framer/framer.h" #include "media/cast/framer/framer.h"
#include "media/cast/video_receiver/video_decoder.h" #include "media/cast/video_receiver/video_decoder.h"
...@@ -102,6 +105,18 @@ VideoReceiver::VideoReceiver(scoped_refptr<CastEnvironment> cast_environment, ...@@ -102,6 +105,18 @@ VideoReceiver::VideoReceiver(scoped_refptr<CastEnvironment> cast_environment,
video_config.max_frame_rate / 1000; video_config.max_frame_rate / 1000;
DCHECK(max_unacked_frames) << "Invalid argument"; DCHECK(max_unacked_frames) << "Invalid argument";
if (video_config.aes_iv_mask.size() == kAesKeySize &&
video_config.aes_key.size() == kAesKeySize) {
iv_mask_ = video_config.aes_iv_mask;
crypto::SymmetricKey* key = crypto::SymmetricKey::Import(
crypto::SymmetricKey::AES, video_config.aes_key);
decryptor_.reset(new crypto::Encryptor());
decryptor_->Init(key, crypto::Encryptor::CTR, std::string());
} else if (video_config.aes_iv_mask.size() != 0 ||
video_config.aes_key.size() != 0) {
DCHECK(false) << "Invalid crypto configuration";
}
framer_.reset(new Framer(cast_environment->Clock(), framer_.reset(new Framer(cast_environment->Clock(),
incoming_payload_feedback_.get(), incoming_payload_feedback_.get(),
video_config.incoming_ssrc, video_config.incoming_ssrc,
...@@ -170,6 +185,26 @@ void VideoReceiver::DecodeVideoFrameThread( ...@@ -170,6 +185,26 @@ void VideoReceiver::DecodeVideoFrameThread(
} }
} }
bool VideoReceiver::DecryptVideoFrame(
scoped_ptr<EncodedVideoFrame>* video_frame) {
DCHECK(decryptor_) << "Invalid state";
if (!decryptor_->SetCounter(GetAesNonce((*video_frame)->frame_id,
iv_mask_))) {
NOTREACHED() << "Failed to set counter";
return false;
}
std::string decrypted_video_data;
if (!decryptor_->Decrypt((*video_frame)->data, &decrypted_video_data)) {
VLOG(0) << "Decryption error";
// Give up on this frame, release it from jitter buffer.
framer_->ReleaseFrame((*video_frame)->frame_id);
return false;
}
(*video_frame)->data.swap(decrypted_video_data);
return true;
}
// Called from the main cast thread. // Called from the main cast thread.
void VideoReceiver::GetEncodedVideoFrame( void VideoReceiver::GetEncodedVideoFrame(
const VideoFrameEncodedCallback& callback) { const VideoFrameEncodedCallback& callback) {
...@@ -184,6 +219,13 @@ void VideoReceiver::GetEncodedVideoFrame( ...@@ -184,6 +219,13 @@ void VideoReceiver::GetEncodedVideoFrame(
queued_encoded_callbacks_.push_back(callback); queued_encoded_callbacks_.push_back(callback);
return; return;
} }
if (decryptor_ && !DecryptVideoFrame(&encoded_frame)) {
// Logging already done.
queued_encoded_callbacks_.push_back(callback);
return;
}
base::TimeTicks render_time; base::TimeTicks render_time;
if (PullEncodedVideoFrame(rtp_timestamp, next_frame, &encoded_frame, if (PullEncodedVideoFrame(rtp_timestamp, next_frame, &encoded_frame,
&render_time)) { &render_time)) {
...@@ -266,6 +308,11 @@ void VideoReceiver::PlayoutTimeout() { ...@@ -266,6 +308,11 @@ void VideoReceiver::PlayoutTimeout() {
VLOG(1) << "PlayoutTimeout retrieved frame " VLOG(1) << "PlayoutTimeout retrieved frame "
<< static_cast<int>(encoded_frame->frame_id); << static_cast<int>(encoded_frame->frame_id);
if (decryptor_ && !DecryptVideoFrame(&encoded_frame)) {
// Logging already done.
return;
}
base::TimeTicks render_time; base::TimeTicks render_time;
if (PullEncodedVideoFrame(rtp_timestamp, next_frame, &encoded_frame, if (PullEncodedVideoFrame(rtp_timestamp, next_frame, &encoded_frame,
&render_time)) { &render_time)) {
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
'video_receiver.cc', 'video_receiver.cc',
], # source ], # source
'dependencies': [ 'dependencies': [
'<(DEPTH)/crypto/crypto.gyp:crypto',
'framer/framer.gyp:cast_framer', 'framer/framer.gyp:cast_framer',
'video_receiver/codecs/vp8/vp8_decoder.gyp:cast_vp8_decoder', 'video_receiver/codecs/vp8/vp8_decoder.gyp:cast_vp8_decoder',
'rtp_receiver/rtp_receiver.gyp:cast_rtp_receiver', 'rtp_receiver/rtp_receiver.gyp:cast_rtp_receiver',
......
...@@ -20,6 +20,10 @@ ...@@ -20,6 +20,10 @@
#include "media/cast/rtp_common/rtp_defines.h" #include "media/cast/rtp_common/rtp_defines.h"
#include "media/cast/rtp_receiver/rtp_receiver.h" #include "media/cast/rtp_receiver/rtp_receiver.h"
namespace crypto {
class Encryptor;
}
namespace media { namespace media {
namespace cast { namespace cast {
...@@ -72,6 +76,8 @@ class VideoReceiver : public base::NonThreadSafe, ...@@ -72,6 +76,8 @@ class VideoReceiver : public base::NonThreadSafe,
scoped_ptr<EncodedVideoFrame> encoded_frame, scoped_ptr<EncodedVideoFrame> encoded_frame,
const base::TimeTicks& render_time); const base::TimeTicks& render_time);
bool DecryptVideoFrame(scoped_ptr<EncodedVideoFrame>* video_frame);
bool PullEncodedVideoFrame(uint32 rtp_timestamp, bool PullEncodedVideoFrame(uint32 rtp_timestamp,
bool next_frame, bool next_frame,
scoped_ptr<EncodedVideoFrame>* encoded_frame, scoped_ptr<EncodedVideoFrame>* encoded_frame,
...@@ -109,9 +115,9 @@ class VideoReceiver : public base::NonThreadSafe, ...@@ -109,9 +115,9 @@ class VideoReceiver : public base::NonThreadSafe,
scoped_ptr<RtpReceiverStatistics> rtp_video_receiver_statistics_; scoped_ptr<RtpReceiverStatistics> rtp_video_receiver_statistics_;
base::TimeTicks time_last_sent_cast_message_; base::TimeTicks time_last_sent_cast_message_;
base::TimeDelta time_offset_; // Sender-receiver offset estimation. base::TimeDelta time_offset_; // Sender-receiver offset estimation.
scoped_ptr<crypto::Encryptor> decryptor_;
std::string iv_mask_;
std::list<VideoFrameEncodedCallback> queued_encoded_callbacks_; std::list<VideoFrameEncodedCallback> queued_encoded_callbacks_;
bool time_incoming_packet_updated_; bool time_incoming_packet_updated_;
base::TimeTicks time_incoming_packet_; base::TimeTicks time_incoming_packet_;
uint32 incoming_rtp_timestamp_; uint32 incoming_rtp_timestamp_;
......
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop.h"
#include "crypto/encryptor.h"
#include "crypto/symmetric_key.h"
#include "media/cast/cast_defines.h" #include "media/cast/cast_defines.h"
#include "media/cast/pacing/paced_sender.h" #include "media/cast/pacing/paced_sender.h"
#include "media/cast/video_sender/video_encoder.h" #include "media/cast/video_sender/video_encoder.h"
...@@ -87,6 +89,19 @@ VideoSender::VideoSender( ...@@ -87,6 +89,19 @@ VideoSender::VideoSender(
max_unacked_frames_); max_unacked_frames_);
video_encoder_controller_ = video_encoder_.get(); video_encoder_controller_ = video_encoder_.get();
} }
if (video_config.aes_iv_mask.size() == kAesKeySize &&
video_config.aes_key.size() == kAesKeySize) {
iv_mask_ = video_config.aes_iv_mask;
crypto::SymmetricKey* key = crypto::SymmetricKey::Import(
crypto::SymmetricKey::AES, video_config.aes_key);
encryptor_.reset(new crypto::Encryptor());
encryptor_->Init(key, crypto::Encryptor::CTR, std::string());
} else if (video_config.aes_iv_mask.size() != 0 ||
video_config.aes_key.size() != 0) {
DCHECK(false) << "Invalid crypto configuration";
}
rtcp_.reset(new Rtcp( rtcp_.reset(new Rtcp(
cast_environment_->Clock(), cast_environment_->Clock(),
rtcp_feedback_.get(), rtcp_feedback_.get(),
...@@ -143,11 +158,44 @@ void VideoSender::SendEncodedVideoFrameMainThread( ...@@ -143,11 +158,44 @@ void VideoSender::SendEncodedVideoFrameMainThread(
SendEncodedVideoFrame(video_frame.get(), capture_time); SendEncodedVideoFrame(video_frame.get(), capture_time);
} }
bool VideoSender::EncryptVideoFrame(const EncodedVideoFrame& video_frame,
EncodedVideoFrame* encrypted_frame) {
DCHECK(encryptor_) << "Invalid state";
if (!encryptor_->SetCounter(GetAesNonce(video_frame.frame_id, iv_mask_))) {
NOTREACHED() << "Failed to set counter";
return false;
}
if (!encryptor_->Encrypt(video_frame.data, &encrypted_frame->data)) {
NOTREACHED() << "Encrypt error";
return false;
}
encrypted_frame->codec = video_frame.codec;
encrypted_frame->key_frame = video_frame.key_frame;
encrypted_frame->frame_id = video_frame.frame_id;
encrypted_frame->last_referenced_frame_id =
video_frame.last_referenced_frame_id;
return true;
}
void VideoSender::SendEncodedVideoFrame(const EncodedVideoFrame* encoded_frame, void VideoSender::SendEncodedVideoFrame(const EncodedVideoFrame* encoded_frame,
const base::TimeTicks& capture_time) { const base::TimeTicks& capture_time) {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
last_send_time_ = cast_environment_->Clock()->NowTicks(); last_send_time_ = cast_environment_->Clock()->NowTicks();
rtp_sender_->IncomingEncodedVideoFrame(encoded_frame, capture_time);
if (encryptor_) {
EncodedVideoFrame encrypted_video_frame;
if (!EncryptVideoFrame(*encoded_frame, &encrypted_video_frame)) {
// Logging already done.
return;
}
rtp_sender_->IncomingEncodedVideoFrame(&encrypted_video_frame,
capture_time);
} else {
rtp_sender_->IncomingEncodedVideoFrame(encoded_frame, capture_time);
}
if (encoded_frame->key_frame) { if (encoded_frame->key_frame) {
VLOG(1) << "Send encoded key frame; frame_id:" VLOG(1) << "Send encoded key frame; frame_id:"
<< static_cast<int>(encoded_frame->frame_id); << static_cast<int>(encoded_frame->frame_id);
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
'video_sender.cc', 'video_sender.cc',
], # source ], # source
'dependencies': [ 'dependencies': [
'<(DEPTH)/crypto/crypto.gyp:crypto',
'<(DEPTH)/media/cast/rtcp/rtcp.gyp:*', '<(DEPTH)/media/cast/rtcp/rtcp.gyp:*',
'<(DEPTH)/media/cast/rtp_sender/rtp_sender.gyp:*', '<(DEPTH)/media/cast/rtp_sender/rtp_sender.gyp:*',
'congestion_control', 'congestion_control',
......
...@@ -18,6 +18,10 @@ ...@@ -18,6 +18,10 @@
#include "media/cast/rtcp/rtcp.h" #include "media/cast/rtcp/rtcp.h"
#include "media/cast/rtp_sender/rtp_sender.h" #include "media/cast/rtp_sender/rtp_sender.h"
namespace crypto {
class Encryptor;
}
namespace media { namespace media {
namespace cast { namespace cast {
...@@ -102,6 +106,11 @@ class VideoSender : public base::NonThreadSafe, ...@@ -102,6 +106,11 @@ class VideoSender : public base::NonThreadSafe,
void InitializeTimers(); void InitializeTimers();
// Caller must allocate the destination |encrypted_video_frame| the data
// member will be resized to hold the encrypted size.
bool EncryptVideoFrame(const EncodedVideoFrame& encoded_frame,
EncodedVideoFrame* encrypted_video_frame);
const base::TimeDelta rtp_max_delay_; const base::TimeDelta rtp_max_delay_;
const int max_frame_rate_; const int max_frame_rate_;
...@@ -113,6 +122,8 @@ class VideoSender : public base::NonThreadSafe, ...@@ -113,6 +122,8 @@ class VideoSender : public base::NonThreadSafe,
scoped_ptr<RtpSender> rtp_sender_; scoped_ptr<RtpSender> rtp_sender_;
VideoEncoderController* video_encoder_controller_; VideoEncoderController* video_encoder_controller_;
uint8 max_unacked_frames_; uint8 max_unacked_frames_;
scoped_ptr<crypto::Encryptor> encryptor_;
std::string iv_mask_;
int last_acked_frame_id_; int last_acked_frame_id_;
int last_sent_frame_id_; int last_sent_frame_id_;
int duplicate_ack_; int duplicate_ack_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment