Implemented WebM VP9 subsample encryption and decryption.
Change-Id: I98f02c05ec8bcc976e01ff41478e92b8809c0076
This commit is contained in:
parent
336ea5cb34
commit
db92c9a22b
|
@ -50,4 +50,5 @@ DEFINE_string(temp_dir,
|
||||||
"",
|
"",
|
||||||
"Specify a directory in which to store temporary (intermediate) "
|
"Specify a directory in which to store temporary (intermediate) "
|
||||||
" files. Used only if single_segment=true.");
|
" files. Used only if single_segment=true.");
|
||||||
|
DEFINE_bool(webm_subsample_encryption, true,
|
||||||
|
"Enable WebM subsample encryption.");
|
||||||
|
|
|
@ -20,5 +20,6 @@ DECLARE_double(fragment_duration);
|
||||||
DECLARE_bool(fragment_sap_aligned);
|
DECLARE_bool(fragment_sap_aligned);
|
||||||
DECLARE_int32(num_subsegments_per_sidx);
|
DECLARE_int32(num_subsegments_per_sidx);
|
||||||
DECLARE_string(temp_dir);
|
DECLARE_string(temp_dir);
|
||||||
|
DECLARE_bool(webm_subsample_encryption);
|
||||||
|
|
||||||
#endif // APP_MUXER_FLAGS_H_
|
#endif // APP_MUXER_FLAGS_H_
|
||||||
|
|
|
@ -154,7 +154,7 @@ bool GetMuxerOptions(MuxerOptions* muxer_options) {
|
||||||
muxer_options->segment_sap_aligned = FLAGS_segment_sap_aligned;
|
muxer_options->segment_sap_aligned = FLAGS_segment_sap_aligned;
|
||||||
muxer_options->fragment_sap_aligned = FLAGS_fragment_sap_aligned;
|
muxer_options->fragment_sap_aligned = FLAGS_fragment_sap_aligned;
|
||||||
muxer_options->num_subsegments_per_sidx = FLAGS_num_subsegments_per_sidx;
|
muxer_options->num_subsegments_per_sidx = FLAGS_num_subsegments_per_sidx;
|
||||||
|
muxer_options->webm_subsample_encryption = FLAGS_webm_subsample_encryption;
|
||||||
if (FLAGS_mp4_use_decoding_timestamp_in_timeline) {
|
if (FLAGS_mp4_use_decoding_timestamp_in_timeline) {
|
||||||
LOG(WARNING) << "Flag --mp4_use_decoding_timestamp_in_timeline is set. "
|
LOG(WARNING) << "Flag --mp4_use_decoding_timestamp_in_timeline is set. "
|
||||||
"Note that it is a temporary hack to workaround Chromium "
|
"Note that it is a temporary hack to workaround Chromium "
|
||||||
|
|
|
@ -195,6 +195,16 @@ class PackagerAppTest(unittest.TestCase):
|
||||||
self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4')
|
self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4')
|
||||||
self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4')
|
self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4')
|
||||||
|
|
||||||
|
def testPackageWithWebmSubsampleEncryption(self):
|
||||||
|
self.packager.Package(
|
||||||
|
self._GetStreams(['video'],
|
||||||
|
output_format='webm',
|
||||||
|
test_files=['bear-640x360-vp9-altref.webm']),
|
||||||
|
self._GetFlags(encryption=True))
|
||||||
|
self._DiffGold(self.output[0], 'bear-640x360-vp9-altref-enc-golden.webm')
|
||||||
|
self._VerifyDecryption(self.output[0],
|
||||||
|
'bear-640x360-vp9-altref-dec-golden.webm')
|
||||||
|
|
||||||
def testPackageAvcTsWithEncryption(self):
|
def testPackageAvcTsWithEncryption(self):
|
||||||
# Currently we only support live packaging for ts.
|
# Currently we only support live packaging for ts.
|
||||||
self.packager.Package(
|
self.packager.Package(
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -17,8 +17,8 @@ namespace shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::string AudioCodecToString(AudioCodec audio_codec) {
|
std::string AudioCodecToString(Codec codec) {
|
||||||
switch (audio_codec) {
|
switch (codec) {
|
||||||
case kCodecAAC:
|
case kCodecAAC:
|
||||||
return "AAC";
|
return "AAC";
|
||||||
case kCodecAC3:
|
case kCodecAC3:
|
||||||
|
@ -42,38 +42,22 @@ std::string AudioCodecToString(AudioCodec audio_codec) {
|
||||||
case kCodecVorbis:
|
case kCodecVorbis:
|
||||||
return "Vorbis";
|
return "Vorbis";
|
||||||
default:
|
default:
|
||||||
NOTIMPLEMENTED() << "Unknown Audio Codec: " << audio_codec;
|
NOTIMPLEMENTED() << "Unknown Audio Codec: " << codec;
|
||||||
return "UnknownAudioCodec";
|
return "UnknownCodec";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
AudioStreamInfo::AudioStreamInfo(int track_id,
|
AudioStreamInfo::AudioStreamInfo(
|
||||||
uint32_t time_scale,
|
int track_id, uint32_t time_scale, uint64_t duration, Codec codec,
|
||||||
uint64_t duration,
|
const std::string& codec_string, const uint8_t* codec_config,
|
||||||
AudioCodec codec,
|
size_t codec_config_size, uint8_t sample_bits, uint8_t num_channels,
|
||||||
const std::string& codec_string,
|
uint32_t sampling_frequency, uint64_t seek_preroll_ns,
|
||||||
const std::string& language,
|
uint64_t codec_delay_ns, uint32_t max_bitrate, uint32_t avg_bitrate,
|
||||||
uint8_t sample_bits,
|
const std::string& language, bool is_encrypted)
|
||||||
uint8_t num_channels,
|
: StreamInfo(kStreamAudio, track_id, time_scale, duration, codec,
|
||||||
uint32_t sampling_frequency,
|
codec_string, codec_config, codec_config_size, language,
|
||||||
uint64_t seek_preroll_ns,
|
|
||||||
uint64_t codec_delay_ns,
|
|
||||||
uint32_t max_bitrate,
|
|
||||||
uint32_t avg_bitrate,
|
|
||||||
const uint8_t* codec_config,
|
|
||||||
size_t codec_config_size,
|
|
||||||
bool is_encrypted)
|
|
||||||
: StreamInfo(kStreamAudio,
|
|
||||||
track_id,
|
|
||||||
time_scale,
|
|
||||||
duration,
|
|
||||||
codec_string,
|
|
||||||
language,
|
|
||||||
codec_config,
|
|
||||||
codec_config_size,
|
|
||||||
is_encrypted),
|
is_encrypted),
|
||||||
codec_(codec),
|
|
||||||
sample_bits_(sample_bits),
|
sample_bits_(sample_bits),
|
||||||
num_channels_(num_channels),
|
num_channels_(num_channels),
|
||||||
sampling_frequency_(sampling_frequency),
|
sampling_frequency_(sampling_frequency),
|
||||||
|
@ -85,10 +69,9 @@ AudioStreamInfo::AudioStreamInfo(int track_id,
|
||||||
AudioStreamInfo::~AudioStreamInfo() {}
|
AudioStreamInfo::~AudioStreamInfo() {}
|
||||||
|
|
||||||
bool AudioStreamInfo::IsValidConfig() const {
|
bool AudioStreamInfo::IsValidConfig() const {
|
||||||
return codec_ != kUnknownAudioCodec && num_channels_ != 0 &&
|
return codec() != kUnknownCodec && num_channels_ != 0 &&
|
||||||
num_channels_ <= limits::kMaxChannels && sample_bits_ > 0 &&
|
num_channels_ <= limits::kMaxChannels && sample_bits_ > 0 &&
|
||||||
sample_bits_ <= limits::kMaxBitsPerSample &&
|
sample_bits_ <= limits::kMaxBitsPerSample && sampling_frequency_ > 0 &&
|
||||||
sampling_frequency_ > 0 &&
|
|
||||||
sampling_frequency_ <= limits::kMaxSampleRate;
|
sampling_frequency_ <= limits::kMaxSampleRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +79,7 @@ std::string AudioStreamInfo::ToString() const {
|
||||||
std::string str = base::StringPrintf(
|
std::string str = base::StringPrintf(
|
||||||
"%s codec: %s\n sample_bits: %d\n num_channels: %d\n "
|
"%s codec: %s\n sample_bits: %d\n num_channels: %d\n "
|
||||||
"sampling_frequency: %d\n language: %s\n",
|
"sampling_frequency: %d\n language: %s\n",
|
||||||
StreamInfo::ToString().c_str(), AudioCodecToString(codec_).c_str(),
|
StreamInfo::ToString().c_str(), AudioCodecToString(codec()).c_str(),
|
||||||
sample_bits_, num_channels_, sampling_frequency_, language().c_str());
|
sample_bits_, num_channels_, sampling_frequency_, language().c_str());
|
||||||
if (seek_preroll_ns_ != 0) {
|
if (seek_preroll_ns_ != 0) {
|
||||||
base::StringAppendF(&str, " seek_preroll_ns: %" PRIu64 "\n",
|
base::StringAppendF(&str, " seek_preroll_ns: %" PRIu64 "\n",
|
||||||
|
@ -109,7 +92,7 @@ std::string AudioStreamInfo::ToString() const {
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string AudioStreamInfo::GetCodecString(AudioCodec codec,
|
std::string AudioStreamInfo::GetCodecString(Codec codec,
|
||||||
uint8_t audio_object_type) {
|
uint8_t audio_object_type) {
|
||||||
switch (codec) {
|
switch (codec) {
|
||||||
case kCodecVorbis:
|
case kCodecVorbis:
|
||||||
|
|
|
@ -14,42 +14,17 @@
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
enum AudioCodec {
|
|
||||||
kUnknownAudioCodec = 0,
|
|
||||||
kCodecAAC,
|
|
||||||
kCodecAC3,
|
|
||||||
kCodecDTSC,
|
|
||||||
kCodecDTSE,
|
|
||||||
kCodecDTSH,
|
|
||||||
kCodecDTSL,
|
|
||||||
kCodecDTSM,
|
|
||||||
kCodecDTSP,
|
|
||||||
kCodecEAC3,
|
|
||||||
kCodecOpus,
|
|
||||||
kCodecVorbis,
|
|
||||||
|
|
||||||
kNumAudioCodec
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Holds audio stream information.
|
/// Holds audio stream information.
|
||||||
class AudioStreamInfo : public StreamInfo {
|
class AudioStreamInfo : public StreamInfo {
|
||||||
public:
|
public:
|
||||||
/// Construct an initialized audio stream info object.
|
/// Construct an initialized audio stream info object.
|
||||||
AudioStreamInfo(int track_id,
|
AudioStreamInfo(int track_id, uint32_t time_scale, uint64_t duration,
|
||||||
uint32_t time_scale,
|
Codec codec, const std::string& codec_string,
|
||||||
uint64_t duration,
|
const uint8_t* codec_config, size_t codec_config_size,
|
||||||
AudioCodec codec,
|
uint8_t sample_bits, uint8_t num_channels,
|
||||||
const std::string& codec_string,
|
uint32_t sampling_frequency, uint64_t seek_preroll_ns,
|
||||||
const std::string& language,
|
uint64_t codec_delay_ns, uint32_t max_bitrate,
|
||||||
uint8_t sample_bits,
|
uint32_t avg_bitrate, const std::string& language,
|
||||||
uint8_t num_channels,
|
|
||||||
uint32_t sampling_frequency,
|
|
||||||
uint64_t seek_preroll_ns,
|
|
||||||
uint64_t codec_delay_ns,
|
|
||||||
uint32_t max_bitrate,
|
|
||||||
uint32_t avg_bitrate,
|
|
||||||
const uint8_t* codec_config,
|
|
||||||
size_t codec_config_size,
|
|
||||||
bool is_encrypted);
|
bool is_encrypted);
|
||||||
|
|
||||||
/// @name StreamInfo implementation overrides.
|
/// @name StreamInfo implementation overrides.
|
||||||
|
@ -58,7 +33,6 @@ class AudioStreamInfo : public StreamInfo {
|
||||||
std::string ToString() const override;
|
std::string ToString() const override;
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
AudioCodec codec() const { return codec_; }
|
|
||||||
uint8_t sample_bits() const { return sample_bits_; }
|
uint8_t sample_bits() const { return sample_bits_; }
|
||||||
uint8_t sample_bytes() const { return sample_bits_ / 8; }
|
uint8_t sample_bytes() const { return sample_bits_ / 8; }
|
||||||
uint8_t num_channels() const { return num_channels_; }
|
uint8_t num_channels() const { return num_channels_; }
|
||||||
|
@ -71,20 +45,17 @@ class AudioStreamInfo : public StreamInfo {
|
||||||
uint32_t max_bitrate() const { return max_bitrate_; }
|
uint32_t max_bitrate() const { return max_bitrate_; }
|
||||||
uint32_t avg_bitrate() const { return avg_bitrate_; }
|
uint32_t avg_bitrate() const { return avg_bitrate_; }
|
||||||
|
|
||||||
void set_codec(AudioCodec codec) { codec_ = codec; }
|
|
||||||
void set_sampling_frequency(const uint32_t sampling_frequency) {
|
void set_sampling_frequency(const uint32_t sampling_frequency) {
|
||||||
sampling_frequency_ = sampling_frequency;
|
sampling_frequency_ = sampling_frequency;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @param audio_object_type is only used by AAC Codec, ignored otherwise.
|
/// @param audio_object_type is only used by AAC Codec, ignored otherwise.
|
||||||
/// @return The codec string.
|
/// @return The codec string.
|
||||||
static std::string GetCodecString(AudioCodec codec,
|
static std::string GetCodecString(Codec codec, uint8_t audio_object_type);
|
||||||
uint8_t audio_object_type);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~AudioStreamInfo() override;
|
~AudioStreamInfo() override;
|
||||||
|
|
||||||
AudioCodec codec_;
|
|
||||||
uint8_t sample_bits_;
|
uint8_t sample_bits_;
|
||||||
uint8_t num_channels_;
|
uint8_t num_channels_;
|
||||||
uint32_t sampling_frequency_;
|
uint32_t sampling_frequency_;
|
||||||
|
|
|
@ -17,7 +17,8 @@ MuxerOptions::MuxerOptions()
|
||||||
fragment_sap_aligned(false),
|
fragment_sap_aligned(false),
|
||||||
num_subsegments_per_sidx(0),
|
num_subsegments_per_sidx(0),
|
||||||
mp4_use_decoding_timestamp_in_timeline(false),
|
mp4_use_decoding_timestamp_in_timeline(false),
|
||||||
bandwidth(0) {}
|
bandwidth(0),
|
||||||
|
webm_subsample_encryption(true) {}
|
||||||
MuxerOptions::~MuxerOptions() {}
|
MuxerOptions::~MuxerOptions() {}
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
|
|
|
@ -73,6 +73,9 @@ struct MuxerOptions {
|
||||||
/// User-specified bit rate for the media stream. If zero, the muxer will
|
/// User-specified bit rate for the media stream. If zero, the muxer will
|
||||||
/// attempt to estimate.
|
/// attempt to estimate.
|
||||||
uint32_t bandwidth;
|
uint32_t bandwidth;
|
||||||
|
|
||||||
|
// Enable/disable subsample encryption for WebM containers.
|
||||||
|
bool webm_subsample_encryption;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
|
|
|
@ -14,19 +14,16 @@
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
StreamInfo::StreamInfo(StreamType stream_type,
|
StreamInfo::StreamInfo(StreamType stream_type, int track_id,
|
||||||
int track_id,
|
uint32_t time_scale, uint64_t duration, Codec codec,
|
||||||
uint32_t time_scale,
|
|
||||||
uint64_t duration,
|
|
||||||
const std::string& codec_string,
|
const std::string& codec_string,
|
||||||
const std::string& language,
|
const uint8_t* codec_config, size_t codec_config_size,
|
||||||
const uint8_t* codec_config,
|
const std::string& language, bool is_encrypted)
|
||||||
size_t codec_config_size,
|
|
||||||
bool is_encrypted)
|
|
||||||
: stream_type_(stream_type),
|
: stream_type_(stream_type),
|
||||||
track_id_(track_id),
|
track_id_(track_id),
|
||||||
time_scale_(time_scale),
|
time_scale_(time_scale),
|
||||||
duration_(duration),
|
duration_(duration),
|
||||||
|
codec_(codec),
|
||||||
codec_string_(codec_string),
|
codec_string_(codec_string),
|
||||||
language_(language),
|
language_(language),
|
||||||
is_encrypted_(is_encrypted) {
|
is_encrypted_(is_encrypted) {
|
||||||
|
|
|
@ -22,18 +22,40 @@ enum StreamType {
|
||||||
kStreamText,
|
kStreamText,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum Codec {
|
||||||
|
kUnknownCodec = 0,
|
||||||
|
kCodecH264,
|
||||||
|
kCodecHEV1,
|
||||||
|
kCodecHVC1,
|
||||||
|
kCodecVC1,
|
||||||
|
kCodecMPEG2,
|
||||||
|
kCodecMPEG4,
|
||||||
|
kCodecTheora,
|
||||||
|
kCodecVP8,
|
||||||
|
kCodecVP9,
|
||||||
|
kCodecVP10,
|
||||||
|
kCodecAAC,
|
||||||
|
kCodecAC3,
|
||||||
|
kCodecDTSC,
|
||||||
|
kCodecDTSE,
|
||||||
|
kCodecDTSH,
|
||||||
|
kCodecDTSL,
|
||||||
|
kCodecDTSM,
|
||||||
|
kCodecDTSP,
|
||||||
|
kCodecEAC3,
|
||||||
|
kCodecOpus,
|
||||||
|
kCodecVorbis,
|
||||||
|
kCodecText,
|
||||||
|
kNumCodec
|
||||||
|
};
|
||||||
|
|
||||||
/// Abstract class holds stream information.
|
/// Abstract class holds stream information.
|
||||||
class StreamInfo : public base::RefCountedThreadSafe<StreamInfo> {
|
class StreamInfo : public base::RefCountedThreadSafe<StreamInfo> {
|
||||||
public:
|
public:
|
||||||
StreamInfo(StreamType stream_type,
|
StreamInfo(StreamType stream_type, int track_id, uint32_t time_scale,
|
||||||
int track_id,
|
uint64_t duration, Codec codec, const std::string& codec_string,
|
||||||
uint32_t time_scale,
|
const uint8_t* codec_config, size_t codec_config_size,
|
||||||
uint64_t duration,
|
const std::string& language, bool is_encrypted);
|
||||||
const std::string& codec_string,
|
|
||||||
const std::string& language,
|
|
||||||
const uint8_t* codec_config,
|
|
||||||
size_t codec_config_size,
|
|
||||||
bool is_encrypted);
|
|
||||||
|
|
||||||
/// @return true if this object has appropriate configuration values, false
|
/// @return true if this object has appropriate configuration values, false
|
||||||
/// otherwise.
|
/// otherwise.
|
||||||
|
@ -46,21 +68,18 @@ class StreamInfo : public base::RefCountedThreadSafe<StreamInfo> {
|
||||||
uint32_t track_id() const { return track_id_; }
|
uint32_t track_id() const { return track_id_; }
|
||||||
uint32_t time_scale() const { return time_scale_; }
|
uint32_t time_scale() const { return time_scale_; }
|
||||||
uint64_t duration() const { return duration_; }
|
uint64_t duration() const { return duration_; }
|
||||||
|
Codec codec() const { return codec_; }
|
||||||
const std::string& codec_string() const { return codec_string_; }
|
const std::string& codec_string() const { return codec_string_; }
|
||||||
|
const std::vector<uint8_t>& codec_config() const { return codec_config_; }
|
||||||
const std::string& language() const { return language_; }
|
const std::string& language() const { return language_; }
|
||||||
|
|
||||||
bool is_encrypted() const { return is_encrypted_; }
|
bool is_encrypted() const { return is_encrypted_; }
|
||||||
|
|
||||||
const std::vector<uint8_t>& codec_config() const { return codec_config_; }
|
|
||||||
|
|
||||||
void set_duration(int duration) { duration_ = duration; }
|
void set_duration(int duration) { duration_ = duration; }
|
||||||
|
void set_codec(Codec codec) { codec_ = codec; }
|
||||||
void set_codec_config(const std::vector<uint8_t>& data) { codec_config_ = data; }
|
void set_codec_config(const std::vector<uint8_t>& data) { codec_config_ = data; }
|
||||||
|
|
||||||
void set_codec_string(const std::string& codec_string) {
|
void set_codec_string(const std::string& codec_string) {
|
||||||
codec_string_ = codec_string;
|
codec_string_ = codec_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_language(const std::string& language) { language_ = language; }
|
void set_language(const std::string& language) { language_ = language; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -75,6 +94,7 @@ class StreamInfo : public base::RefCountedThreadSafe<StreamInfo> {
|
||||||
uint32_t time_scale_;
|
uint32_t time_scale_;
|
||||||
// Duration base on time_scale.
|
// Duration base on time_scale.
|
||||||
uint64_t duration_;
|
uint64_t duration_;
|
||||||
|
Codec codec_;
|
||||||
std::string codec_string_;
|
std::string codec_string_;
|
||||||
std::string language_;
|
std::string language_;
|
||||||
// Whether the stream is potentially encrypted.
|
// Whether the stream is potentially encrypted.
|
||||||
|
|
|
@ -9,23 +9,15 @@
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
TextStreamInfo::TextStreamInfo(int track_id,
|
TextStreamInfo::TextStreamInfo(int track_id, uint32_t time_scale,
|
||||||
uint32_t time_scale,
|
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
const std::string& codec_string,
|
const std::string& codec_string,
|
||||||
const std::string& language,
|
const std::string& codec_config, uint16_t width,
|
||||||
const std::string& codec_config,
|
uint16_t height, const std::string& language)
|
||||||
uint16_t width,
|
: StreamInfo(kStreamText, track_id, time_scale, duration, kCodecText,
|
||||||
uint16_t height)
|
|
||||||
: StreamInfo(kStreamText,
|
|
||||||
track_id,
|
|
||||||
time_scale,
|
|
||||||
duration,
|
|
||||||
codec_string,
|
codec_string,
|
||||||
language,
|
|
||||||
reinterpret_cast<const uint8_t*>(codec_config.data()),
|
reinterpret_cast<const uint8_t*>(codec_config.data()),
|
||||||
codec_config.size(),
|
codec_config.size(), language, false),
|
||||||
false),
|
|
||||||
width_(width),
|
width_(width),
|
||||||
height_(height) {}
|
height_(height) {}
|
||||||
|
|
||||||
|
|
|
@ -21,20 +21,16 @@ class TextStreamInfo : public StreamInfo {
|
||||||
/// @param time_scale is the time scale of this stream.
|
/// @param time_scale is the time scale of this stream.
|
||||||
/// @param duration is the duration of this stream.
|
/// @param duration is the duration of this stream.
|
||||||
/// @param codec_string is the codec.
|
/// @param codec_string is the codec.
|
||||||
/// @param language is the language of this stream. This may be empty.
|
|
||||||
/// @param codec_config is configuration for this text stream. This could be
|
/// @param codec_config is configuration for this text stream. This could be
|
||||||
/// the metadata that applies to all the samples of this stream. This
|
/// the metadata that applies to all the samples of this stream. This
|
||||||
/// may be empty.
|
/// may be empty.
|
||||||
/// @param width of the text. This may be 0.
|
/// @param width of the text. This may be 0.
|
||||||
/// @param height of the text. This may be 0.
|
/// @param height of the text. This may be 0.
|
||||||
TextStreamInfo(int track_id,
|
/// @param language is the language of this stream. This may be empty.
|
||||||
uint32_t time_scale,
|
TextStreamInfo(int track_id, uint32_t time_scale, uint64_t duration,
|
||||||
uint64_t duration,
|
|
||||||
const std::string& codec_string,
|
const std::string& codec_string,
|
||||||
const std::string& language,
|
const std::string& codec_config, uint16_t width,
|
||||||
const std::string& codec_config,
|
uint16_t height, const std::string& language);
|
||||||
uint16_t width,
|
|
||||||
uint16_t height);
|
|
||||||
|
|
||||||
bool IsValidConfig() const override;
|
bool IsValidConfig() const override;
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,8 @@ namespace shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::string VideoCodecToString(VideoCodec video_codec) {
|
std::string VideoCodecToString(Codec codec) {
|
||||||
switch (video_codec) {
|
switch (codec) {
|
||||||
case kCodecH264:
|
case kCodecH264:
|
||||||
return "H264";
|
return "H264";
|
||||||
case kCodecHEV1:
|
case kCodecHEV1:
|
||||||
|
@ -40,52 +40,35 @@ std::string VideoCodecToString(VideoCodec video_codec) {
|
||||||
case kCodecVP10:
|
case kCodecVP10:
|
||||||
return "VP10";
|
return "VP10";
|
||||||
default:
|
default:
|
||||||
NOTIMPLEMENTED() << "Unknown Video Codec: " << video_codec;
|
NOTIMPLEMENTED() << "Unknown Video Codec: " << codec;
|
||||||
return "UnknownVideoCodec";
|
return "UnknownCodec";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
VideoStreamInfo::VideoStreamInfo(int track_id,
|
VideoStreamInfo::VideoStreamInfo(
|
||||||
uint32_t time_scale,
|
int track_id, uint32_t time_scale, uint64_t duration, Codec codec,
|
||||||
uint64_t duration,
|
const std::string& codec_string, const uint8_t* codec_config,
|
||||||
VideoCodec codec,
|
size_t codec_config_size, uint16_t width, uint16_t height,
|
||||||
const std::string& codec_string,
|
uint32_t pixel_width, uint32_t pixel_height, int16_t trick_play_rate,
|
||||||
const std::string& language,
|
uint8_t nalu_length_size, const std::string& language, bool is_encrypted)
|
||||||
uint16_t width,
|
: StreamInfo(kStreamVideo, track_id, time_scale, duration, codec,
|
||||||
uint16_t height,
|
codec_string, codec_config, codec_config_size, language,
|
||||||
uint32_t pixel_width,
|
|
||||||
uint32_t pixel_height,
|
|
||||||
int16_t trick_play_rate,
|
|
||||||
uint8_t nalu_length_size,
|
|
||||||
const uint8_t* codec_config,
|
|
||||||
size_t codec_config_size,
|
|
||||||
bool is_encrypted)
|
|
||||||
: StreamInfo(kStreamVideo,
|
|
||||||
track_id,
|
|
||||||
time_scale,
|
|
||||||
duration,
|
|
||||||
codec_string,
|
|
||||||
language,
|
|
||||||
codec_config,
|
|
||||||
codec_config_size,
|
|
||||||
is_encrypted),
|
is_encrypted),
|
||||||
codec_(codec),
|
|
||||||
width_(width),
|
width_(width),
|
||||||
height_(height),
|
height_(height),
|
||||||
pixel_width_(pixel_width),
|
pixel_width_(pixel_width),
|
||||||
pixel_height_(pixel_height),
|
pixel_height_(pixel_height),
|
||||||
trick_play_rate_(trick_play_rate),
|
trick_play_rate_(trick_play_rate),
|
||||||
nalu_length_size_(nalu_length_size) {
|
nalu_length_size_(nalu_length_size) {}
|
||||||
}
|
|
||||||
|
|
||||||
VideoStreamInfo::~VideoStreamInfo() {}
|
VideoStreamInfo::~VideoStreamInfo() {}
|
||||||
|
|
||||||
bool VideoStreamInfo::IsValidConfig() const {
|
bool VideoStreamInfo::IsValidConfig() const {
|
||||||
return codec_ != kUnknownVideoCodec &&
|
return codec() != kUnknownCodec && width_ > 0 &&
|
||||||
width_ > 0 && width_ <= limits::kMaxDimension &&
|
width_ <= limits::kMaxDimension && height_ > 0 &&
|
||||||
height_ > 0 && height_ <= limits::kMaxDimension &&
|
height_ <= limits::kMaxDimension &&
|
||||||
(nalu_length_size_ <= 2 || nalu_length_size_ == 4);
|
(nalu_length_size_ <= 2 || nalu_length_size_ == 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +76,7 @@ std::string VideoStreamInfo::ToString() const {
|
||||||
return base::StringPrintf(
|
return base::StringPrintf(
|
||||||
"%s codec: %s\n width: %d\n height: %d\n pixel_aspect_ratio: %d:%d\n "
|
"%s codec: %s\n width: %d\n height: %d\n pixel_aspect_ratio: %d:%d\n "
|
||||||
"trick_play_rate: %d\n nalu_length_size: %d\n",
|
"trick_play_rate: %d\n nalu_length_size: %d\n",
|
||||||
StreamInfo::ToString().c_str(), VideoCodecToString(codec_).c_str(),
|
StreamInfo::ToString().c_str(), VideoCodecToString(codec()).c_str(),
|
||||||
width_, height_, pixel_width_, pixel_height_, trick_play_rate_,
|
width_, height_, pixel_width_, pixel_height_, trick_play_rate_,
|
||||||
nalu_length_size_);
|
nalu_length_size_);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,41 +12,18 @@
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
enum VideoCodec {
|
|
||||||
kUnknownVideoCodec = 0,
|
|
||||||
kCodecH264,
|
|
||||||
kCodecHEV1,
|
|
||||||
kCodecHVC1,
|
|
||||||
kCodecVC1,
|
|
||||||
kCodecMPEG2,
|
|
||||||
kCodecMPEG4,
|
|
||||||
kCodecTheora,
|
|
||||||
kCodecVP8,
|
|
||||||
kCodecVP9,
|
|
||||||
kCodecVP10,
|
|
||||||
kNumVideoCodec
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Holds video stream information.
|
/// Holds video stream information.
|
||||||
class VideoStreamInfo : public StreamInfo {
|
class VideoStreamInfo : public StreamInfo {
|
||||||
public:
|
public:
|
||||||
/// Construct an initialized video stream info object.
|
/// Construct an initialized video stream info object.
|
||||||
/// @param pixel_width is the width of the pixel. 0 if unknown.
|
/// @param pixel_width is the width of the pixel. 0 if unknown.
|
||||||
/// @param pixel_height is the height of the pixels. 0 if unknown.
|
/// @param pixel_height is the height of the pixels. 0 if unknown.
|
||||||
VideoStreamInfo(int track_id,
|
VideoStreamInfo(int track_id, uint32_t time_scale, uint64_t duration,
|
||||||
uint32_t time_scale,
|
Codec codec, const std::string& codec_string,
|
||||||
uint64_t duration,
|
const uint8_t* codec_config, size_t codec_config_size,
|
||||||
VideoCodec codec,
|
uint16_t width, uint16_t height, uint32_t pixel_width,
|
||||||
const std::string& codec_string,
|
uint32_t pixel_height, int16_t trick_play_rate,
|
||||||
const std::string& language,
|
uint8_t nalu_length_size, const std::string& language,
|
||||||
uint16_t width,
|
|
||||||
uint16_t height,
|
|
||||||
uint32_t pixel_width,
|
|
||||||
uint32_t pixel_height,
|
|
||||||
int16_t trick_play_rate,
|
|
||||||
uint8_t nalu_length_size,
|
|
||||||
const uint8_t* codec_config,
|
|
||||||
size_t codec_config_size,
|
|
||||||
bool is_encrypted);
|
bool is_encrypted);
|
||||||
|
|
||||||
/// @name StreamInfo implementation overrides.
|
/// @name StreamInfo implementation overrides.
|
||||||
|
@ -55,7 +32,6 @@ class VideoStreamInfo : public StreamInfo {
|
||||||
std::string ToString() const override;
|
std::string ToString() const override;
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
VideoCodec codec() const { return codec_; }
|
|
||||||
uint16_t width() const { return width_; }
|
uint16_t width() const { return width_; }
|
||||||
uint16_t height() const { return height_; }
|
uint16_t height() const { return height_; }
|
||||||
/// Returns the pixel width.
|
/// Returns the pixel width.
|
||||||
|
@ -67,7 +43,6 @@ class VideoStreamInfo : public StreamInfo {
|
||||||
uint8_t nalu_length_size() const { return nalu_length_size_; }
|
uint8_t nalu_length_size() const { return nalu_length_size_; }
|
||||||
int16_t trick_play_rate() const { return trick_play_rate_; }
|
int16_t trick_play_rate() const { return trick_play_rate_; }
|
||||||
|
|
||||||
void set_codec(VideoCodec codec) { codec_ = codec; }
|
|
||||||
void set_width(uint32_t width) { width_ = width; }
|
void set_width(uint32_t width) { width_ = width; }
|
||||||
void set_height(uint32_t height) { height_ = height; }
|
void set_height(uint32_t height) { height_ = height; }
|
||||||
void set_pixel_width(uint32_t pixel_width) { pixel_width_ = pixel_width; }
|
void set_pixel_width(uint32_t pixel_width) { pixel_width_ = pixel_width; }
|
||||||
|
@ -76,7 +51,6 @@ class VideoStreamInfo : public StreamInfo {
|
||||||
private:
|
private:
|
||||||
~VideoStreamInfo() override;
|
~VideoStreamInfo() override;
|
||||||
|
|
||||||
VideoCodec codec_;
|
|
||||||
uint16_t width_;
|
uint16_t width_;
|
||||||
uint16_t height_;
|
uint16_t height_;
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ std::string ReverseBitsAndHexEncode(uint32_t x) {
|
||||||
return TrimLeadingZeros(base::HexEncode(bytes, arraysize(bytes)));
|
return TrimLeadingZeros(base::HexEncode(bytes, arraysize(bytes)));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CodecAsString(VideoCodec codec) {
|
std::string CodecAsString(Codec codec) {
|
||||||
switch (codec) {
|
switch (codec) {
|
||||||
case kCodecHEV1:
|
case kCodecHEV1:
|
||||||
return "hev1";
|
return "hev1";
|
||||||
|
@ -132,8 +132,7 @@ bool HEVCDecoderConfigurationRecord::ParseInternal() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string HEVCDecoderConfigurationRecord::GetCodecString(
|
std::string HEVCDecoderConfigurationRecord::GetCodecString(Codec codec) const {
|
||||||
VideoCodec codec) const {
|
|
||||||
// ISO/IEC 14496-15:2014 Annex E.
|
// ISO/IEC 14496-15:2014 Annex E.
|
||||||
std::vector<std::string> fields;
|
std::vector<std::string> fields;
|
||||||
fields.push_back(CodecAsString(codec));
|
fields.push_back(CodecAsString(codec));
|
||||||
|
|
|
@ -25,7 +25,7 @@ class HEVCDecoderConfigurationRecord : public DecoderConfigurationRecord {
|
||||||
~HEVCDecoderConfigurationRecord() override;
|
~HEVCDecoderConfigurationRecord() override;
|
||||||
|
|
||||||
/// @return The codec string.
|
/// @return The codec string.
|
||||||
std::string GetCodecString(VideoCodec codec) const;
|
std::string GetCodecString(Codec codec) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool ParseInternal() override;
|
bool ParseInternal() override;
|
||||||
|
|
|
@ -24,7 +24,7 @@ enum VP9CodecFeatures {
|
||||||
kFeatureChromaSubsampling = 4,
|
kFeatureChromaSubsampling = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string VPCodecAsString(VideoCodec codec) {
|
std::string VPCodecAsString(Codec codec) {
|
||||||
switch (codec) {
|
switch (codec) {
|
||||||
case kCodecVP8:
|
case kCodecVP8:
|
||||||
return "vp08";
|
return "vp08";
|
||||||
|
@ -205,7 +205,7 @@ void VPCodecConfigurationRecord::WriteWebM(std::vector<uint8_t>* data) const {
|
||||||
writer.SwapBuffer(data);
|
writer.SwapBuffer(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string VPCodecConfigurationRecord::GetCodecString(VideoCodec codec) const {
|
std::string VPCodecConfigurationRecord::GetCodecString(Codec codec) const {
|
||||||
const std::string fields[] = {
|
const std::string fields[] = {
|
||||||
base::IntToString(profile_),
|
base::IntToString(profile_),
|
||||||
base::IntToString(level_),
|
base::IntToString(level_),
|
||||||
|
|
|
@ -68,7 +68,7 @@ class VPCodecConfigurationRecord {
|
||||||
void WriteWebM(std::vector<uint8_t>* data) const;
|
void WriteWebM(std::vector<uint8_t>* data) const;
|
||||||
|
|
||||||
/// @return The codec string.
|
/// @return The codec string.
|
||||||
std::string GetCodecString(VideoCodec codec) const;
|
std::string GetCodecString(Codec codec) const;
|
||||||
|
|
||||||
// Merges the values from the given configuration. If there are values in
|
// Merges the values from the given configuration. If there are values in
|
||||||
// both |*this| and |other|, the values in |other| take precedence.
|
// both |*this| and |other|, the values in |other| take precedence.
|
||||||
|
|
|
@ -16,29 +16,19 @@ VideoStreamInfoParameters::~VideoStreamInfoParameters() {}
|
||||||
|
|
||||||
scoped_refptr<StreamInfo> CreateVideoStreamInfo(
|
scoped_refptr<StreamInfo> CreateVideoStreamInfo(
|
||||||
const VideoStreamInfoParameters& param) {
|
const VideoStreamInfoParameters& param) {
|
||||||
return scoped_refptr<StreamInfo>(
|
return scoped_refptr<StreamInfo>(new VideoStreamInfo(
|
||||||
new VideoStreamInfo(param.track_id,
|
param.track_id, param.time_scale, param.duration, param.codec,
|
||||||
param.time_scale,
|
param.codec_string, param.codec_config.data(), param.codec_config.size(),
|
||||||
param.duration,
|
param.width, param.height, param.pixel_width, param.pixel_height,
|
||||||
param.codec,
|
|
||||||
param.codec_string,
|
|
||||||
param.language,
|
|
||||||
param.width,
|
|
||||||
param.height,
|
|
||||||
param.pixel_width,
|
|
||||||
param.pixel_height,
|
|
||||||
0, // trick_play_rate
|
0, // trick_play_rate
|
||||||
param.nalu_length_size,
|
param.nalu_length_size, param.language, param.is_encrypted));
|
||||||
param.codec_config.data(),
|
|
||||||
param.codec_config.size(),
|
|
||||||
param.is_encrypted));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoStreamInfoParameters GetDefaultVideoStreamInfoParams() {
|
VideoStreamInfoParameters GetDefaultVideoStreamInfoParams() {
|
||||||
const int kTrackId = 0;
|
const int kTrackId = 0;
|
||||||
const uint32_t kTimeScale = 10;
|
const uint32_t kTimeScale = 10;
|
||||||
const uint64_t kVideoStreamDuration = 200;
|
const uint64_t kVideoStreamDuration = 200;
|
||||||
const VideoCodec kH264Codec = kCodecH264;
|
const Codec kH264Codec = kCodecH264;
|
||||||
const char* kCodecString = "avc1.010101";
|
const char* kCodecString = "avc1.010101";
|
||||||
const char* kLanuageUndefined = "und";
|
const char* kLanuageUndefined = "und";
|
||||||
const uint16_t kWidth = 720;
|
const uint16_t kWidth = 720;
|
||||||
|
|
|
@ -60,7 +60,7 @@ struct VideoStreamInfoParameters {
|
||||||
int track_id;
|
int track_id;
|
||||||
uint32_t time_scale;
|
uint32_t time_scale;
|
||||||
uint64_t duration;
|
uint64_t duration;
|
||||||
VideoCodec codec;
|
Codec codec;
|
||||||
std::string codec_string;
|
std::string codec_string;
|
||||||
std::string language;
|
std::string language;
|
||||||
uint16_t width;
|
uint16_t width;
|
||||||
|
|
|
@ -221,25 +221,13 @@ bool EsParserAdts::UpdateAudioConfiguration(const uint8_t* adts_frame,
|
||||||
? std::min(2 * samples_per_second, 48000)
|
? std::min(2 * samples_per_second, 48000)
|
||||||
: samples_per_second;
|
: samples_per_second;
|
||||||
|
|
||||||
last_audio_decoder_config_ = scoped_refptr<StreamInfo>(
|
last_audio_decoder_config_ = scoped_refptr<StreamInfo>(new AudioStreamInfo(
|
||||||
new AudioStreamInfo(
|
pid(), kMpeg2Timescale, kInfiniteDuration, kCodecAAC,
|
||||||
pid(),
|
AudioStreamInfo::GetCodecString(kCodecAAC, adts_header.GetObjectType()),
|
||||||
kMpeg2Timescale,
|
audio_specific_config.data(), audio_specific_config.size(),
|
||||||
kInfiniteDuration,
|
kAacSampleSizeBits, adts_header.GetNumChannels(),
|
||||||
kCodecAAC,
|
extended_samples_per_second, 0 /* seek preroll */, 0 /* codec delay */,
|
||||||
AudioStreamInfo::GetCodecString(kCodecAAC,
|
0 /* max bitrate */, 0 /* avg bitrate */, std::string(), false));
|
||||||
adts_header.GetObjectType()),
|
|
||||||
std::string(),
|
|
||||||
kAacSampleSizeBits,
|
|
||||||
adts_header.GetNumChannels(),
|
|
||||||
extended_samples_per_second,
|
|
||||||
0 /* seek preroll */,
|
|
||||||
0 /* codec delay */,
|
|
||||||
0 /* max bitrate */,
|
|
||||||
0 /* avg bitrate */,
|
|
||||||
audio_specific_config.data(),
|
|
||||||
audio_specific_config.size(),
|
|
||||||
false));
|
|
||||||
|
|
||||||
DVLOG(1) << "Sampling frequency: " << samples_per_second;
|
DVLOG(1) << "Sampling frequency: " << samples_per_second;
|
||||||
DVLOG(1) << "Extended sampling frequency: " << extended_samples_per_second;
|
DVLOG(1) << "Extended sampling frequency: " << extended_samples_per_second;
|
||||||
|
|
|
@ -148,9 +148,10 @@ bool EsParserH264::UpdateVideoDecoderConfig(int pps_id) {
|
||||||
AVCDecoderConfigurationRecord::GetCodecString(decoder_config_record[1],
|
AVCDecoderConfigurationRecord::GetCodecString(decoder_config_record[1],
|
||||||
decoder_config_record[2],
|
decoder_config_record[2],
|
||||||
decoder_config_record[3]),
|
decoder_config_record[3]),
|
||||||
std::string(), coded_width, coded_height, pixel_width, pixel_height, 0,
|
decoder_config_record.data(), decoder_config_record.size(), coded_width,
|
||||||
H264ByteToUnitStreamConverter::kUnitStreamNaluLengthSize,
|
coded_height, pixel_width, pixel_height, 0,
|
||||||
decoder_config_record.data(), decoder_config_record.size(), false));
|
H264ByteToUnitStreamConverter::kUnitStreamNaluLengthSize, std::string(),
|
||||||
|
false));
|
||||||
DVLOG(1) << "Profile IDC: " << sps->profile_idc;
|
DVLOG(1) << "Profile IDC: " << sps->profile_idc;
|
||||||
DVLOG(1) << "Level IDC: " << sps->level_idc;
|
DVLOG(1) << "Level IDC: " << sps->level_idc;
|
||||||
DVLOG(1) << "log2_max_frame_num_minus4: " << sps->log2_max_frame_num_minus4;
|
DVLOG(1) << "log2_max_frame_num_minus4: " << sps->log2_max_frame_num_minus4;
|
||||||
|
|
|
@ -148,23 +148,12 @@ bool EsParserH265::UpdateVideoDecoderConfig(int pps_id) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
last_video_decoder_config_ = scoped_refptr<StreamInfo>(
|
last_video_decoder_config_ = scoped_refptr<StreamInfo>(new VideoStreamInfo(
|
||||||
new VideoStreamInfo(
|
pid(), kMpeg2Timescale, kInfiniteDuration, kCodecHVC1,
|
||||||
pid(),
|
decoder_config.GetCodecString(kCodecHVC1), decoder_config_record.data(),
|
||||||
kMpeg2Timescale,
|
decoder_config_record.size(), coded_width, coded_height, pixel_width,
|
||||||
kInfiniteDuration,
|
pixel_height, 0, H26xByteToUnitStreamConverter::kUnitStreamNaluLengthSize,
|
||||||
kCodecHVC1,
|
std::string(), false));
|
||||||
decoder_config.GetCodecString(kCodecHVC1),
|
|
||||||
std::string(),
|
|
||||||
coded_width,
|
|
||||||
coded_height,
|
|
||||||
pixel_width,
|
|
||||||
pixel_height,
|
|
||||||
0,
|
|
||||||
H26xByteToUnitStreamConverter::kUnitStreamNaluLengthSize,
|
|
||||||
decoder_config_record.data(),
|
|
||||||
decoder_config_record.size(),
|
|
||||||
false));
|
|
||||||
|
|
||||||
// Video config notification.
|
// Video config notification.
|
||||||
new_stream_info_cb_.Run(last_video_decoder_config_);
|
new_stream_info_cb_.Run(last_video_decoder_config_);
|
||||||
|
|
|
@ -97,7 +97,7 @@ bool PesPacketGenerator::Initialize(const StreamInfo& stream_info) {
|
||||||
if (stream_type_ == kStreamVideo) {
|
if (stream_type_ == kStreamVideo) {
|
||||||
const VideoStreamInfo& video_stream_info =
|
const VideoStreamInfo& video_stream_info =
|
||||||
static_cast<const VideoStreamInfo&>(stream_info);
|
static_cast<const VideoStreamInfo&>(stream_info);
|
||||||
if (video_stream_info.codec() != VideoCodec::kCodecH264) {
|
if (video_stream_info.codec() != Codec::kCodecH264) {
|
||||||
NOTIMPLEMENTED() << "Video codec " << video_stream_info.codec()
|
NOTIMPLEMENTED() << "Video codec " << video_stream_info.codec()
|
||||||
<< " is not supported.";
|
<< " is not supported.";
|
||||||
return false;
|
return false;
|
||||||
|
@ -110,7 +110,7 @@ bool PesPacketGenerator::Initialize(const StreamInfo& stream_info) {
|
||||||
} else if (stream_type_ == kStreamAudio) {
|
} else if (stream_type_ == kStreamAudio) {
|
||||||
const AudioStreamInfo& audio_stream_info =
|
const AudioStreamInfo& audio_stream_info =
|
||||||
static_cast<const AudioStreamInfo&>(stream_info);
|
static_cast<const AudioStreamInfo&>(stream_info);
|
||||||
if (audio_stream_info.codec() != AudioCodec::kCodecAAC) {
|
if (audio_stream_info.codec() != Codec::kCodecAAC) {
|
||||||
NOTIMPLEMENTED() << "Audio codec " << audio_stream_info.codec()
|
NOTIMPLEMENTED() << "Audio codec " << audio_stream_info.codec()
|
||||||
<< " is not supported yet.";
|
<< " is not supported yet.";
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -34,10 +34,10 @@ const uint8_t kAnyData[] = {
|
||||||
|
|
||||||
const bool kIsKeyFrame = true;
|
const bool kIsKeyFrame = true;
|
||||||
|
|
||||||
// Only {Audio,Video}Codec and extra data matter for this test. Other values are
|
// Only Codec and extra data matter for this test. Other values are
|
||||||
// bogus.
|
// bogus.
|
||||||
const VideoCodec kH264VideoCodec = VideoCodec::kCodecH264;
|
const Codec kH264Codec = Codec::kCodecH264;
|
||||||
const AudioCodec kAacAudioCodec = AudioCodec::kCodecAAC;
|
const Codec kAacCodec = Codec::kCodecAAC;
|
||||||
|
|
||||||
// TODO(rkuroiwa): It might make sense to inject factory functions to create
|
// TODO(rkuroiwa): It might make sense to inject factory functions to create
|
||||||
// NalUnitToByteStreamConverter and AACAudioSpecificConfig so that these
|
// NalUnitToByteStreamConverter and AACAudioSpecificConfig so that these
|
||||||
|
@ -102,20 +102,19 @@ class MockAACAudioSpecificConfig : public AACAudioSpecificConfig {
|
||||||
MOCK_CONST_METHOD1(ConvertToADTS, bool(std::vector<uint8_t>* buffer));
|
MOCK_CONST_METHOD1(ConvertToADTS, bool(std::vector<uint8_t>* buffer));
|
||||||
};
|
};
|
||||||
|
|
||||||
scoped_refptr<VideoStreamInfo> CreateVideoStreamInfo(VideoCodec codec) {
|
scoped_refptr<VideoStreamInfo> CreateVideoStreamInfo(Codec codec) {
|
||||||
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||||
kTrackId, kTimeScale, kDuration, codec, kCodecString, kLanguage,
|
kTrackId, kTimeScale, kDuration, codec, kCodecString, kVideoExtraData,
|
||||||
kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
arraysize(kVideoExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||||
kNaluLengthSize, kVideoExtraData, arraysize(kVideoExtraData),
|
kTrickPlayRate, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||||
kIsEncrypted));
|
|
||||||
return stream_info;
|
return stream_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
scoped_refptr<AudioStreamInfo> CreateAudioStreamInfo(AudioCodec codec) {
|
scoped_refptr<AudioStreamInfo> CreateAudioStreamInfo(Codec codec) {
|
||||||
scoped_refptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
|
scoped_refptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
|
||||||
kTrackId, kTimeScale, kDuration, codec, kCodecString, kLanguage,
|
kTrackId, kTimeScale, kDuration, codec, kCodecString, kAudioExtraData,
|
||||||
kSampleBits, kNumChannels, kSamplingFrequency, kSeekPreroll, kCodecDelay,
|
arraysize(kAudioExtraData), kSampleBits, kNumChannels, kSamplingFrequency,
|
||||||
kMaxBitrate, kAverageBitrate, kAudioExtraData, arraysize(kAudioExtraData),
|
kSeekPreroll, kCodecDelay, kMaxBitrate, kAverageBitrate, kLanguage,
|
||||||
kIsEncrypted));
|
kIsEncrypted));
|
||||||
return stream_info;
|
return stream_info;
|
||||||
}
|
}
|
||||||
|
@ -140,7 +139,7 @@ class PesPacketGeneratorTest : public ::testing::Test {
|
||||||
const uint8_t* expected_output,
|
const uint8_t* expected_output,
|
||||||
size_t expected_output_size) {
|
size_t expected_output_size) {
|
||||||
scoped_refptr<VideoStreamInfo> stream_info(
|
scoped_refptr<VideoStreamInfo> stream_info(
|
||||||
CreateVideoStreamInfo(kH264VideoCodec));
|
CreateVideoStreamInfo(kH264Codec));
|
||||||
EXPECT_TRUE(generator_.Initialize(*stream_info));
|
EXPECT_TRUE(generator_.Initialize(*stream_info));
|
||||||
EXPECT_EQ(0u, generator_.NumberOfReadyPesPackets());
|
EXPECT_EQ(0u, generator_.NumberOfReadyPesPackets());
|
||||||
|
|
||||||
|
@ -189,7 +188,7 @@ class PesPacketGeneratorTest : public ::testing::Test {
|
||||||
const uint8_t* expected_output,
|
const uint8_t* expected_output,
|
||||||
size_t expected_output_size) {
|
size_t expected_output_size) {
|
||||||
scoped_refptr<AudioStreamInfo> stream_info(
|
scoped_refptr<AudioStreamInfo> stream_info(
|
||||||
CreateAudioStreamInfo(kAacAudioCodec));
|
CreateAudioStreamInfo(kAacCodec));
|
||||||
EXPECT_TRUE(generator_.Initialize(*stream_info));
|
EXPECT_TRUE(generator_.Initialize(*stream_info));
|
||||||
EXPECT_EQ(0u, generator_.NumberOfReadyPesPackets());
|
EXPECT_EQ(0u, generator_.NumberOfReadyPesPackets());
|
||||||
|
|
||||||
|
@ -224,26 +223,24 @@ class PesPacketGeneratorTest : public ::testing::Test {
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(PesPacketGeneratorTest, InitializeVideo) {
|
TEST_F(PesPacketGeneratorTest, InitializeVideo) {
|
||||||
scoped_refptr<VideoStreamInfo> stream_info(
|
scoped_refptr<VideoStreamInfo> stream_info(CreateVideoStreamInfo(kH264Codec));
|
||||||
CreateVideoStreamInfo(kH264VideoCodec));
|
|
||||||
EXPECT_TRUE(generator_.Initialize(*stream_info));
|
EXPECT_TRUE(generator_.Initialize(*stream_info));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(PesPacketGeneratorTest, InitializeVideoNonH264) {
|
TEST_F(PesPacketGeneratorTest, InitializeVideoNonH264) {
|
||||||
scoped_refptr<VideoStreamInfo> stream_info(
|
scoped_refptr<VideoStreamInfo> stream_info(
|
||||||
CreateVideoStreamInfo(VideoCodec::kCodecVP9));
|
CreateVideoStreamInfo(Codec::kCodecVP9));
|
||||||
EXPECT_FALSE(generator_.Initialize(*stream_info));
|
EXPECT_FALSE(generator_.Initialize(*stream_info));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(PesPacketGeneratorTest, InitializeAudio) {
|
TEST_F(PesPacketGeneratorTest, InitializeAudio) {
|
||||||
scoped_refptr<AudioStreamInfo> stream_info(
|
scoped_refptr<AudioStreamInfo> stream_info(CreateAudioStreamInfo(kAacCodec));
|
||||||
CreateAudioStreamInfo(kAacAudioCodec));
|
|
||||||
EXPECT_TRUE(generator_.Initialize(*stream_info));
|
EXPECT_TRUE(generator_.Initialize(*stream_info));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(PesPacketGeneratorTest, InitializeAudioNonAac) {
|
TEST_F(PesPacketGeneratorTest, InitializeAudioNonAac) {
|
||||||
scoped_refptr<AudioStreamInfo> stream_info(
|
scoped_refptr<AudioStreamInfo> stream_info(
|
||||||
CreateAudioStreamInfo(AudioCodec::kCodecOpus));
|
CreateAudioStreamInfo(Codec::kCodecOpus));
|
||||||
EXPECT_FALSE(generator_.Initialize(*stream_info));
|
EXPECT_FALSE(generator_.Initialize(*stream_info));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,13 +248,12 @@ TEST_F(PesPacketGeneratorTest, InitializeAudioNonAac) {
|
||||||
TEST_F(PesPacketGeneratorTest, InitializeTextInfo) {
|
TEST_F(PesPacketGeneratorTest, InitializeTextInfo) {
|
||||||
scoped_refptr<TextStreamInfo> stream_info(
|
scoped_refptr<TextStreamInfo> stream_info(
|
||||||
new TextStreamInfo(kTrackId, kTimeScale, kDuration, kCodecString,
|
new TextStreamInfo(kTrackId, kTimeScale, kDuration, kCodecString,
|
||||||
kLanguage, std::string(), kWidth, kHeight));
|
std::string(), kWidth, kHeight, kLanguage));
|
||||||
EXPECT_FALSE(generator_.Initialize(*stream_info));
|
EXPECT_FALSE(generator_.Initialize(*stream_info));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(PesPacketGeneratorTest, AddVideoSample) {
|
TEST_F(PesPacketGeneratorTest, AddVideoSample) {
|
||||||
scoped_refptr<VideoStreamInfo> stream_info(
|
scoped_refptr<VideoStreamInfo> stream_info(CreateVideoStreamInfo(kH264Codec));
|
||||||
CreateVideoStreamInfo(kH264VideoCodec));
|
|
||||||
EXPECT_TRUE(generator_.Initialize(*stream_info));
|
EXPECT_TRUE(generator_.Initialize(*stream_info));
|
||||||
EXPECT_EQ(0u, generator_.NumberOfReadyPesPackets());
|
EXPECT_EQ(0u, generator_.NumberOfReadyPesPackets());
|
||||||
|
|
||||||
|
@ -293,8 +289,7 @@ TEST_F(PesPacketGeneratorTest, AddVideoSample) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(PesPacketGeneratorTest, AddVideoSampleFailedToConvert) {
|
TEST_F(PesPacketGeneratorTest, AddVideoSampleFailedToConvert) {
|
||||||
scoped_refptr<VideoStreamInfo> stream_info(
|
scoped_refptr<VideoStreamInfo> stream_info(CreateVideoStreamInfo(kH264Codec));
|
||||||
CreateVideoStreamInfo(kH264VideoCodec));
|
|
||||||
EXPECT_TRUE(generator_.Initialize(*stream_info));
|
EXPECT_TRUE(generator_.Initialize(*stream_info));
|
||||||
EXPECT_EQ(0u, generator_.NumberOfReadyPesPackets());
|
EXPECT_EQ(0u, generator_.NumberOfReadyPesPackets());
|
||||||
|
|
||||||
|
@ -316,8 +311,7 @@ TEST_F(PesPacketGeneratorTest, AddVideoSampleFailedToConvert) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(PesPacketGeneratorTest, AddAudioSample) {
|
TEST_F(PesPacketGeneratorTest, AddAudioSample) {
|
||||||
scoped_refptr<AudioStreamInfo> stream_info(
|
scoped_refptr<AudioStreamInfo> stream_info(CreateAudioStreamInfo(kAacCodec));
|
||||||
CreateAudioStreamInfo(kAacAudioCodec));
|
|
||||||
EXPECT_TRUE(generator_.Initialize(*stream_info));
|
EXPECT_TRUE(generator_.Initialize(*stream_info));
|
||||||
EXPECT_EQ(0u, generator_.NumberOfReadyPesPackets());
|
EXPECT_EQ(0u, generator_.NumberOfReadyPesPackets());
|
||||||
|
|
||||||
|
@ -345,8 +339,7 @@ TEST_F(PesPacketGeneratorTest, AddAudioSample) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(PesPacketGeneratorTest, AddAudioSampleFailedToConvert) {
|
TEST_F(PesPacketGeneratorTest, AddAudioSampleFailedToConvert) {
|
||||||
scoped_refptr<AudioStreamInfo> stream_info(
|
scoped_refptr<AudioStreamInfo> stream_info(CreateAudioStreamInfo(kAacCodec));
|
||||||
CreateAudioStreamInfo(kAacAudioCodec));
|
|
||||||
EXPECT_TRUE(generator_.Initialize(*stream_info));
|
EXPECT_TRUE(generator_.Initialize(*stream_info));
|
||||||
EXPECT_EQ(0u, generator_.NumberOfReadyPesPackets());
|
EXPECT_EQ(0u, generator_.NumberOfReadyPesPackets());
|
||||||
|
|
||||||
|
@ -368,10 +361,9 @@ TEST_F(PesPacketGeneratorTest, AddAudioSampleFailedToConvert) {
|
||||||
TEST_F(PesPacketGeneratorTest, TimeStampScaling) {
|
TEST_F(PesPacketGeneratorTest, TimeStampScaling) {
|
||||||
const uint32_t kTestTimescale = 1000;
|
const uint32_t kTestTimescale = 1000;
|
||||||
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||||
kTrackId, kTestTimescale, kDuration, kH264VideoCodec, kCodecString,
|
kTrackId, kTestTimescale, kDuration, kH264Codec, kCodecString,
|
||||||
kLanguage, kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
kVideoExtraData, arraysize(kVideoExtraData), kWidth, kHeight, kPixelWidth,
|
||||||
kNaluLengthSize, kVideoExtraData, arraysize(kVideoExtraData),
|
kPixelHeight, kTrickPlayRate, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||||
kIsEncrypted));
|
|
||||||
EXPECT_TRUE(generator_.Initialize(*stream_info));
|
EXPECT_TRUE(generator_.Initialize(*stream_info));
|
||||||
|
|
||||||
EXPECT_EQ(0u, generator_.NumberOfReadyPesPackets());
|
EXPECT_EQ(0u, generator_.NumberOfReadyPesPackets());
|
||||||
|
|
|
@ -29,7 +29,7 @@ namespace {
|
||||||
// All data here is bogus. They are used to create VideoStreamInfo but the
|
// All data here is bogus. They are used to create VideoStreamInfo but the
|
||||||
// actual values don't matter at all.
|
// actual values don't matter at all.
|
||||||
const bool kIsKeyFrame = true;
|
const bool kIsKeyFrame = true;
|
||||||
const VideoCodec kH264VideoCodec = VideoCodec::kCodecH264;
|
const Codec kH264Codec = Codec::kCodecH264;
|
||||||
const uint8_t kExtraData[] = {
|
const uint8_t kExtraData[] = {
|
||||||
0x00,
|
0x00,
|
||||||
};
|
};
|
||||||
|
@ -107,9 +107,9 @@ class TsSegmenterTest : public ::testing::Test {
|
||||||
|
|
||||||
TEST_F(TsSegmenterTest, Initialize) {
|
TEST_F(TsSegmenterTest, Initialize) {
|
||||||
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||||
kTrackId, kTimeScale, kDuration, kH264VideoCodec, kCodecString, kLanguage,
|
kTrackId, kTimeScale, kDuration, kH264Codec, kCodecString, kExtraData,
|
||||||
kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
kTrickPlayRate, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||||
MuxerOptions options;
|
MuxerOptions options;
|
||||||
options.segment_template = "file$Number$.ts";
|
options.segment_template = "file$Number$.ts";
|
||||||
TsSegmenter segmenter(options, nullptr);
|
TsSegmenter segmenter(options, nullptr);
|
||||||
|
@ -127,9 +127,9 @@ TEST_F(TsSegmenterTest, Initialize) {
|
||||||
|
|
||||||
TEST_F(TsSegmenterTest, AddSample) {
|
TEST_F(TsSegmenterTest, AddSample) {
|
||||||
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||||
kTrackId, kTimeScale, kDuration, kH264VideoCodec, kCodecString, kLanguage,
|
kTrackId, kTimeScale, kDuration, kH264Codec, kCodecString, kExtraData,
|
||||||
kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
kTrickPlayRate, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||||
MuxerOptions options;
|
MuxerOptions options;
|
||||||
options.segment_duration = 10.0;
|
options.segment_duration = 10.0;
|
||||||
options.segment_template = "file$Number$.ts";
|
options.segment_template = "file$Number$.ts";
|
||||||
|
@ -182,9 +182,9 @@ TEST_F(TsSegmenterTest, PassedSegmentDuration) {
|
||||||
// done correctly in the segmenter.
|
// done correctly in the segmenter.
|
||||||
const uint32_t kInputTimescale = 1001;
|
const uint32_t kInputTimescale = 1001;
|
||||||
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||||
kTrackId, kInputTimescale, kDuration, kH264VideoCodec, kCodecString,
|
kTrackId, kInputTimescale, kDuration, kH264Codec, kCodecString,
|
||||||
kLanguage, kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
kExtraData, arraysize(kExtraData), kWidth, kHeight, kPixelWidth,
|
||||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
kPixelHeight, kTrickPlayRate, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||||
MuxerOptions options;
|
MuxerOptions options;
|
||||||
options.segment_duration = 10.0;
|
options.segment_duration = 10.0;
|
||||||
options.segment_template = "file$Number$.ts";
|
options.segment_template = "file$Number$.ts";
|
||||||
|
@ -281,9 +281,9 @@ TEST_F(TsSegmenterTest, PassedSegmentDuration) {
|
||||||
// Finalize right after Initialize(). The writer will not be initialized.
|
// Finalize right after Initialize(). The writer will not be initialized.
|
||||||
TEST_F(TsSegmenterTest, InitializeThenFinalize) {
|
TEST_F(TsSegmenterTest, InitializeThenFinalize) {
|
||||||
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||||
kTrackId, kTimeScale, kDuration, kH264VideoCodec, kCodecString, kLanguage,
|
kTrackId, kTimeScale, kDuration, kH264Codec, kCodecString, kExtraData,
|
||||||
kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
kTrickPlayRate, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||||
MuxerOptions options;
|
MuxerOptions options;
|
||||||
options.segment_duration = 10.0;
|
options.segment_duration = 10.0;
|
||||||
options.segment_template = "file$Number$.ts";
|
options.segment_template = "file$Number$.ts";
|
||||||
|
@ -310,9 +310,9 @@ TEST_F(TsSegmenterTest, InitializeThenFinalize) {
|
||||||
// writer with a mock.
|
// writer with a mock.
|
||||||
TEST_F(TsSegmenterTest, Finalize) {
|
TEST_F(TsSegmenterTest, Finalize) {
|
||||||
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||||
kTrackId, kTimeScale, kDuration, kH264VideoCodec, kCodecString, kLanguage,
|
kTrackId, kTimeScale, kDuration, kH264Codec, kCodecString, kExtraData,
|
||||||
kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
kTrickPlayRate, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||||
MuxerOptions options;
|
MuxerOptions options;
|
||||||
options.segment_duration = 10.0;
|
options.segment_duration = 10.0;
|
||||||
options.segment_template = "file$Number$.ts";
|
options.segment_template = "file$Number$.ts";
|
||||||
|
@ -339,9 +339,9 @@ TEST_F(TsSegmenterTest, Finalize) {
|
||||||
// Verify that it won't finish a segment if the sample is not a key frame.
|
// Verify that it won't finish a segment if the sample is not a key frame.
|
||||||
TEST_F(TsSegmenterTest, SegmentOnlyBeforeKeyFrame) {
|
TEST_F(TsSegmenterTest, SegmentOnlyBeforeKeyFrame) {
|
||||||
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||||
kTrackId, kTimeScale, kDuration, kH264VideoCodec, kCodecString, kLanguage,
|
kTrackId, kTimeScale, kDuration, kH264Codec, kCodecString, kExtraData,
|
||||||
kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
kTrickPlayRate, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||||
MuxerOptions options;
|
MuxerOptions options;
|
||||||
options.segment_duration = 10.0;
|
options.segment_duration = 10.0;
|
||||||
options.segment_template = "file$Number$.ts";
|
options.segment_template = "file$Number$.ts";
|
||||||
|
@ -445,9 +445,9 @@ TEST_F(TsSegmenterTest, SegmentOnlyBeforeKeyFrame) {
|
||||||
|
|
||||||
TEST_F(TsSegmenterTest, WithEncryptionNoClearLead) {
|
TEST_F(TsSegmenterTest, WithEncryptionNoClearLead) {
|
||||||
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||||
kTrackId, kTimeScale, kDuration, kH264VideoCodec, kCodecString, kLanguage,
|
kTrackId, kTimeScale, kDuration, kH264Codec, kCodecString, kExtraData,
|
||||||
kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
kTrickPlayRate, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||||
MuxerOptions options;
|
MuxerOptions options;
|
||||||
options.segment_duration = 10.0;
|
options.segment_duration = 10.0;
|
||||||
options.segment_template = "file$Number$.ts";
|
options.segment_template = "file$Number$.ts";
|
||||||
|
@ -485,9 +485,9 @@ TEST_F(TsSegmenterTest, WithEncryptionNoClearLead) {
|
||||||
// not null.
|
// not null.
|
||||||
TEST_F(TsSegmenterTest, WithEncryptionNoClearLeadNoMuxerListener) {
|
TEST_F(TsSegmenterTest, WithEncryptionNoClearLeadNoMuxerListener) {
|
||||||
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||||
kTrackId, kTimeScale, kDuration, kH264VideoCodec, kCodecString, kLanguage,
|
kTrackId, kTimeScale, kDuration, kH264Codec, kCodecString, kExtraData,
|
||||||
kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
kTrickPlayRate, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||||
MuxerOptions options;
|
MuxerOptions options;
|
||||||
options.segment_duration = 10.0;
|
options.segment_duration = 10.0;
|
||||||
options.segment_template = "file$Number$.ts";
|
options.segment_template = "file$Number$.ts";
|
||||||
|
@ -522,9 +522,9 @@ TEST_F(TsSegmenterTest, WithEncryptionNoClearLeadNoMuxerListener) {
|
||||||
// Verify that encryption notification is sent to objects after clear lead.
|
// Verify that encryption notification is sent to objects after clear lead.
|
||||||
TEST_F(TsSegmenterTest, WithEncryptionWithClearLead) {
|
TEST_F(TsSegmenterTest, WithEncryptionWithClearLead) {
|
||||||
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||||
kTrackId, kTimeScale, kDuration, kH264VideoCodec, kCodecString,
|
kTrackId, kTimeScale, kDuration, kH264Codec, kCodecString, kExtraData,
|
||||||
kLanguage, kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
kTrickPlayRate, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||||
MuxerOptions options;
|
MuxerOptions options;
|
||||||
|
|
||||||
options.segment_duration = 1.0;
|
options.segment_duration = 1.0;
|
||||||
|
|
|
@ -174,7 +174,7 @@ bool TsWriter::Initialize(const StreamInfo& stream_info) {
|
||||||
if (stream_info.stream_type() == StreamType::kStreamVideo) {
|
if (stream_info.stream_type() == StreamType::kStreamVideo) {
|
||||||
const VideoStreamInfo& video_stream_info =
|
const VideoStreamInfo& video_stream_info =
|
||||||
static_cast<const VideoStreamInfo&>(stream_info);
|
static_cast<const VideoStreamInfo&>(stream_info);
|
||||||
if (video_stream_info.codec() != VideoCodec::kCodecH264) {
|
if (video_stream_info.codec() != Codec::kCodecH264) {
|
||||||
LOG(ERROR) << "TsWriter cannot handle video codec "
|
LOG(ERROR) << "TsWriter cannot handle video codec "
|
||||||
<< video_stream_info.codec() << " yet.";
|
<< video_stream_info.codec() << " yet.";
|
||||||
return false;
|
return false;
|
||||||
|
@ -184,7 +184,7 @@ bool TsWriter::Initialize(const StreamInfo& stream_info) {
|
||||||
DCHECK_EQ(stream_type, StreamType::kStreamAudio);
|
DCHECK_EQ(stream_type, StreamType::kStreamAudio);
|
||||||
const AudioStreamInfo& audio_stream_info =
|
const AudioStreamInfo& audio_stream_info =
|
||||||
static_cast<const AudioStreamInfo&>(stream_info);
|
static_cast<const AudioStreamInfo&>(stream_info);
|
||||||
if (audio_stream_info.codec() != AudioCodec::kCodecAAC) {
|
if (audio_stream_info.codec() != Codec::kCodecAAC) {
|
||||||
LOG(ERROR) << "TsWriter cannot handle audio codec "
|
LOG(ERROR) << "TsWriter cannot handle audio codec "
|
||||||
<< audio_stream_info.codec() << " yet.";
|
<< audio_stream_info.codec() << " yet.";
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -28,8 +28,8 @@ namespace {
|
||||||
const int kTsPacketSize = 188;
|
const int kTsPacketSize = 188;
|
||||||
|
|
||||||
// Only {Audio,Video}Codec matter for this test. Other values are bogus.
|
// Only {Audio,Video}Codec matter for this test. Other values are bogus.
|
||||||
const VideoCodec kH264VideoCodec = VideoCodec::kCodecH264;
|
const Codec kH264Codec = Codec::kCodecH264;
|
||||||
const AudioCodec kAacAudioCodec = AudioCodec::kCodecAAC;
|
const Codec kAacCodec = Codec::kCodecAAC;
|
||||||
const int kTrackId = 0;
|
const int kTrackId = 0;
|
||||||
const uint32_t kTimeScale = 90000;
|
const uint32_t kTimeScale = 90000;
|
||||||
const uint64_t kDuration = 180000;
|
const uint64_t kDuration = 180000;
|
||||||
|
@ -158,35 +158,35 @@ class TsWriterTest : public ::testing::Test {
|
||||||
|
|
||||||
TEST_F(TsWriterTest, InitializeVideoH264) {
|
TEST_F(TsWriterTest, InitializeVideoH264) {
|
||||||
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||||
kTrackId, kTimeScale, kDuration, kH264VideoCodec, kCodecString, kLanguage,
|
kTrackId, kTimeScale, kDuration, kH264Codec, kCodecString, kExtraData,
|
||||||
kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
kTrickPlayRate, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TsWriterTest, InitializeVideoNonH264) {
|
TEST_F(TsWriterTest, InitializeVideoNonH264) {
|
||||||
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||||
kTrackId, kTimeScale, kDuration, VideoCodec::kCodecVP9, kCodecString,
|
kTrackId, kTimeScale, kDuration, Codec::kCodecVP9, kCodecString,
|
||||||
kLanguage, kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
kExtraData, arraysize(kExtraData), kWidth, kHeight, kPixelWidth,
|
||||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
kPixelHeight, kTrickPlayRate, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||||
EXPECT_FALSE(ts_writer_.Initialize(*stream_info));
|
EXPECT_FALSE(ts_writer_.Initialize(*stream_info));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TsWriterTest, InitializeAudioAac) {
|
TEST_F(TsWriterTest, InitializeAudioAac) {
|
||||||
scoped_refptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
|
scoped_refptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
|
||||||
kTrackId, kTimeScale, kDuration, kAacAudioCodec, kCodecString, kLanguage,
|
kTrackId, kTimeScale, kDuration, kAacCodec, kCodecString, kExtraData,
|
||||||
kSampleBits, kNumChannels, kSamplingFrequency, kSeekPreroll, kCodecDelay,
|
arraysize(kExtraData), kSampleBits, kNumChannels, kSamplingFrequency,
|
||||||
kMaxBitrate, kAverageBitrate, kExtraData, arraysize(kExtraData),
|
kSeekPreroll, kCodecDelay, kMaxBitrate, kAverageBitrate, kLanguage,
|
||||||
kIsEncrypted));
|
kIsEncrypted));
|
||||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TsWriterTest, InitializeAudioNonAac) {
|
TEST_F(TsWriterTest, InitializeAudioNonAac) {
|
||||||
scoped_refptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
|
scoped_refptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
|
||||||
kTrackId, kTimeScale, kDuration, AudioCodec::kCodecOpus, kCodecString,
|
kTrackId, kTimeScale, kDuration, Codec::kCodecOpus, kCodecString,
|
||||||
kLanguage, kSampleBits, kNumChannels, kSamplingFrequency, kSeekPreroll,
|
kExtraData, arraysize(kExtraData), kSampleBits, kNumChannels,
|
||||||
kCodecDelay, kMaxBitrate, kAverageBitrate, kExtraData,
|
kSamplingFrequency, kSeekPreroll, kCodecDelay, kMaxBitrate,
|
||||||
arraysize(kExtraData), kIsEncrypted));
|
kAverageBitrate, kLanguage, kIsEncrypted));
|
||||||
EXPECT_FALSE(ts_writer_.Initialize(*stream_info));
|
EXPECT_FALSE(ts_writer_.Initialize(*stream_info));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,9 +199,9 @@ TEST_F(TsWriterTest, ClearH264Psi) {
|
||||||
EXPECT_CALL(*mock_pmt_writer, ClearSegmentPmt(_)).WillOnce(WriteOnePmt());
|
EXPECT_CALL(*mock_pmt_writer, ClearSegmentPmt(_)).WillOnce(WriteOnePmt());
|
||||||
|
|
||||||
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||||
kTrackId, kTimeScale, kDuration, kH264VideoCodec, kCodecString, kLanguage,
|
kTrackId, kTimeScale, kDuration, kH264Codec, kCodecString, kExtraData,
|
||||||
kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
kTrickPlayRate, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||||
|
|
||||||
ts_writer_.SetProgramMapTableWriterForTesting(mock_pmt_writer.Pass());
|
ts_writer_.SetProgramMapTableWriterForTesting(mock_pmt_writer.Pass());
|
||||||
|
@ -253,10 +253,10 @@ TEST_F(TsWriterTest, ClearAacPmt) {
|
||||||
EXPECT_CALL(*mock_pmt_writer, ClearSegmentPmt(_)).WillOnce(WriteOnePmt());
|
EXPECT_CALL(*mock_pmt_writer, ClearSegmentPmt(_)).WillOnce(WriteOnePmt());
|
||||||
|
|
||||||
scoped_refptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
|
scoped_refptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
|
||||||
kTrackId, kTimeScale, kDuration, kAacAudioCodec, kCodecString, kLanguage,
|
kTrackId, kTimeScale, kDuration, kAacCodec, kCodecString,
|
||||||
|
kAacBasicProfileExtraData, arraysize(kAacBasicProfileExtraData),
|
||||||
kSampleBits, kNumChannels, kSamplingFrequency, kSeekPreroll, kCodecDelay,
|
kSampleBits, kNumChannels, kSamplingFrequency, kSeekPreroll, kCodecDelay,
|
||||||
kMaxBitrate, kAverageBitrate, kAacBasicProfileExtraData,
|
kMaxBitrate, kAverageBitrate, kLanguage, kIsEncrypted));
|
||||||
arraysize(kAacBasicProfileExtraData), kIsEncrypted));
|
|
||||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||||
|
|
||||||
ts_writer_.SetProgramMapTableWriterForTesting(mock_pmt_writer.Pass());
|
ts_writer_.SetProgramMapTableWriterForTesting(mock_pmt_writer.Pass());
|
||||||
|
@ -281,9 +281,9 @@ TEST_F(TsWriterTest, ClearLeadH264Pmt) {
|
||||||
.WillOnce(WriteTwoPmts());
|
.WillOnce(WriteTwoPmts());
|
||||||
|
|
||||||
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||||
kTrackId, kTimeScale, kDuration, kH264VideoCodec, kCodecString, kLanguage,
|
kTrackId, kTimeScale, kDuration, kH264Codec, kCodecString, kExtraData,
|
||||||
kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
kTrickPlayRate, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||||
|
|
||||||
ts_writer_.SetProgramMapTableWriterForTesting(mock_pmt_writer.Pass());
|
ts_writer_.SetProgramMapTableWriterForTesting(mock_pmt_writer.Pass());
|
||||||
|
@ -310,9 +310,9 @@ TEST_F(TsWriterTest, EncryptedSegmentsH264Pmt) {
|
||||||
EXPECT_CALL(*mock_pmt_writer, EncryptedSegmentPmt(_)).WillOnce(WriteOnePmt());
|
EXPECT_CALL(*mock_pmt_writer, EncryptedSegmentPmt(_)).WillOnce(WriteOnePmt());
|
||||||
|
|
||||||
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||||
kTrackId, kTimeScale, kDuration, kH264VideoCodec, kCodecString, kLanguage,
|
kTrackId, kTimeScale, kDuration, kH264Codec, kCodecString, kExtraData,
|
||||||
kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
kTrickPlayRate, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||||
|
|
||||||
ts_writer_.SetProgramMapTableWriterForTesting(mock_pmt_writer.Pass());
|
ts_writer_.SetProgramMapTableWriterForTesting(mock_pmt_writer.Pass());
|
||||||
|
@ -341,10 +341,10 @@ TEST_F(TsWriterTest, ClearLeadAacPmt) {
|
||||||
.WillOnce(WriteTwoPmts());
|
.WillOnce(WriteTwoPmts());
|
||||||
|
|
||||||
scoped_refptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
|
scoped_refptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
|
||||||
kTrackId, kTimeScale, kDuration, kAacAudioCodec, kCodecString, kLanguage,
|
kTrackId, kTimeScale, kDuration, kAacCodec, kCodecString,
|
||||||
|
kAacBasicProfileExtraData, arraysize(kAacBasicProfileExtraData),
|
||||||
kSampleBits, kNumChannels, kSamplingFrequency, kSeekPreroll, kCodecDelay,
|
kSampleBits, kNumChannels, kSamplingFrequency, kSeekPreroll, kCodecDelay,
|
||||||
kMaxBitrate, kAverageBitrate, kAacBasicProfileExtraData,
|
kMaxBitrate, kAverageBitrate, kLanguage, kIsEncrypted));
|
||||||
arraysize(kAacBasicProfileExtraData), kIsEncrypted));
|
|
||||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||||
|
|
||||||
ts_writer_.SetProgramMapTableWriterForTesting(mock_pmt_writer.Pass());
|
ts_writer_.SetProgramMapTableWriterForTesting(mock_pmt_writer.Pass());
|
||||||
|
@ -371,10 +371,10 @@ TEST_F(TsWriterTest, EncryptedSegmentsAacPmt) {
|
||||||
EXPECT_CALL(*mock_pmt_writer, EncryptedSegmentPmt(_)).WillOnce(WriteOnePmt());
|
EXPECT_CALL(*mock_pmt_writer, EncryptedSegmentPmt(_)).WillOnce(WriteOnePmt());
|
||||||
|
|
||||||
scoped_refptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
|
scoped_refptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
|
||||||
kTrackId, kTimeScale, kDuration, kAacAudioCodec, kCodecString, kLanguage,
|
kTrackId, kTimeScale, kDuration, kAacCodec, kCodecString,
|
||||||
|
kAacBasicProfileExtraData, arraysize(kAacBasicProfileExtraData),
|
||||||
kSampleBits, kNumChannels, kSamplingFrequency, kSeekPreroll, kCodecDelay,
|
kSampleBits, kNumChannels, kSamplingFrequency, kSeekPreroll, kCodecDelay,
|
||||||
kMaxBitrate, kAverageBitrate, kAacBasicProfileExtraData,
|
kMaxBitrate, kAverageBitrate, kLanguage, kIsEncrypted));
|
||||||
arraysize(kAacBasicProfileExtraData), kIsEncrypted));
|
|
||||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||||
|
|
||||||
ts_writer_.SetProgramMapTableWriterForTesting(mock_pmt_writer.Pass());
|
ts_writer_.SetProgramMapTableWriterForTesting(mock_pmt_writer.Pass());
|
||||||
|
@ -398,9 +398,9 @@ TEST_F(TsWriterTest, EncryptedSegmentsAacPmt) {
|
||||||
|
|
||||||
TEST_F(TsWriterTest, AddPesPacket) {
|
TEST_F(TsWriterTest, AddPesPacket) {
|
||||||
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||||
kTrackId, kTimeScale, kDuration, kH264VideoCodec, kCodecString, kLanguage,
|
kTrackId, kTimeScale, kDuration, kH264Codec, kCodecString, kExtraData,
|
||||||
kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
kTrickPlayRate, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||||
|
|
||||||
|
@ -463,9 +463,9 @@ TEST_F(TsWriterTest, AddPesPacket) {
|
||||||
// Verify that PES packet > 64KiB can be handled.
|
// Verify that PES packet > 64KiB can be handled.
|
||||||
TEST_F(TsWriterTest, BigPesPacket) {
|
TEST_F(TsWriterTest, BigPesPacket) {
|
||||||
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||||
kTrackId, kTimeScale, kDuration, kH264VideoCodec, kCodecString, kLanguage,
|
kTrackId, kTimeScale, kDuration, kH264Codec, kCodecString, kExtraData,
|
||||||
kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
kTrickPlayRate, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||||
|
|
||||||
|
@ -499,9 +499,9 @@ TEST_F(TsWriterTest, BigPesPacket) {
|
||||||
// PTS (implicilty) cast to bool is true.
|
// PTS (implicilty) cast to bool is true.
|
||||||
TEST_F(TsWriterTest, PesPtsZeroNoDts) {
|
TEST_F(TsWriterTest, PesPtsZeroNoDts) {
|
||||||
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||||
kTrackId, kTimeScale, kDuration, kH264VideoCodec, kCodecString, kLanguage,
|
kTrackId, kTimeScale, kDuration, kH264Codec, kCodecString, kExtraData,
|
||||||
kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
kTrickPlayRate, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||||
|
|
||||||
|
@ -559,9 +559,9 @@ TEST_F(TsWriterTest, PesPtsZeroNoDts) {
|
||||||
// adaptation_field_length should be 0.
|
// adaptation_field_length should be 0.
|
||||||
TEST_F(TsWriterTest, TsPacketPayload183Bytes) {
|
TEST_F(TsWriterTest, TsPacketPayload183Bytes) {
|
||||||
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||||
kTrackId, kTimeScale, kDuration, kH264VideoCodec, kCodecString, kLanguage,
|
kTrackId, kTimeScale, kDuration, kH264Codec, kCodecString, kExtraData,
|
||||||
kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
kTrickPlayRate, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||||
|
|
||||||
|
|
|
@ -41,9 +41,8 @@ void AddSubsamples(uint64_t clear_bytes,
|
||||||
subsamples->push_back(SubsampleEntry(clear_bytes, cipher_bytes));
|
subsamples->push_back(SubsampleEntry(clear_bytes, cipher_bytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoCodec GetVideoCodec(const StreamInfo& stream_info) {
|
Codec GetCodec(const StreamInfo& stream_info) {
|
||||||
if (stream_info.stream_type() != kStreamVideo)
|
if (stream_info.stream_type() != kStreamVideo) return kUnknownCodec;
|
||||||
return kUnknownVideoCodec;
|
|
||||||
const VideoStreamInfo& video_stream_info =
|
const VideoStreamInfo& video_stream_info =
|
||||||
static_cast<const VideoStreamInfo&>(stream_info);
|
static_cast<const VideoStreamInfo&>(stream_info);
|
||||||
return video_stream_info.codec();
|
return video_stream_info.codec();
|
||||||
|
@ -60,19 +59,15 @@ uint8_t GetNaluLengthSize(const StreamInfo& stream_info) {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
EncryptingFragmenter::EncryptingFragmenter(
|
EncryptingFragmenter::EncryptingFragmenter(
|
||||||
scoped_refptr<StreamInfo> info,
|
scoped_refptr<StreamInfo> info, TrackFragment* traf,
|
||||||
TrackFragment* traf,
|
scoped_ptr<EncryptionKey> encryption_key, int64_t clear_time,
|
||||||
scoped_ptr<EncryptionKey> encryption_key,
|
FourCC protection_scheme, uint8_t crypt_byte_block, uint8_t skip_byte_block,
|
||||||
int64_t clear_time,
|
|
||||||
FourCC protection_scheme,
|
|
||||||
uint8_t crypt_byte_block,
|
|
||||||
uint8_t skip_byte_block,
|
|
||||||
MuxerListener* listener)
|
MuxerListener* listener)
|
||||||
: Fragmenter(info, traf),
|
: Fragmenter(info, traf),
|
||||||
info_(info),
|
info_(info),
|
||||||
encryption_key_(encryption_key.Pass()),
|
encryption_key_(encryption_key.Pass()),
|
||||||
nalu_length_size_(GetNaluLengthSize(*info)),
|
nalu_length_size_(GetNaluLengthSize(*info)),
|
||||||
video_codec_(GetVideoCodec(*info)),
|
video_codec_(GetCodec(*info)),
|
||||||
clear_time_(clear_time),
|
clear_time_(clear_time),
|
||||||
protection_scheme_(protection_scheme),
|
protection_scheme_(protection_scheme),
|
||||||
crypt_byte_block_(crypt_byte_block),
|
crypt_byte_block_(crypt_byte_block),
|
||||||
|
|
|
@ -92,7 +92,7 @@ class EncryptingFragmenter : public Fragmenter {
|
||||||
// and type of NAL units remain unencrypted. This function returns the size of
|
// and type of NAL units remain unencrypted. This function returns the size of
|
||||||
// the size field in bytes. Can be 1, 2 or 4 bytes.
|
// the size field in bytes. Can be 1, 2 or 4 bytes.
|
||||||
const uint8_t nalu_length_size_;
|
const uint8_t nalu_length_size_;
|
||||||
const VideoCodec video_codec_;
|
const Codec video_codec_;
|
||||||
int64_t clear_time_;
|
int64_t clear_time_;
|
||||||
const FourCC protection_scheme_;
|
const FourCC protection_scheme_;
|
||||||
const uint8_t crypt_byte_block_;
|
const uint8_t crypt_byte_block_;
|
||||||
|
|
|
@ -40,7 +40,7 @@ uint64_t Rescale(uint64_t time_in_old_scale,
|
||||||
return (static_cast<double>(time_in_old_scale) / old_scale) * new_scale;
|
return (static_cast<double>(time_in_old_scale) / old_scale) * new_scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoCodec FourCCToVideoCodec(FourCC fourcc) {
|
Codec FourCCToCodec(FourCC fourcc) {
|
||||||
switch (fourcc) {
|
switch (fourcc) {
|
||||||
case FOURCC_avc1:
|
case FOURCC_avc1:
|
||||||
return kCodecH264;
|
return kCodecH264;
|
||||||
|
@ -54,13 +54,6 @@ VideoCodec FourCCToVideoCodec(FourCC fourcc) {
|
||||||
return kCodecVP9;
|
return kCodecVP9;
|
||||||
case FOURCC_vp10:
|
case FOURCC_vp10:
|
||||||
return kCodecVP10;
|
return kCodecVP10;
|
||||||
default:
|
|
||||||
return kUnknownVideoCodec;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioCodec FourCCToAudioCodec(FourCC fourcc) {
|
|
||||||
switch(fourcc) {
|
|
||||||
case FOURCC_Opus:
|
case FOURCC_Opus:
|
||||||
return kCodecOpus;
|
return kCodecOpus;
|
||||||
case FOURCC_dtsc:
|
case FOURCC_dtsc:
|
||||||
|
@ -80,7 +73,7 @@ AudioCodec FourCCToAudioCodec(FourCC fourcc) {
|
||||||
case FOURCC_ec_3:
|
case FOURCC_ec_3:
|
||||||
return kCodecEAC3;
|
return kCodecEAC3;
|
||||||
default:
|
default:
|
||||||
return kUnknownAudioCodec;
|
return kUnknownCodec;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,7 +336,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
||||||
|
|
||||||
const AudioSampleEntry& entry = samp_descr.audio_entries[desc_idx];
|
const AudioSampleEntry& entry = samp_descr.audio_entries[desc_idx];
|
||||||
const FourCC actual_format = entry.GetActualFormat();
|
const FourCC actual_format = entry.GetActualFormat();
|
||||||
AudioCodec codec = FourCCToAudioCodec(actual_format);
|
Codec codec = FourCCToCodec(actual_format);
|
||||||
uint8_t num_channels = 0;
|
uint8_t num_channels = 0;
|
||||||
uint32_t sampling_frequency = 0;
|
uint32_t sampling_frequency = 0;
|
||||||
uint64_t codec_delay_ns = 0;
|
uint64_t codec_delay_ns = 0;
|
||||||
|
@ -473,21 +466,11 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
||||||
entry.sinf.info.track_encryption.default_is_protected == 1;
|
entry.sinf.info.track_encryption.default_is_protected == 1;
|
||||||
DVLOG(1) << "is_audio_track_encrypted_: " << is_encrypted;
|
DVLOG(1) << "is_audio_track_encrypted_: " << is_encrypted;
|
||||||
streams.push_back(new AudioStreamInfo(
|
streams.push_back(new AudioStreamInfo(
|
||||||
track->header.track_id,
|
track->header.track_id, timescale, duration, codec,
|
||||||
timescale,
|
|
||||||
duration,
|
|
||||||
codec,
|
|
||||||
AudioStreamInfo::GetCodecString(codec, audio_object_type),
|
AudioStreamInfo::GetCodecString(codec, audio_object_type),
|
||||||
track->media.header.language.code,
|
codec_config.data(), codec_config.size(), entry.samplesize,
|
||||||
entry.samplesize,
|
num_channels, sampling_frequency, seek_preroll_ns, codec_delay_ns,
|
||||||
num_channels,
|
max_bitrate, avg_bitrate, track->media.header.language.code,
|
||||||
sampling_frequency,
|
|
||||||
seek_preroll_ns,
|
|
||||||
codec_delay_ns,
|
|
||||||
max_bitrate,
|
|
||||||
avg_bitrate,
|
|
||||||
codec_config.data(),
|
|
||||||
codec_config.size(),
|
|
||||||
is_encrypted));
|
is_encrypted));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -509,7 +492,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
||||||
uint8_t nalu_length_size = 0;
|
uint8_t nalu_length_size = 0;
|
||||||
|
|
||||||
const FourCC actual_format = entry.GetActualFormat();
|
const FourCC actual_format = entry.GetActualFormat();
|
||||||
const VideoCodec video_codec = FourCCToVideoCodec(actual_format);
|
const Codec video_codec = FourCCToCodec(actual_format);
|
||||||
switch (actual_format) {
|
switch (actual_format) {
|
||||||
case FOURCC_avc1: {
|
case FOURCC_avc1: {
|
||||||
AVCDecoderConfigurationRecord avc_config;
|
AVCDecoderConfigurationRecord avc_config;
|
||||||
|
@ -580,11 +563,11 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
||||||
DVLOG(1) << "is_video_track_encrypted_: " << is_encrypted;
|
DVLOG(1) << "is_video_track_encrypted_: " << is_encrypted;
|
||||||
streams.push_back(new VideoStreamInfo(
|
streams.push_back(new VideoStreamInfo(
|
||||||
track->header.track_id, timescale, duration, video_codec,
|
track->header.track_id, timescale, duration, video_codec,
|
||||||
codec_string, track->media.header.language.code, coded_width,
|
codec_string, entry.codec_configuration.data.data(),
|
||||||
coded_height, pixel_width, pixel_height,
|
entry.codec_configuration.data.size(), coded_width, coded_height,
|
||||||
|
pixel_width, pixel_height,
|
||||||
0, // trick_play_rate
|
0, // trick_play_rate
|
||||||
nalu_length_size, entry.codec_configuration.data.data(),
|
nalu_length_size, track->media.header.language.code, is_encrypted));
|
||||||
entry.codec_configuration.data.size(), is_encrypted));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ void SetStartAndEndFromOffsetAndSize(size_t offset,
|
||||||
*end = *start + static_cast<uint32_t>(size) - 1;
|
*end = *start + static_cast<uint32_t>(size) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
FourCC VideoCodecToFourCC(VideoCodec codec) {
|
FourCC CodecToFourCC(Codec codec) {
|
||||||
switch (codec) {
|
switch (codec) {
|
||||||
case kCodecH264:
|
case kCodecH264:
|
||||||
return FOURCC_avc1;
|
return FOURCC_avc1;
|
||||||
|
@ -54,13 +54,6 @@ FourCC VideoCodecToFourCC(VideoCodec codec) {
|
||||||
return FOURCC_vp09;
|
return FOURCC_vp09;
|
||||||
case kCodecVP10:
|
case kCodecVP10:
|
||||||
return FOURCC_vp10;
|
return FOURCC_vp10;
|
||||||
default:
|
|
||||||
return FOURCC_NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FourCC AudioCodecToFourCC(AudioCodec codec) {
|
|
||||||
switch (codec) {
|
|
||||||
case kCodecAAC:
|
case kCodecAAC:
|
||||||
return FOURCC_mp4a;
|
return FOURCC_mp4a;
|
||||||
case kCodecAC3:
|
case kCodecAC3:
|
||||||
|
@ -100,7 +93,7 @@ Status MP4Muxer::Initialize() {
|
||||||
ftyp->compatible_brands.push_back(FOURCC_mp41);
|
ftyp->compatible_brands.push_back(FOURCC_mp41);
|
||||||
if (streams().size() == 1 &&
|
if (streams().size() == 1 &&
|
||||||
streams()[0]->info()->stream_type() == kStreamVideo) {
|
streams()[0]->info()->stream_type() == kStreamVideo) {
|
||||||
const FourCC codec_fourcc = VideoCodecToFourCC(
|
const FourCC codec_fourcc = CodecToFourCC(
|
||||||
static_cast<VideoStreamInfo*>(streams()[0]->info().get())->codec());
|
static_cast<VideoStreamInfo*>(streams()[0]->info().get())->codec());
|
||||||
if (codec_fourcc != FOURCC_NULL)
|
if (codec_fourcc != FOURCC_NULL)
|
||||||
ftyp->compatible_brands.push_back(codec_fourcc);
|
ftyp->compatible_brands.push_back(codec_fourcc);
|
||||||
|
@ -226,7 +219,7 @@ void MP4Muxer::GenerateVideoTrak(const VideoStreamInfo* video_info,
|
||||||
trak->header.height = video_info->height() * 0x10000;
|
trak->header.height = video_info->height() * 0x10000;
|
||||||
|
|
||||||
VideoSampleEntry video;
|
VideoSampleEntry video;
|
||||||
video.format = VideoCodecToFourCC(video_info->codec());
|
video.format = CodecToFourCC(video_info->codec());
|
||||||
video.width = video_info->width();
|
video.width = video_info->width();
|
||||||
video.height = video_info->height();
|
video.height = video_info->height();
|
||||||
video.codec_configuration.data = video_info->codec_config();
|
video.codec_configuration.data = video_info->codec_config();
|
||||||
|
@ -249,7 +242,7 @@ void MP4Muxer::GenerateAudioTrak(const AudioStreamInfo* audio_info,
|
||||||
trak->header.volume = 0x100;
|
trak->header.volume = 0x100;
|
||||||
|
|
||||||
AudioSampleEntry audio;
|
AudioSampleEntry audio;
|
||||||
audio.format = AudioCodecToFourCC(audio_info->codec());
|
audio.format = CodecToFourCC(audio_info->codec());
|
||||||
switch(audio_info->codec()){
|
switch(audio_info->codec()){
|
||||||
case kCodecAAC:
|
case kCodecAAC:
|
||||||
audio.esds.es_descriptor.set_object_type(kISO_14496_3); // MPEG4 AAC.
|
audio.esds.es_descriptor.set_object_type(kISO_14496_3); // MPEG4 AAC.
|
||||||
|
|
|
@ -6,15 +6,21 @@
|
||||||
|
|
||||||
#include "packager/media/formats/webm/encryptor.h"
|
#include "packager/media/formats/webm/encryptor.h"
|
||||||
|
|
||||||
|
#include <gflags/gflags.h>
|
||||||
#include "packager/media/base/aes_encryptor.h"
|
#include "packager/media/base/aes_encryptor.h"
|
||||||
|
#include "packager/media/base/buffer_writer.h"
|
||||||
#include "packager/media/base/fourccs.h"
|
#include "packager/media/base/fourccs.h"
|
||||||
#include "packager/media/base/media_sample.h"
|
#include "packager/media/base/media_sample.h"
|
||||||
|
#include "packager/media/codecs/vp9_parser.h"
|
||||||
|
#include "packager/media/formats/webm/webm_constants.h"
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
namespace webm {
|
namespace webm {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
const size_t kAesBlockSize = 16;
|
||||||
|
|
||||||
Status CreateContentEncryption(mkvmuxer::Track* track, EncryptionKey* key) {
|
Status CreateContentEncryption(mkvmuxer::Track* track, EncryptionKey* key) {
|
||||||
if (!track->AddContentEncoding()) {
|
if (!track->AddContentEncoding()) {
|
||||||
return Status(error::INTERNAL_ERROR,
|
return Status(error::INTERNAL_ERROR,
|
||||||
|
@ -54,9 +60,12 @@ Encryptor::~Encryptor() {}
|
||||||
|
|
||||||
Status Encryptor::Initialize(MuxerListener* muxer_listener,
|
Status Encryptor::Initialize(MuxerListener* muxer_listener,
|
||||||
KeySource::TrackType track_type,
|
KeySource::TrackType track_type,
|
||||||
KeySource* key_source) {
|
Codec codec,
|
||||||
|
KeySource* key_source,
|
||||||
|
bool webm_subsample_encryption) {
|
||||||
DCHECK(key_source);
|
DCHECK(key_source);
|
||||||
return CreateEncryptor(muxer_listener, track_type, key_source);
|
return CreateEncryptor(muxer_listener, track_type, codec, key_source,
|
||||||
|
webm_subsample_encryption);
|
||||||
}
|
}
|
||||||
|
|
||||||
Status Encryptor::AddTrackInfo(mkvmuxer::Track* track) {
|
Status Encryptor::AddTrackInfo(mkvmuxer::Track* track) {
|
||||||
|
@ -69,10 +78,71 @@ Status Encryptor::EncryptFrame(scoped_refptr<MediaSample> sample,
|
||||||
DCHECK(encryptor_);
|
DCHECK(encryptor_);
|
||||||
|
|
||||||
const size_t sample_size = sample->data_size();
|
const size_t sample_size = sample->data_size();
|
||||||
|
// We need to parse the frame (which also updates the vpx parser) even if the
|
||||||
|
// frame is not encrypted as the next (encrypted) frame may be dependent on
|
||||||
|
// this clear frame.
|
||||||
|
std::vector<VPxFrameInfo> vpx_frames;
|
||||||
|
if (vpx_parser_) {
|
||||||
|
if (!vpx_parser_->Parse(sample->data(), sample_size, &vpx_frames)) {
|
||||||
|
return Status(error::MUXER_FAILURE, "Failed to parse VPx frame.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (encrypt_frame) {
|
if (encrypt_frame) {
|
||||||
// | 1 | iv | enc_data |
|
|
||||||
const size_t iv_size = encryptor_->iv().size();
|
const size_t iv_size = encryptor_->iv().size();
|
||||||
sample->resize_data(sample_size + iv_size + 1);
|
if (iv_size != kWebMIvSize) {
|
||||||
|
return Status(error::MUXER_FAILURE,
|
||||||
|
"Incorrect size WebM encryption IV.");
|
||||||
|
}
|
||||||
|
if (vpx_frames.size()) {
|
||||||
|
// Use partitioned subsample encryption: | signal_byte(3) | iv
|
||||||
|
// | num_partitions | partition_offset * n | enc_data |
|
||||||
|
|
||||||
|
if (vpx_frames.size() > kWebMMaxSubsamples) {
|
||||||
|
return Status(error::MUXER_FAILURE,
|
||||||
|
"Maximum number of VPx encryption partitions exceeded.");
|
||||||
|
}
|
||||||
|
uint8_t num_partitions =
|
||||||
|
vpx_frames.size() == 1 ? 1 : vpx_frames.size() * 2;
|
||||||
|
size_t header_size = kWebMSignalByteSize + iv_size +
|
||||||
|
kWebMNumPartitionsSize +
|
||||||
|
(kWebMPartitionOffsetSize * num_partitions);
|
||||||
|
sample->resize_data(header_size + sample_size);
|
||||||
|
uint8_t* sample_data = sample->writable_data();
|
||||||
|
memmove(sample_data + header_size, sample_data, sample_size);
|
||||||
|
sample_data[0] = kWebMEncryptedSignal | kWebMPartitionedSignal;
|
||||||
|
memcpy(sample_data + kWebMSignalByteSize, encryptor_->iv().data(),
|
||||||
|
iv_size);
|
||||||
|
sample_data[kWebMSignalByteSize + kWebMIvSize] = num_partitions;
|
||||||
|
uint32 partition_offset = 0;
|
||||||
|
BufferWriter offsets_buffer(kWebMPartitionOffsetSize * num_partitions);
|
||||||
|
for (const auto& vpx_frame : vpx_frames) {
|
||||||
|
uint32_t encrypted_size =
|
||||||
|
vpx_frame.frame_size - vpx_frame.uncompressed_header_size;
|
||||||
|
encrypted_size -= encrypted_size % kAesBlockSize;
|
||||||
|
uint32_t clear_size = vpx_frame.frame_size - encrypted_size;
|
||||||
|
partition_offset += clear_size;
|
||||||
|
offsets_buffer.AppendInt(partition_offset);
|
||||||
|
if (encrypted_size > 0) {
|
||||||
|
uint8_t* encrypted_ptr = sample_data + header_size + partition_offset;
|
||||||
|
if (!encryptor_->Crypt(encrypted_ptr, encrypted_size, encrypted_ptr)) {
|
||||||
|
return Status(error::MUXER_FAILURE, "Failed to encrypt the frame.");
|
||||||
|
}
|
||||||
|
partition_offset += encrypted_size;
|
||||||
|
}
|
||||||
|
if (num_partitions > 1) {
|
||||||
|
offsets_buffer.AppendInt(partition_offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DCHECK_EQ(num_partitions * kWebMPartitionOffsetSize,
|
||||||
|
offsets_buffer.Size());
|
||||||
|
memcpy(sample_data + kWebMSignalByteSize + kWebMIvSize +
|
||||||
|
kWebMNumPartitionsSize,
|
||||||
|
offsets_buffer.Buffer(), offsets_buffer.Size());
|
||||||
|
} else {
|
||||||
|
// Use whole-frame encryption: | signal_byte(1) | iv | enc_data |
|
||||||
|
|
||||||
|
sample->resize_data(sample_size + iv_size + kWebMSignalByteSize);
|
||||||
uint8_t* sample_data = sample->writable_data();
|
uint8_t* sample_data = sample->writable_data();
|
||||||
|
|
||||||
// Encrypt the data in-place.
|
// Encrypt the data in-place.
|
||||||
|
@ -80,15 +150,16 @@ Status Encryptor::EncryptFrame(scoped_refptr<MediaSample> sample,
|
||||||
return Status(error::MUXER_FAILURE, "Failed to encrypt the frame.");
|
return Status(error::MUXER_FAILURE, "Failed to encrypt the frame.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// First move the sample data to after the IV; then write the IV and signal
|
// First move the sample data to after the IV; then write the IV and
|
||||||
// byte.
|
// signal byte.
|
||||||
memmove(sample_data + iv_size + 1, sample_data, sample_size);
|
memmove(sample_data + iv_size + kWebMSignalByteSize, sample_data,
|
||||||
sample_data[0] = 0x01;
|
sample_size);
|
||||||
|
sample_data[0] = kWebMEncryptedSignal;
|
||||||
memcpy(sample_data + 1, encryptor_->iv().data(), iv_size);
|
memcpy(sample_data + 1, encryptor_->iv().data(), iv_size);
|
||||||
|
}
|
||||||
encryptor_->UpdateIv();
|
encryptor_->UpdateIv();
|
||||||
} else {
|
} else {
|
||||||
// | 0 | data |
|
// Clear sample: | signal_byte(0) | data |
|
||||||
sample->resize_data(sample_size + 1);
|
sample->resize_data(sample_size + 1);
|
||||||
uint8_t* sample_data = sample->writable_data();
|
uint8_t* sample_data = sample->writable_data();
|
||||||
memmove(sample_data + 1, sample_data, sample_size);
|
memmove(sample_data + 1, sample_data, sample_size);
|
||||||
|
@ -100,7 +171,9 @@ Status Encryptor::EncryptFrame(scoped_refptr<MediaSample> sample,
|
||||||
|
|
||||||
Status Encryptor::CreateEncryptor(MuxerListener* muxer_listener,
|
Status Encryptor::CreateEncryptor(MuxerListener* muxer_listener,
|
||||||
KeySource::TrackType track_type,
|
KeySource::TrackType track_type,
|
||||||
KeySource* key_source) {
|
Codec codec,
|
||||||
|
KeySource* key_source,
|
||||||
|
bool webm_subsample_encryption) {
|
||||||
scoped_ptr<EncryptionKey> encryption_key(new EncryptionKey());
|
scoped_ptr<EncryptionKey> encryption_key(new EncryptionKey());
|
||||||
Status status = key_source->GetKey(track_type, encryption_key.get());
|
Status status = key_source->GetKey(track_type, encryption_key.get());
|
||||||
if (!status.ok())
|
if (!status.ok())
|
||||||
|
@ -109,13 +182,18 @@ Status Encryptor::CreateEncryptor(MuxerListener* muxer_listener,
|
||||||
if (!AesCryptor::GenerateRandomIv(FOURCC_cenc, &encryption_key->iv))
|
if (!AesCryptor::GenerateRandomIv(FOURCC_cenc, &encryption_key->iv))
|
||||||
return Status(error::INTERNAL_ERROR, "Failed to generate random iv.");
|
return Status(error::INTERNAL_ERROR, "Failed to generate random iv.");
|
||||||
}
|
}
|
||||||
|
DCHECK_EQ(kWebMIvSize, encryption_key->iv.size());
|
||||||
scoped_ptr<AesCtrEncryptor> encryptor(new AesCtrEncryptor());
|
scoped_ptr<AesCtrEncryptor> encryptor(new AesCtrEncryptor());
|
||||||
const bool initialized =
|
const bool initialized =
|
||||||
encryptor->InitializeWithIv(encryption_key->key, encryption_key->iv);
|
encryptor->InitializeWithIv(encryption_key->key, encryption_key->iv);
|
||||||
if (!initialized)
|
if (!initialized)
|
||||||
return Status(error::INTERNAL_ERROR, "Failed to create the encryptor.");
|
return Status(error::INTERNAL_ERROR, "Failed to create the encryptor.");
|
||||||
|
|
||||||
|
if (webm_subsample_encryption && codec == kCodecVP9) {
|
||||||
|
// Allocate VP9 parser to do subsample encryption of VP9.
|
||||||
|
vpx_parser_.reset(new VP9Parser);
|
||||||
|
}
|
||||||
|
|
||||||
if (muxer_listener) {
|
if (muxer_listener) {
|
||||||
const bool kInitialEncryptionInfo = true;
|
const bool kInitialEncryptionInfo = true;
|
||||||
muxer_listener->OnEncryptionInfoReady(
|
muxer_listener->OnEncryptionInfoReady(
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
#include "packager/base/memory/scoped_ptr.h"
|
#include "packager/base/memory/scoped_ptr.h"
|
||||||
#include "packager/media/base/key_source.h"
|
#include "packager/media/base/key_source.h"
|
||||||
#include "packager/media/base/status.h"
|
#include "packager/media/base/status.h"
|
||||||
|
#include "packager/media/base/stream_info.h"
|
||||||
|
#include "packager/media/codecs/vpx_parser.h"
|
||||||
#include "packager/media/event/muxer_listener.h"
|
#include "packager/media/event/muxer_listener.h"
|
||||||
#include "packager/third_party/libwebm/src/mkvmuxer.hpp"
|
#include "packager/third_party/libwebm/src/mkvmuxer.hpp"
|
||||||
|
|
||||||
|
@ -33,7 +35,9 @@ class Encryptor {
|
||||||
/// @return OK on success, an error status otherwise.
|
/// @return OK on success, an error status otherwise.
|
||||||
Status Initialize(MuxerListener* muxer_listener,
|
Status Initialize(MuxerListener* muxer_listener,
|
||||||
KeySource::TrackType track_type,
|
KeySource::TrackType track_type,
|
||||||
KeySource* key_source);
|
Codec codec,
|
||||||
|
KeySource* key_source,
|
||||||
|
bool webm_subsample_encryption);
|
||||||
|
|
||||||
/// Adds the encryption info to the given track. Initialize must be called
|
/// Adds the encryption info to the given track. Initialize must be called
|
||||||
/// first.
|
/// first.
|
||||||
|
@ -50,11 +54,14 @@ class Encryptor {
|
||||||
// Create the encryptor for the internal encryption key.
|
// Create the encryptor for the internal encryption key.
|
||||||
Status CreateEncryptor(MuxerListener* muxer_listener,
|
Status CreateEncryptor(MuxerListener* muxer_listener,
|
||||||
KeySource::TrackType track_type,
|
KeySource::TrackType track_type,
|
||||||
KeySource* key_source);
|
Codec codec,
|
||||||
|
KeySource* key_source,
|
||||||
|
bool webm_subsample_encryption);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
scoped_ptr<EncryptionKey> key_;
|
scoped_ptr<EncryptionKey> key_;
|
||||||
scoped_ptr<AesCtrEncryptor> encryptor_;
|
scoped_ptr<AesCtrEncryptor> encryptor_;
|
||||||
|
scoped_ptr<VPxParser> vpx_parser_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webm
|
} // namespace webm
|
||||||
|
|
|
@ -79,7 +79,8 @@ Status Segmenter::Initialize(scoped_ptr<MkvWriter> writer,
|
||||||
|
|
||||||
Status status;
|
Status status;
|
||||||
if (encryption_key_source) {
|
if (encryption_key_source) {
|
||||||
status = InitializeEncryptor(encryption_key_source, max_sd_pixels);
|
status = InitializeEncryptor(encryption_key_source,
|
||||||
|
max_sd_pixels);
|
||||||
if (!status.ok())
|
if (!status.ok())
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -362,7 +363,8 @@ Status Segmenter::InitializeEncryptor(KeySource* key_source,
|
||||||
GetTrackTypeForEncryption(*info_, max_sd_pixels);
|
GetTrackTypeForEncryption(*info_, max_sd_pixels);
|
||||||
if (track_type == KeySource::TrackType::TRACK_TYPE_UNKNOWN)
|
if (track_type == KeySource::TrackType::TRACK_TYPE_UNKNOWN)
|
||||||
return Status::OK;
|
return Status::OK;
|
||||||
return encryptor_->Initialize(muxer_listener_, track_type, key_source);
|
return encryptor_->Initialize(muxer_listener_, track_type, info_->codec(),
|
||||||
|
key_source, options_.webm_subsample_encryption);
|
||||||
}
|
}
|
||||||
|
|
||||||
Status Segmenter::WriteFrame(bool write_duration) {
|
Status Segmenter::WriteFrame(bool write_duration) {
|
||||||
|
|
|
@ -25,7 +25,7 @@ const uint8_t kTestMediaSampleSideData[] = {
|
||||||
const int kTrackId = 1;
|
const int kTrackId = 1;
|
||||||
const uint32_t kTimeScale = 1000;
|
const uint32_t kTimeScale = 1000;
|
||||||
const uint64_t kDuration = 8000;
|
const uint64_t kDuration = 8000;
|
||||||
const VideoCodec kVideoCodec = kCodecVP8;
|
const Codec kCodec = kCodecVP8;
|
||||||
const std::string kCodecString = "vp8";
|
const std::string kCodecString = "vp8";
|
||||||
const std::string kLanguage = "en";
|
const std::string kLanguage = "en";
|
||||||
const uint16_t kWidth = 100;
|
const uint16_t kWidth = 100;
|
||||||
|
@ -92,10 +92,10 @@ MuxerOptions SegmentTestBase::CreateMuxerOptions() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoStreamInfo* SegmentTestBase::CreateVideoStreamInfo() const {
|
VideoStreamInfo* SegmentTestBase::CreateVideoStreamInfo() const {
|
||||||
return new VideoStreamInfo(kTrackId, kTimeScale, kDuration, kVideoCodec,
|
return new VideoStreamInfo(kTrackId, kTimeScale, kDuration, kCodec,
|
||||||
kCodecString, kLanguage, kWidth, kHeight,
|
kCodecString, NULL, 0, kWidth, kHeight,
|
||||||
kPixelWidth, kPixelHeight, kTrickPlayRate,
|
kPixelWidth, kPixelHeight, kTrickPlayRate,
|
||||||
kNaluLengthSize, NULL, 0, false);
|
kNaluLengthSize, kLanguage, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SegmentTestBase::OutputFileName() const {
|
std::string SegmentTestBase::OutputFileName() const {
|
||||||
|
@ -200,4 +200,3 @@ bool SegmentTestBase::ClusterParser::OnString(int id, const std::string& str) {
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,7 @@
|
||||||
],
|
],
|
||||||
'dependencies': [
|
'dependencies': [
|
||||||
'../../../third_party/boringssl/boringssl.gyp:boringssl',
|
'../../../third_party/boringssl/boringssl.gyp:boringssl',
|
||||||
|
'../../../third_party/gflags/gflags.gyp:gflags',
|
||||||
'../../../third_party/libwebm/libwebm.gyp:mkvmuxer',
|
'../../../third_party/libwebm/libwebm.gyp:mkvmuxer',
|
||||||
'../../base/media_base.gyp:media_base',
|
'../../base/media_base.gyp:media_base',
|
||||||
'../../codecs/codecs.gyp:codecs'
|
'../../codecs/codecs.gyp:codecs'
|
||||||
|
|
|
@ -36,7 +36,7 @@ scoped_refptr<AudioStreamInfo> WebMAudioClient::GetAudioStreamInfo(
|
||||||
int64_t codec_delay,
|
int64_t codec_delay,
|
||||||
const std::string& language,
|
const std::string& language,
|
||||||
bool is_encrypted) {
|
bool is_encrypted) {
|
||||||
AudioCodec audio_codec = kUnknownAudioCodec;
|
Codec audio_codec = kUnknownCodec;
|
||||||
if (codec_id == "A_VORBIS") {
|
if (codec_id == "A_VORBIS") {
|
||||||
audio_codec = kCodecVorbis;
|
audio_codec = kCodecVorbis;
|
||||||
} else if (codec_id == "A_OPUS") {
|
} else if (codec_id == "A_OPUS") {
|
||||||
|
@ -70,10 +70,10 @@ scoped_refptr<AudioStreamInfo> WebMAudioClient::GetAudioStreamInfo(
|
||||||
const uint8_t kSampleSizeInBits = 16u;
|
const uint8_t kSampleSizeInBits = 16u;
|
||||||
return scoped_refptr<AudioStreamInfo>(new AudioStreamInfo(
|
return scoped_refptr<AudioStreamInfo>(new AudioStreamInfo(
|
||||||
track_num, kWebMTimeScale, 0, audio_codec,
|
track_num, kWebMTimeScale, 0, audio_codec,
|
||||||
AudioStreamInfo::GetCodecString(audio_codec, 0), language,
|
AudioStreamInfo::GetCodecString(audio_codec, 0), codec_config,
|
||||||
kSampleSizeInBits, channels_, sampling_frequency,
|
codec_config_size, kSampleSizeInBits, channels_, sampling_frequency,
|
||||||
seek_preroll < 0 ? 0 : seek_preroll, codec_delay < 0 ? 0 : codec_delay, 0,
|
seek_preroll < 0 ? 0 : seek_preroll, codec_delay < 0 ? 0 : codec_delay, 0,
|
||||||
0, codec_config, codec_config_size, is_encrypted));
|
0, language, is_encrypted));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebMAudioClient::OnUInt(int id, int64_t val) {
|
bool WebMAudioClient::OnUInt(int id, int64_t val) {
|
||||||
|
|
|
@ -27,10 +27,7 @@ const int64_t kMicrosecondsPerMillisecond = 1000;
|
||||||
// block is a keyframe.
|
// block is a keyframe.
|
||||||
// |data| contains the bytes in the block.
|
// |data| contains the bytes in the block.
|
||||||
// |size| indicates the number of bytes in |data|.
|
// |size| indicates the number of bytes in |data|.
|
||||||
bool IsKeyframe(bool is_video,
|
bool IsKeyframe(bool is_video, Codec codec, const uint8_t* data, int size) {
|
||||||
VideoCodec codec,
|
|
||||||
const uint8_t* data,
|
|
||||||
int size) {
|
|
||||||
// For now, assume that all blocks are keyframes for datatypes other than
|
// For now, assume that all blocks are keyframes for datatypes other than
|
||||||
// video. This is a valid assumption for Vorbis, WebVTT, & Opus.
|
// video. This is a valid assumption for Vorbis, WebVTT, & Opus.
|
||||||
if (!is_video)
|
if (!is_video)
|
||||||
|
@ -370,7 +367,7 @@ bool WebMClusterParser::OnBlock(bool is_simple_block,
|
||||||
? (flags & 0x80) != 0
|
? (flags & 0x80) != 0
|
||||||
: IsKeyframe(stream_type == kStreamVideo,
|
: IsKeyframe(stream_type == kStreamVideo,
|
||||||
video_stream_info_ ? video_stream_info_->codec()
|
video_stream_info_ ? video_stream_info_->codec()
|
||||||
: kUnknownVideoCodec,
|
: kUnknownCodec,
|
||||||
data, size);
|
data, size);
|
||||||
|
|
||||||
// Every encrypted Block has a signal byte and IV prepended to it. Current
|
// Every encrypted Block has a signal byte and IV prepended to it. Current
|
||||||
|
@ -390,8 +387,7 @@ bool WebMClusterParser::OnBlock(bool is_simple_block,
|
||||||
buffer = MediaSample::CopyFrom(data + data_offset, size - data_offset,
|
buffer = MediaSample::CopyFrom(data + data_offset, size - data_offset,
|
||||||
additional, additional_size, is_keyframe);
|
additional, additional_size, is_keyframe);
|
||||||
|
|
||||||
// An empty iv indicates that this sample is not encrypted.
|
if (decrypt_config) {
|
||||||
if (decrypt_config && !decrypt_config->iv().empty()) {
|
|
||||||
if (!decryptor_source_) {
|
if (!decryptor_source_) {
|
||||||
LOG(ERROR) << "Encrypted media sample encountered, but decryption is "
|
LOG(ERROR) << "Encrypted media sample encountered, but decryption is "
|
||||||
"not enabled";
|
"not enabled";
|
||||||
|
|
|
@ -84,6 +84,7 @@ const uint8_t kNumChannels = 2u;
|
||||||
const uint32_t kSamplingFrequency = 48000u;
|
const uint32_t kSamplingFrequency = 48000u;
|
||||||
const uint64_t kSeekPreroll = 0u;
|
const uint64_t kSeekPreroll = 0u;
|
||||||
const uint64_t kCodecDelay = 0u;
|
const uint64_t kCodecDelay = 0u;
|
||||||
|
const uint8_t* kExtraData = nullptr;
|
||||||
const size_t kExtraDataSize = 0u;
|
const size_t kExtraDataSize = 0u;
|
||||||
const bool kEncrypted = true;
|
const bool kEncrypted = true;
|
||||||
const uint16_t kWidth = 320u;
|
const uint16_t kWidth = 320u;
|
||||||
|
@ -316,36 +317,15 @@ bool VerifyTextBuffers(const BlockInfo* block_info_ptr,
|
||||||
class WebMClusterParserTest : public testing::Test {
|
class WebMClusterParserTest : public testing::Test {
|
||||||
public:
|
public:
|
||||||
WebMClusterParserTest()
|
WebMClusterParserTest()
|
||||||
: audio_stream_info_(new AudioStreamInfo(kAudioTrackNum,
|
: audio_stream_info_(new AudioStreamInfo(
|
||||||
kTimeScale,
|
kAudioTrackNum, kTimeScale, kDuration, kUnknownCodec, kCodecString,
|
||||||
kDuration,
|
kExtraData, kExtraDataSize, kBitsPerSample, kNumChannels,
|
||||||
kUnknownAudioCodec,
|
kSamplingFrequency, kSeekPreroll, kCodecDelay, 0, 0, kLanguage,
|
||||||
kCodecString,
|
|
||||||
kLanguage,
|
|
||||||
kBitsPerSample,
|
|
||||||
kNumChannels,
|
|
||||||
kSamplingFrequency,
|
|
||||||
kSeekPreroll,
|
|
||||||
kCodecDelay,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
NULL,
|
|
||||||
kExtraDataSize,
|
|
||||||
!kEncrypted)),
|
!kEncrypted)),
|
||||||
video_stream_info_(new VideoStreamInfo(kVideoTrackNum,
|
video_stream_info_(new VideoStreamInfo(
|
||||||
kTimeScale,
|
kVideoTrackNum, kTimeScale, kDuration, kCodecVP8, kCodecString,
|
||||||
kDuration,
|
kExtraData, kExtraDataSize, kWidth, kHeight, kPixelWidth,
|
||||||
kCodecVP8,
|
kPixelHeight, kTrickPlayRate, kNaluLengthSize, kLanguage,
|
||||||
kCodecString,
|
|
||||||
kLanguage,
|
|
||||||
kWidth,
|
|
||||||
kHeight,
|
|
||||||
kPixelWidth,
|
|
||||||
kPixelHeight,
|
|
||||||
kTrickPlayRate,
|
|
||||||
kNaluLengthSize,
|
|
||||||
NULL,
|
|
||||||
kExtraDataSize,
|
|
||||||
!kEncrypted)),
|
!kEncrypted)),
|
||||||
parser_(CreateDefaultParser()) {}
|
parser_(CreateDefaultParser()) {}
|
||||||
|
|
||||||
|
@ -390,15 +370,12 @@ class WebMClusterParserTest : public testing::Test {
|
||||||
|
|
||||||
// Helper that hard-codes some non-varying constructor parameters.
|
// Helper that hard-codes some non-varying constructor parameters.
|
||||||
WebMClusterParser* CreateParserHelper(
|
WebMClusterParser* CreateParserHelper(
|
||||||
int64_t audio_default_duration,
|
int64_t audio_default_duration, int64_t video_default_duration,
|
||||||
int64_t video_default_duration,
|
|
||||||
const WebMTracksParser::TextTracks& text_tracks,
|
const WebMTracksParser::TextTracks& text_tracks,
|
||||||
const std::set<int64_t>& ignored_tracks,
|
const std::set<int64_t>& ignored_tracks,
|
||||||
const std::string& audio_encryption_key_id,
|
const std::string& audio_encryption_key_id,
|
||||||
const std::string& video_encryption_key_id,
|
const std::string& video_encryption_key_id, const Codec audio_codec,
|
||||||
const AudioCodec audio_codec,
|
const Codec video_codec, const MediaParser::InitCB& init_cb) {
|
||||||
const VideoCodec video_codec,
|
|
||||||
const MediaParser::InitCB& init_cb) {
|
|
||||||
audio_stream_info_->set_codec(audio_codec);
|
audio_stream_info_->set_codec(audio_codec);
|
||||||
video_stream_info_->set_codec(video_codec);
|
video_stream_info_->set_codec(video_codec);
|
||||||
return new WebMClusterParser(
|
return new WebMClusterParser(
|
||||||
|
@ -414,8 +391,7 @@ class WebMClusterParserTest : public testing::Test {
|
||||||
WebMClusterParser* CreateDefaultParser() {
|
WebMClusterParser* CreateDefaultParser() {
|
||||||
return CreateParserHelper(kNoTimestamp, kNoTimestamp, TextTracks(),
|
return CreateParserHelper(kNoTimestamp, kNoTimestamp, TextTracks(),
|
||||||
std::set<int64_t>(), std::string(), std::string(),
|
std::set<int64_t>(), std::string(), std::string(),
|
||||||
kUnknownAudioCodec, kCodecVP8,
|
kUnknownCodec, kCodecVP8, MediaParser::InitCB());
|
||||||
MediaParser::InitCB());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a parser for test with custom audio and video default durations, and
|
// Create a parser for test with custom audio and video default durations, and
|
||||||
|
@ -426,23 +402,22 @@ class WebMClusterParserTest : public testing::Test {
|
||||||
const WebMTracksParser::TextTracks& text_tracks = TextTracks()) {
|
const WebMTracksParser::TextTracks& text_tracks = TextTracks()) {
|
||||||
return CreateParserHelper(audio_default_duration, video_default_duration,
|
return CreateParserHelper(audio_default_duration, video_default_duration,
|
||||||
text_tracks, std::set<int64_t>(), std::string(),
|
text_tracks, std::set<int64_t>(), std::string(),
|
||||||
std::string(), kUnknownAudioCodec, kCodecVP8,
|
std::string(), kUnknownCodec, kCodecVP8,
|
||||||
MediaParser::InitCB());
|
MediaParser::InitCB());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a parser for test with custom ignored tracks.
|
// Create a parser for test with custom ignored tracks.
|
||||||
WebMClusterParser* CreateParserWithIgnoredTracks(
|
WebMClusterParser* CreateParserWithIgnoredTracks(
|
||||||
std::set<int64_t>& ignored_tracks) {
|
std::set<int64_t>& ignored_tracks) {
|
||||||
return CreateParserHelper(
|
return CreateParserHelper(kNoTimestamp, kNoTimestamp, TextTracks(),
|
||||||
kNoTimestamp, kNoTimestamp, TextTracks(), ignored_tracks, std::string(),
|
ignored_tracks, std::string(), std::string(),
|
||||||
std::string(), kUnknownAudioCodec, kCodecVP8, MediaParser::InitCB());
|
kUnknownCodec, kCodecVP8, MediaParser::InitCB());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a parser for test with custom encryption key ids and audio codec.
|
// Create a parser for test with custom encryption key ids and audio codec.
|
||||||
WebMClusterParser* CreateParserWithKeyIdsAndAudioCodec(
|
WebMClusterParser* CreateParserWithKeyIdsAndCodec(
|
||||||
const std::string& audio_encryption_key_id,
|
const std::string& audio_encryption_key_id,
|
||||||
const std::string& video_encryption_key_id,
|
const std::string& video_encryption_key_id, const Codec audio_codec) {
|
||||||
const AudioCodec audio_codec) {
|
|
||||||
return CreateParserHelper(kNoTimestamp, kNoTimestamp, TextTracks(),
|
return CreateParserHelper(kNoTimestamp, kNoTimestamp, TextTracks(),
|
||||||
std::set<int64_t>(), audio_encryption_key_id,
|
std::set<int64_t>(), audio_encryption_key_id,
|
||||||
video_encryption_key_id, audio_codec, kCodecVP8,
|
video_encryption_key_id, audio_codec, kCodecVP8,
|
||||||
|
@ -451,10 +426,10 @@ class WebMClusterParserTest : public testing::Test {
|
||||||
|
|
||||||
// Create a parser for test with custom video codec, also check for init
|
// Create a parser for test with custom video codec, also check for init
|
||||||
// events.
|
// events.
|
||||||
WebMClusterParser* CreateParserWithVideoCodec(const VideoCodec video_codec) {
|
WebMClusterParser* CreateParserWithCodec(const Codec video_codec) {
|
||||||
return CreateParserHelper(
|
return CreateParserHelper(
|
||||||
kNoTimestamp, kNoTimestamp, TextTracks(), std::set<int64_t>(),
|
kNoTimestamp, kNoTimestamp, TextTracks(), std::set<int64_t>(),
|
||||||
std::string(), std::string(), kUnknownAudioCodec, video_codec,
|
std::string(), std::string(), kUnknownCodec, video_codec,
|
||||||
base::Bind(&WebMClusterParserTest::InitEvent, base::Unretained(this)));
|
base::Bind(&WebMClusterParserTest::InitEvent, base::Unretained(this)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -807,7 +782,7 @@ TEST_F(WebMClusterParserTest, ParseMultipleTextTracks) {
|
||||||
|
|
||||||
TEST_F(WebMClusterParserTest, ParseVP8) {
|
TEST_F(WebMClusterParserTest, ParseVP8) {
|
||||||
scoped_ptr<Cluster> cluster(CreateCluster(kVP8Frame, arraysize(kVP8Frame)));
|
scoped_ptr<Cluster> cluster(CreateCluster(kVP8Frame, arraysize(kVP8Frame)));
|
||||||
parser_.reset(CreateParserWithVideoCodec(kCodecVP8));
|
parser_.reset(CreateParserWithCodec(kCodecVP8));
|
||||||
|
|
||||||
EXPECT_EQ(cluster->size(), parser_->Parse(cluster->data(), cluster->size()));
|
EXPECT_EQ(cluster->size(), parser_->Parse(cluster->data(), cluster->size()));
|
||||||
|
|
||||||
|
@ -820,7 +795,7 @@ TEST_F(WebMClusterParserTest, ParseVP8) {
|
||||||
|
|
||||||
TEST_F(WebMClusterParserTest, ParseVP9) {
|
TEST_F(WebMClusterParserTest, ParseVP9) {
|
||||||
scoped_ptr<Cluster> cluster(CreateCluster(kVP9Frame, arraysize(kVP9Frame)));
|
scoped_ptr<Cluster> cluster(CreateCluster(kVP9Frame, arraysize(kVP9Frame)));
|
||||||
parser_.reset(CreateParserWithVideoCodec(kCodecVP9));
|
parser_.reset(CreateParserWithCodec(kCodecVP9));
|
||||||
|
|
||||||
EXPECT_EQ(cluster->size(), parser_->Parse(cluster->data(), cluster->size()));
|
EXPECT_EQ(cluster->size(), parser_->Parse(cluster->data(), cluster->size()));
|
||||||
|
|
||||||
|
@ -844,8 +819,8 @@ TEST_F(WebMClusterParserTest, ParseEncryptedBlock) {
|
||||||
scoped_ptr<Cluster> cluster(
|
scoped_ptr<Cluster> cluster(
|
||||||
CreateCluster(kEncryptedFrame, arraysize(kEncryptedFrame)));
|
CreateCluster(kEncryptedFrame, arraysize(kEncryptedFrame)));
|
||||||
|
|
||||||
parser_.reset(CreateParserWithKeyIdsAndAudioCodec(std::string(), video_key_id,
|
parser_.reset(CreateParserWithKeyIdsAndCodec(std::string(), video_key_id,
|
||||||
kUnknownAudioCodec));
|
kUnknownCodec));
|
||||||
|
|
||||||
int result = parser_->Parse(cluster->data(), cluster->size());
|
int result = parser_->Parse(cluster->data(), cluster->size());
|
||||||
EXPECT_EQ(cluster->size(), result);
|
EXPECT_EQ(cluster->size(), result);
|
||||||
|
@ -865,8 +840,8 @@ TEST_F(WebMClusterParserTest, ParseEncryptedBlockGetKeyFailed) {
|
||||||
scoped_ptr<Cluster> cluster(
|
scoped_ptr<Cluster> cluster(
|
||||||
CreateCluster(kEncryptedFrame, arraysize(kEncryptedFrame)));
|
CreateCluster(kEncryptedFrame, arraysize(kEncryptedFrame)));
|
||||||
|
|
||||||
parser_.reset(CreateParserWithKeyIdsAndAudioCodec(
|
parser_.reset(CreateParserWithKeyIdsAndCodec(std::string(), "video_key_id",
|
||||||
std::string(), "video_key_id", kUnknownAudioCodec));
|
kUnknownCodec));
|
||||||
|
|
||||||
int result = parser_->Parse(cluster->data(), cluster->size());
|
int result = parser_->Parse(cluster->data(), cluster->size());
|
||||||
EXPECT_EQ(-1, result);
|
EXPECT_EQ(-1, result);
|
||||||
|
@ -876,8 +851,8 @@ TEST_F(WebMClusterParserTest, ParseBadEncryptedBlock) {
|
||||||
scoped_ptr<Cluster> cluster(
|
scoped_ptr<Cluster> cluster(
|
||||||
CreateCluster(kEncryptedFrame, arraysize(kEncryptedFrame) - 2));
|
CreateCluster(kEncryptedFrame, arraysize(kEncryptedFrame) - 2));
|
||||||
|
|
||||||
parser_.reset(CreateParserWithKeyIdsAndAudioCodec(
|
parser_.reset(CreateParserWithKeyIdsAndCodec(std::string(), "video_key_id",
|
||||||
std::string(), "video_key_id", kUnknownAudioCodec));
|
kUnknownCodec));
|
||||||
int result = parser_->Parse(cluster->data(), cluster->size());
|
int result = parser_->Parse(cluster->data(), cluster->size());
|
||||||
EXPECT_EQ(-1, result);
|
EXPECT_EQ(-1, result);
|
||||||
}
|
}
|
||||||
|
@ -886,8 +861,8 @@ TEST_F(WebMClusterParserTest, ParseClearFrameInEncryptedTrack) {
|
||||||
scoped_ptr<Cluster> cluster(CreateCluster(
|
scoped_ptr<Cluster> cluster(CreateCluster(
|
||||||
kClearFrameInEncryptedTrack, arraysize(kClearFrameInEncryptedTrack)));
|
kClearFrameInEncryptedTrack, arraysize(kClearFrameInEncryptedTrack)));
|
||||||
|
|
||||||
parser_.reset(CreateParserWithKeyIdsAndAudioCodec(
|
parser_.reset(CreateParserWithKeyIdsAndCodec(std::string(), "video_key_id",
|
||||||
std::string(), "video_key_id", kUnknownAudioCodec));
|
kUnknownCodec));
|
||||||
|
|
||||||
int result = parser_->Parse(cluster->data(), cluster->size());
|
int result = parser_->Parse(cluster->data(), cluster->size());
|
||||||
EXPECT_EQ(cluster->size(), result);
|
EXPECT_EQ(cluster->size(), result);
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#ifndef MEDIA_FORMATS_WEBM_WEBM_CONSTANTS_H_
|
#ifndef MEDIA_FORMATS_WEBM_WEBM_CONSTANTS_H_
|
||||||
#define MEDIA_FORMATS_WEBM_WEBM_CONSTANTS_H_
|
#define MEDIA_FORMATS_WEBM_WEBM_CONSTANTS_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
|
@ -207,9 +208,13 @@ const uint8_t kWebMFlagKeyframe = 0x80;
|
||||||
|
|
||||||
/// Current encrypted WebM request for comments specification is here
|
/// Current encrypted WebM request for comments specification is here
|
||||||
/// http://wiki.webmproject.org/encryption/webm-encryption-rfc
|
/// http://wiki.webmproject.org/encryption/webm-encryption-rfc
|
||||||
const uint8_t kWebMFlagEncryptedFrame = 0x1;
|
const size_t kWebMIvSize = 8;
|
||||||
const int kWebMIvSize = 8;
|
const size_t kWebMSignalByteSize = 1;
|
||||||
const int kWebMSignalByteSize = 1;
|
const uint8_t kWebMEncryptedSignal = 0x01;
|
||||||
|
const uint8_t kWebMPartitionedSignal = 0x02;
|
||||||
|
const size_t kWebMNumPartitionsSize = 1;
|
||||||
|
const size_t kWebMPartitionOffsetSize = sizeof(uint32_t);
|
||||||
|
const uint8_t kWebMMaxSubsamples = 127;
|
||||||
|
|
||||||
/// Current specification for WebVTT embedded in WebM
|
/// Current specification for WebVTT embedded in WebM
|
||||||
/// http://wiki.webmproject.org/webm-metadata/temporal-metadata/webvtt-in-webm
|
/// http://wiki.webmproject.org/webm-metadata/temporal-metadata/webvtt-in-webm
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include "packager/base/logging.h"
|
#include "packager/base/logging.h"
|
||||||
#include "packager/base/sys_byteorder.h"
|
#include "packager/base/sys_byteorder.h"
|
||||||
|
#include "packager/media/base/buffer_reader.h"
|
||||||
#include "packager/media/formats/webm/webm_constants.h"
|
#include "packager/media/formats/webm/webm_constants.h"
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
|
@ -16,48 +17,96 @@ namespace {
|
||||||
// CTR IV appended with a CTR block counter. |iv| is an 8 byte CTR IV.
|
// CTR IV appended with a CTR block counter. |iv| is an 8 byte CTR IV.
|
||||||
// |iv_size| is the size of |iv| in btyes. Returns a string of
|
// |iv_size| is the size of |iv| in btyes. Returns a string of
|
||||||
// kDecryptionKeySize bytes.
|
// kDecryptionKeySize bytes.
|
||||||
std::string GenerateWebMCounterBlock(const uint8_t* iv, int iv_size) {
|
std::vector<uint8_t> GenerateWebMCounterBlock(const uint8_t* iv, int iv_size) {
|
||||||
std::string counter_block(reinterpret_cast<const char*>(iv), iv_size);
|
std::vector<uint8_t> counter_block(iv, iv + iv_size);
|
||||||
counter_block.append(DecryptConfig::kDecryptionKeySize - iv_size, 0);
|
counter_block.insert(counter_block.end(),
|
||||||
|
DecryptConfig::kDecryptionKeySize - iv_size, 0);
|
||||||
return counter_block;
|
return counter_block;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace anonymous
|
} // namespace anonymous
|
||||||
|
|
||||||
|
// TODO(tinskip): Add unit test for this function.
|
||||||
bool WebMCreateDecryptConfig(const uint8_t* data,
|
bool WebMCreateDecryptConfig(const uint8_t* data,
|
||||||
int data_size,
|
int data_size,
|
||||||
const uint8_t* key_id,
|
const uint8_t* key_id,
|
||||||
int key_id_size,
|
int key_id_size,
|
||||||
scoped_ptr<DecryptConfig>* decrypt_config,
|
scoped_ptr<DecryptConfig>* decrypt_config,
|
||||||
int* data_offset) {
|
int* data_offset) {
|
||||||
if (data_size < kWebMSignalByteSize) {
|
int header_size = kWebMSignalByteSize;
|
||||||
DVLOG(1) << "Got a block from an encrypted stream with no data.";
|
if (data_size < header_size) {
|
||||||
|
DVLOG(1) << "Empty WebM sample.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t signal_byte = data[0];
|
uint8_t signal_byte = data[0];
|
||||||
int frame_offset = sizeof(signal_byte);
|
|
||||||
|
|
||||||
// Setting the DecryptConfig object of the buffer while leaving the
|
if (signal_byte & kWebMEncryptedSignal) {
|
||||||
// initialization vector empty will tell the decryptor that the frame is
|
// Encrypted sample.
|
||||||
// unencrypted.
|
header_size += kWebMIvSize;
|
||||||
std::string counter_block;
|
if (data_size < header_size) {
|
||||||
|
DVLOG(1) << "Encrypted WebM sample too small to hold IV: " << data_size;
|
||||||
if (signal_byte & kWebMFlagEncryptedFrame) {
|
|
||||||
if (data_size < kWebMSignalByteSize + kWebMIvSize) {
|
|
||||||
DVLOG(1) << "Got an encrypted block with not enough data " << data_size;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
counter_block = GenerateWebMCounterBlock(data + frame_offset, kWebMIvSize);
|
std::vector<SubsampleEntry> subsamples;
|
||||||
frame_offset += kWebMIvSize;
|
if (signal_byte & kWebMPartitionedSignal) {
|
||||||
|
// Encrypted sample with subsamples / partitioning.
|
||||||
|
header_size += kWebMNumPartitionsSize;
|
||||||
|
if (data_size < header_size) {
|
||||||
|
DVLOG(1)
|
||||||
|
<< "Encrypted WebM sample too small to hold number of partitions: "
|
||||||
|
<< data_size;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint8_t num_partitions = data[kWebMSignalByteSize + kWebMIvSize];
|
||||||
|
BufferReader offsets_buffer(data + header_size, data_size - header_size);
|
||||||
|
header_size += num_partitions * kWebMPartitionOffsetSize;
|
||||||
|
uint32_t subsample_offset = 0;
|
||||||
|
bool encrypted_subsample = false;
|
||||||
|
uint16_t clear_size = 0;
|
||||||
|
uint32_t encrypted_size = 0;
|
||||||
|
for (uint8_t partition_idx = 0; partition_idx < num_partitions;
|
||||||
|
++partition_idx) {
|
||||||
|
uint32_t partition_offset;
|
||||||
|
if (!offsets_buffer.Read4(&partition_offset)) {
|
||||||
|
DVLOG(1)
|
||||||
|
<< "Encrypted WebM sample too small to hold partition offsets: "
|
||||||
|
<< data_size;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (partition_offset < subsample_offset) {
|
||||||
|
DVLOG(1) << "Partition offsets out of order.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (encrypted_subsample) {
|
||||||
|
encrypted_size = partition_offset - subsample_offset;
|
||||||
|
subsamples.push_back(SubsampleEntry(clear_size, encrypted_size));
|
||||||
|
} else {
|
||||||
|
clear_size = partition_offset - subsample_offset;
|
||||||
|
if (partition_idx == (num_partitions - 1)) {
|
||||||
|
encrypted_size = data_size - header_size - subsample_offset - clear_size;
|
||||||
|
subsamples.push_back(SubsampleEntry(clear_size, encrypted_size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subsample_offset = partition_offset;
|
||||||
|
encrypted_subsample = !encrypted_subsample;
|
||||||
|
}
|
||||||
|
if (!(num_partitions % 2)) {
|
||||||
|
// Even number of partitions. Add one last all-clear subsample.
|
||||||
|
clear_size = data_size - header_size - subsample_offset;
|
||||||
|
encrypted_size = 0;
|
||||||
|
subsamples.push_back(SubsampleEntry(clear_size, encrypted_size));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
decrypt_config->reset(new DecryptConfig(
|
decrypt_config->reset(new DecryptConfig(
|
||||||
std::vector<uint8_t>(key_id, key_id + key_id_size),
|
std::vector<uint8_t>(key_id, key_id + key_id_size),
|
||||||
std::vector<uint8_t>(counter_block.begin(), counter_block.end()),
|
GenerateWebMCounterBlock(data + kWebMSignalByteSize, kWebMIvSize),
|
||||||
std::vector<SubsampleEntry>()));
|
subsamples));
|
||||||
*data_offset = frame_offset;
|
} else {
|
||||||
|
// Clear sample.
|
||||||
|
decrypt_config->reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
*data_offset = header_size;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@ scoped_refptr<VideoStreamInfo> WebMVideoClient::GetVideoStreamInfo(
|
||||||
const std::string& codec_id,
|
const std::string& codec_id,
|
||||||
const std::vector<uint8_t>& codec_private,
|
const std::vector<uint8_t>& codec_private,
|
||||||
bool is_encrypted) {
|
bool is_encrypted) {
|
||||||
VideoCodec video_codec = kUnknownVideoCodec;
|
Codec video_codec = kUnknownCodec;
|
||||||
if (codec_id == "V_VP8") {
|
if (codec_id == "V_VP8") {
|
||||||
video_codec = kCodecVP8;
|
video_codec = kCodecVP8;
|
||||||
} else if (codec_id == "V_VP9") {
|
} else if (codec_id == "V_VP9") {
|
||||||
|
@ -110,9 +110,9 @@ scoped_refptr<VideoStreamInfo> WebMVideoClient::GetVideoStreamInfo(
|
||||||
sar_y /= gcd;
|
sar_y /= gcd;
|
||||||
|
|
||||||
return scoped_refptr<VideoStreamInfo>(new VideoStreamInfo(
|
return scoped_refptr<VideoStreamInfo>(new VideoStreamInfo(
|
||||||
track_num, kWebMTimeScale, 0, video_codec, std::string(), std::string(),
|
track_num, kWebMTimeScale, 0, video_codec, std::string(),
|
||||||
width_after_crop, height_after_crop, sar_x, sar_y, 0, 0,
|
codec_private.data(), codec_private.size(), width_after_crop,
|
||||||
codec_private.data(), codec_private.size(), is_encrypted));
|
height_after_crop, sar_x, sar_y, 0, 0, std::string(), is_encrypted));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebMVideoClient::OnUInt(int id, int64_t val) {
|
bool WebMVideoClient::OnUInt(int id, int64_t val) {
|
||||||
|
|
|
@ -294,15 +294,12 @@ bool WebVttMediaParser::Parse(const uint8_t* buf, int size) {
|
||||||
// There is no one metadata to determine what the language is. Parts
|
// There is no one metadata to determine what the language is. Parts
|
||||||
// of the text may be annotated as some specific language.
|
// of the text may be annotated as some specific language.
|
||||||
const char kLanguage[] = "";
|
const char kLanguage[] = "";
|
||||||
streams.push_back(new TextStreamInfo(
|
streams.push_back(new TextStreamInfo(kTrackId, kTimescale, kDuration,
|
||||||
kTrackId,
|
|
||||||
kTimescale,
|
|
||||||
kDuration,
|
|
||||||
"wvtt",
|
"wvtt",
|
||||||
kLanguage,
|
|
||||||
base::JoinString(header_, "\n"),
|
base::JoinString(header_, "\n"),
|
||||||
0, // Not necessary.
|
0, // Not necessary.
|
||||||
0)); // Not necessary.
|
0,
|
||||||
|
kLanguage)); // Not necessary.
|
||||||
|
|
||||||
init_cb_.Run(streams);
|
init_cb_.Run(streams);
|
||||||
state_ = kCueIdentifierOrTimingOrComment;
|
state_ = kCueIdentifierOrTimingOrComment;
|
||||||
|
|
|
@ -739,31 +739,33 @@ bool WvmMediaParser::ParseIndexEntry() {
|
||||||
index_size = read_ptr - index_data_.data();
|
index_size = read_ptr - index_data_.data();
|
||||||
|
|
||||||
if (has_video) {
|
if (has_video) {
|
||||||
VideoCodec video_codec = kCodecH264;
|
Codec video_codec = kCodecH264;
|
||||||
stream_infos_.push_back(new VideoStreamInfo(
|
stream_infos_.push_back(new VideoStreamInfo(
|
||||||
stream_id_count_, time_scale, track_duration, video_codec,
|
stream_id_count_, time_scale, track_duration, video_codec,
|
||||||
std::string(), std::string(), video_width, video_height, pixel_width,
|
std::string(), video_codec_config.data(), video_codec_config.size(),
|
||||||
pixel_height, trick_play_rate, nalu_length_size,
|
video_width, video_height, pixel_width, pixel_height, trick_play_rate,
|
||||||
video_codec_config.data(), video_codec_config.size(), true));
|
nalu_length_size, std::string(), true));
|
||||||
program_demux_stream_map_[base::UintToString(index_program_id_) + ":" +
|
program_demux_stream_map_[base::UintToString(index_program_id_) + ":" +
|
||||||
base::UintToString(video_pes_stream_id ?
|
base::UintToString(
|
||||||
video_pes_stream_id :
|
video_pes_stream_id
|
||||||
kDefaultVideoStreamId)] =
|
? video_pes_stream_id
|
||||||
|
: kDefaultVideoStreamId)] =
|
||||||
stream_id_count_++;
|
stream_id_count_++;
|
||||||
}
|
}
|
||||||
if (has_audio) {
|
if (has_audio) {
|
||||||
const AudioCodec audio_codec = kCodecAAC;
|
const Codec audio_codec = kCodecAAC;
|
||||||
// TODO(beil): Pass in max and average bitrate in wvm container.
|
// TODO(beil): Pass in max and average bitrate in wvm container.
|
||||||
stream_infos_.push_back(new AudioStreamInfo(
|
stream_infos_.push_back(new AudioStreamInfo(
|
||||||
stream_id_count_, time_scale, track_duration, audio_codec,
|
stream_id_count_, time_scale, track_duration, audio_codec,
|
||||||
std::string(), std::string(), kAacSampleSizeBits, num_channels,
|
std::string(), audio_codec_config.data(), audio_codec_config.size(),
|
||||||
sampling_frequency, 0 /* seek preroll */, 0 /* codec delay */,
|
kAacSampleSizeBits, num_channels, sampling_frequency,
|
||||||
0 /* max bitrate */, 0 /* avg bitrate */, audio_codec_config.data(),
|
0 /* seek preroll */, 0 /* codec delay */, 0 /* max bitrate */,
|
||||||
audio_codec_config.size(), true));
|
0 /* avg bitrate */, std::string(), true));
|
||||||
program_demux_stream_map_[base::UintToString(index_program_id_) + ":" +
|
program_demux_stream_map_[base::UintToString(index_program_id_) + ":" +
|
||||||
base::UintToString(audio_pes_stream_id ?
|
base::UintToString(
|
||||||
audio_pes_stream_id :
|
audio_pes_stream_id
|
||||||
kDefaultAudioStreamId)] =
|
? audio_pes_stream_id
|
||||||
|
: kDefaultAudioStreamId)] =
|
||||||
stream_id_count_++;
|
stream_id_count_++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue