MuxerOptions and MuxerListener change for HLS

- Add hls_name and hls_group_id fields to MuxerOptions. This is used to
  pass the NAME and GROUP-ID values for EXT-X-MEDIA tag to
  MuxerListener.
- Change MuxerListener::OnEncryptionInfoReady() to take an
  initialization vector.
- Change MuxerListener::OnNewSegment() to take segment name.
- Reworded and formatted MuxerListener comments to Doxygen style.

Issue #85

Change-Id: Iea06e68552a56ae180177ffd6ca315a7cf39456c
This commit is contained in:
Rintaro Kuroiwa 2016-03-28 01:23:20 -07:00
parent 16ba8da295
commit 300c23104e
14 changed files with 122 additions and 67 deletions

View File

@ -60,6 +60,17 @@ struct MuxerOptions {
/// Optional.
std::string segment_template;
/// name of the output stream. This is not (necessarily) the same as @a
/// output_file_name. For HLS this is used as the NAME attribute for
/// EXT-X-MEDIA.
/// Required for audio when outputting HLS.
std::string hls_name;
/// The group ID for the output stream.
/// For HLS this is used as the GROUP-ID attribute for EXT-X-MEDIA.
/// Required for audio when outputting HLS.
std::string hls_group_id;
/// Specify temporary directory for intermediate files.
std::string temp_dir;

View File

@ -31,6 +31,7 @@ MpdNotifyMuxerListener::~MpdNotifyMuxerListener() {}
void MpdNotifyMuxerListener::OnEncryptionInfoReady(
bool is_initial_encryption_info,
const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& iv,
const std::vector<ProtectionSystemSpecificInfo>& key_system_info) {
if (is_initial_encryption_info) {
LOG_IF(WARNING, is_encrypted_)
@ -142,7 +143,8 @@ void MpdNotifyMuxerListener::OnMediaEnd(bool has_init_range,
mpd_notifier_->Flush();
}
void MpdNotifyMuxerListener::OnNewSegment(uint64_t start_time,
void MpdNotifyMuxerListener::OnNewSegment(const std::string& file_name,
uint64_t start_time,
uint64_t duration,
uint64_t segment_file_size) {
if (mpd_notifier_->dash_profile() == kLiveProfile) {

View File

@ -35,6 +35,7 @@ class MpdNotifyMuxerListener : public MuxerListener {
/// @{
void OnEncryptionInfoReady(bool is_initial_encryption_info,
const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& iv,
const std::vector<ProtectionSystemSpecificInfo>&
key_system_info) override;
void OnMediaStart(const MuxerOptions& muxer_options,
@ -50,7 +51,8 @@ class MpdNotifyMuxerListener : public MuxerListener {
uint64_t index_range_end,
float duration_seconds,
uint64_t file_size) override;
void OnNewSegment(uint64_t start_time,
void OnNewSegment(const std::string& file_name,
uint64_t start_time,
uint64_t duration,
uint64_t segment_file_size) override;
/// @}

View File

@ -55,6 +55,11 @@ void SetDefaultLiveMuxerOptionsValues(media::MuxerOptions* muxer_options) {
muxer_options->temp_dir.clear();
}
const uint8_t kBogusIv[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x67, 0x83, 0xC3, 0x66, 0xEE, 0xAB, 0xB2, 0xF1,
};
} // namespace
namespace media {
@ -161,8 +166,10 @@ TEST_F(MpdNotifyMuxerListenerTest, VodEncryptedContent) {
"}\n";
EXPECT_CALL(*notifier_, NotifyNewContainer(_, _)).Times(0);
listener_->OnEncryptionInfoReady(kInitialEncryptionInfo,
default_key_id, GetDefaultKeySystemInfo());
std::vector<uint8_t> iv(kBogusIv, kBogusIv + arraysize(kBogusIv));
listener_->OnEncryptionInfoReady(kInitialEncryptionInfo, default_key_id, iv,
GetDefaultKeySystemInfo());
listener_->OnMediaStart(muxer_options, *video_stream_info,
kDefaultReferenceTimeScale,
@ -245,8 +252,8 @@ TEST_F(MpdNotifyMuxerListenerTest, VodOnNewSegment) {
listener_->OnMediaStart(muxer_options, *video_stream_info,
kDefaultReferenceTimeScale,
MuxerListener::kContainerMp4);
listener_->OnNewSegment(kStartTime1, kDuration1, kSegmentFileSize1);
listener_->OnNewSegment(kStartTime2, kDuration2, kSegmentFileSize2);
listener_->OnNewSegment("", kStartTime1, kDuration1, kSegmentFileSize1);
listener_->OnNewSegment("", kStartTime2, kDuration2, kSegmentFileSize2);
::testing::Mock::VerifyAndClearExpectations(notifier_.get());
InSequence s;
@ -312,13 +319,14 @@ TEST_F(MpdNotifyMuxerListenerTest, LiveNoKeyRotation) {
NotifyNewSegment(_, kStartTime2, kDuration2, kSegmentFileSize2));
EXPECT_CALL(*notifier_, Flush());
listener_->OnEncryptionInfoReady(kInitialEncryptionInfo,
default_key_id, GetDefaultKeySystemInfo());
std::vector<uint8_t> iv(kBogusIv, kBogusIv + arraysize(kBogusIv));
listener_->OnEncryptionInfoReady(kInitialEncryptionInfo, default_key_id, iv,
GetDefaultKeySystemInfo());
listener_->OnMediaStart(muxer_options, *video_stream_info,
kDefaultReferenceTimeScale,
MuxerListener::kContainerMp4);
listener_->OnNewSegment(kStartTime1, kDuration1, kSegmentFileSize1);
listener_->OnNewSegment(kStartTime2, kDuration2, kSegmentFileSize2);
listener_->OnNewSegment("", kStartTime1, kDuration1, kSegmentFileSize1);
listener_->OnNewSegment("", kStartTime2, kDuration2, kSegmentFileSize2);
::testing::Mock::VerifyAndClearExpectations(notifier_.get());
EXPECT_CALL(*notifier_, Flush()).Times(0);
@ -374,16 +382,17 @@ TEST_F(MpdNotifyMuxerListenerTest, LiveWithKeyRotation) {
NotifyNewSegment(_, kStartTime2, kDuration2, kSegmentFileSize2));
EXPECT_CALL(*notifier_, Flush());
listener_->OnEncryptionInfoReady(kInitialEncryptionInfo, default_key_id,
std::vector<uint8_t> iv(kBogusIv, kBogusIv + arraysize(kBogusIv));
listener_->OnEncryptionInfoReady(kInitialEncryptionInfo, default_key_id, iv,
std::vector<ProtectionSystemSpecificInfo>());
listener_->OnMediaStart(muxer_options, *video_stream_info,
kDefaultReferenceTimeScale,
MuxerListener::kContainerMp4);
listener_->OnEncryptionInfoReady(kNonInitialEncryptionInfo,
std::vector<uint8_t>(),
std::vector<uint8_t>(), iv,
GetDefaultKeySystemInfo());
listener_->OnNewSegment(kStartTime1, kDuration1, kSegmentFileSize1);
listener_->OnNewSegment(kStartTime2, kDuration2, kSegmentFileSize2);
listener_->OnNewSegment("", kStartTime1, kDuration1, kSegmentFileSize1);
listener_->OnNewSegment("", kStartTime2, kDuration2, kSegmentFileSize2);
::testing::Mock::VerifyAndClearExpectations(notifier_.get());
EXPECT_CALL(*notifier_, Flush()).Times(0);

View File

@ -6,8 +6,6 @@
//
// Event handler for events fired by Muxer.
// TODO(rkuroiwa): Document using doxygen style comments.
#ifndef MEDIA_EVENT_MUXER_LISTENER_H_
#define MEDIA_EVENT_MUXER_LISTENER_H_
@ -38,31 +36,37 @@ class MuxerListener {
virtual ~MuxerListener() {};
// Called when the media's encryption information is ready. This should be
// called before OnMediaStart(), if the media is encrypted.
// All the parameters may be empty just to notify that the media is encrypted.
// |is_initial_encryption_info| is true if this is the first encryption info
// for the media.
// In general, this flag should always be true for non-key-rotated media and
// should be called only once.
// |key_id| is the key ID for the media.
// The format should be a vector of uint8_t, i.e. not (necessarily) human
// readable hex string.
// For ISO BMFF (MP4) media:
// If |is_initial_encryption_info| is true then |key_id| is the default_KID in
// 'tenc' box.
// If |is_initial_encryption_info| is false then |key_id| is the new key ID
// for the for the next crypto period.
/// Called when the media's encryption information is ready. This should be
/// called before OnMediaStart(), if the media is encrypted.
/// All the parameters may be empty just to notify that the media is
/// encrypted.
/// For ISO BMFF (MP4) media:
/// If @a is_initial_encryption_info is true then @a key_id is the default_KID
/// in 'tenc' box.
/// If @a is_initial_encryption_info is false then @a key_id is the new key ID
/// for the for the next crypto period.
/// @param is_initial_encryption_info is true if this is the first encryption
/// info for the media. In general, this flag should always be true for
/// non-key-rotated media and should be called only once.
/// @param key_id is the key ID for the media. The format should be a vector
/// of uint8_t, i.e. not (necessarily) human readable hex string.
/// @param iv is the initialization vector. For most cases this should be 16
/// bytes, but whether the input is accepted is up to the
/// implementation.
virtual void OnEncryptionInfoReady(
bool is_initial_encryption_info,
const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& iv,
const std::vector<ProtectionSystemSpecificInfo>& key_system_info) = 0;
// Called when muxing starts.
// For MPEG DASH Live profile, the initialization segment information is
// available from StreamInfo.
// |time_scale| is a reference time scale that overrides the time scale
// specified in |stream_info|.
/// Called when muxing starts.
/// For MPEG DASH Live profile, the initialization segment information is
/// available from StreamInfo.
/// @param muxer_options is the options for Muxer.
/// @param stream_info is the information of this media.
/// @param time_scale is a reference time scale that overrides the time scale
/// specified in @a stream_info.
/// @param container_type is the container of this media.
virtual void OnMediaStart(const MuxerOptions& muxer_options,
const StreamInfo& stream_info,
uint32_t time_scale,
@ -72,16 +76,21 @@ class MuxerListener {
/// @param sample_duration in timescale of the media.
virtual void OnSampleDurationReady(uint32_t sample_duration) = 0;
// Called when all files are written out and the muxer object does not output
// any more files.
// Note: This event might not be very interesting to MPEG DASH Live profile.
// |init_range_{start,end}| is the byte range of initialization segment, in
// the media file. If |has_init_range| is false, these values are ignored.
// |index_range_{start,end}| is the byte range of segment index, in the media
// file. If |has_index_range| is false, these values are ignored.
// Both ranges are inclusive.
// Media length of |duration_seconds|.
// |file_size| of the media in bytes.
/// Called when all files are written out and the muxer object does not output
/// any more files.
/// Note: This event might not be very interesting to MPEG DASH Live profile.
/// @param has_init_range is true if @a init_range_start and @a init_range_end
/// actually define an initialization range of a segment. The range is
/// inclusive for both start and end.
/// @param init_range_start is the start of the initialization range.
/// @param init_range_end is the end of the initialization range.
/// @param has_index_range is true if @a index_range_start and @a
/// index_range_end actually define an index range of a segment. The
/// range is inclusive for both start and end.
/// @param index_range_start is the start of the index range.
/// @param index_range_end is the end of the index range.
/// @param duration_seconds is the length of the media in seconds.
/// @param file_size is the size of the file in bytes.
virtual void OnMediaEnd(bool has_init_range,
uint64_t init_range_start,
uint64_t init_range_end,
@ -91,12 +100,19 @@ class MuxerListener {
float duration_seconds,
uint64_t file_size) = 0;
// Called when a segment has been muxed and the file has been written.
// Note: For video on demand (VOD), this would be for subsegments.
// |start_time| and |duration| are relative to time scale specified
// OnMediaStart().
// |segment_file_size| in bytes.
virtual void OnNewSegment(uint64_t start_time,
/// Called when a segment has been muxed and the file has been written.
/// Note: For some implementations, this is used to signal new subsegments.
/// For example, for generating video on demand (VOD) MPD manifest, this is
/// called to signal subsegments.
/// @param segment_name is the name of the new segment. Note that some
/// implementations may not require this, e.g. if this is a subsegment.
/// @param start_time is the start time of the segment, relative to the
/// timescale specified by MediaInfo passed to OnMediaStart().
/// @param duration is the duration of the segment, relative to the timescale
/// specified by MediaInfo passed to OnMediaStart().
/// @param segment_file_size is the segment size in bytes.
virtual void OnNewSegment(const std::string& segment_name,
uint64_t start_time,
uint64_t duration,
uint64_t segment_file_size) = 0;

View File

@ -28,6 +28,7 @@ VodMediaInfoDumpMuxerListener::~VodMediaInfoDumpMuxerListener() {}
void VodMediaInfoDumpMuxerListener::OnEncryptionInfoReady(
bool is_initial_encryption_info,
const std::vector<uint8_t>& default_key_id,
const std::vector<uint8_t>& iv,
const std::vector<ProtectionSystemSpecificInfo>& key_system_info) {
LOG_IF(WARNING, !is_initial_encryption_info)
<< "Updating (non initial) encryption info is not supported by "
@ -91,10 +92,10 @@ void VodMediaInfoDumpMuxerListener::OnMediaEnd(bool has_init_range,
WriteMediaInfoToFile(*media_info_, output_file_name_);
}
void VodMediaInfoDumpMuxerListener::OnNewSegment(uint64_t start_time,
void VodMediaInfoDumpMuxerListener::OnNewSegment(const std::string& file_name,
uint64_t start_time,
uint64_t duration,
uint64_t segment_file_size) {
}
uint64_t segment_file_size) {}
// static
bool VodMediaInfoDumpMuxerListener::WriteMediaInfoToFile(

View File

@ -34,6 +34,7 @@ class VodMediaInfoDumpMuxerListener : public MuxerListener {
/// @{
void OnEncryptionInfoReady(bool is_initial_encryption_info,
const std::vector<uint8_t>& default_key_id,
const std::vector<uint8_t>& iv,
const std::vector<ProtectionSystemSpecificInfo>&
key_system_info) override;
void OnMediaStart(const MuxerOptions& muxer_options,
@ -49,7 +50,8 @@ class VodMediaInfoDumpMuxerListener : public MuxerListener {
uint64_t index_range_end,
float duration_seconds,
uint64_t file_size) override;
void OnNewSegment(uint64_t start_time,
void OnNewSegment(const std::string& file_name,
uint64_t start_time,
uint64_t duration,
uint64_t segment_file_size) override;
/// @}

View File

@ -25,6 +25,11 @@ const uint8_t kBogusDefaultKeyId[] = {0x5f, 0x64, 0x65, 0x66, 0x61, 0x75,
0x6c, 0x74, 0x5f, 0x6b, 0x65, 0x79,
0x5f, 0x69, 0x64, 0x5f};
const uint8_t kBogusIv[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x67, 0x83, 0xC3, 0x66, 0xEE, 0xAB, 0xB2, 0xF1,
};
const bool kInitialEncryptionInfo = true;
} // namespace
@ -76,9 +81,10 @@ class VodMediaInfoDumpMuxerListenerTest : public ::testing::Test {
std::vector<uint8_t> bogus_default_key_id(
kBogusDefaultKeyId,
kBogusDefaultKeyId + arraysize(kBogusDefaultKeyId));
std::vector<uint8_t> bogus_iv(kBogusIv, kBogusIv + arraysize(kBogusIv));
listener_->OnEncryptionInfoReady(kInitialEncryptionInfo,
bogus_default_key_id,
bogus_default_key_id, bogus_iv,
GetDefaultKeySystemInfo());
}
listener_->OnMediaStart(muxer_options, stream_info, kReferenceTimeScale,

View File

@ -81,6 +81,7 @@ Status KeyRotationFragmenter::PrepareFragmentForEncryption(
if (muxer_listener_) {
muxer_listener_->OnEncryptionInfoReady(!kInitialEncryptionInfo,
encryption_key()->key_id,
encryption_key()->iv,
encryption_key()->key_system_info);
}

View File

@ -148,11 +148,10 @@ Status MultiSegmentSegmenter::WriteSegment() {
"Cannot open file for append " + options().output_file_name);
}
} else {
file = File::Open(GetSegmentName(options().segment_template,
sidx()->earliest_presentation_time,
num_segments_++,
options().bandwidth).c_str(),
"w");
file_name = GetSegmentName(options().segment_template,
sidx()->earliest_presentation_time,
num_segments_++, options().bandwidth);
file = File::Open(file_name.c_str(), "w");
if (file == NULL) {
return Status(error::FILE_FAILURE,
"Cannot open file for write " + file_name);
@ -186,8 +185,9 @@ Status MultiSegmentSegmenter::WriteSegment() {
UpdateProgress(segment_duration);
if (muxer_listener()) {
muxer_listener()->OnSampleDurationReady(sample_duration());
muxer_listener()->OnNewSegment(
sidx()->earliest_presentation_time, segment_duration, segment_size);
muxer_listener()->OnNewSegment(file_name,
sidx()->earliest_presentation_time,
segment_duration, segment_size);
}
return Status::OK;

View File

@ -8,6 +8,7 @@
#include <algorithm>
#include "packager/base/logging.h"
#include "packager/base/stl_util.h"
#include "packager/media/base/aes_cryptor.h"
#include "packager/media/base/buffer_writer.h"
@ -214,7 +215,7 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
local_protection_scheme, &description);
if (muxer_listener_) {
muxer_listener_->OnEncryptionInfoReady(
kInitialEncryptionInfo, encryption_key.key_id,
kInitialEncryptionInfo, encryption_key.key_id, encryption_key.iv,
encryption_key.key_system_info);
}
@ -252,6 +253,7 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
if (muxer_listener_) {
muxer_listener_->OnEncryptionInfoReady(kInitialEncryptionInfo,
encryption_key->key_id,
encryption_key->iv,
encryption_key->key_system_info);
}
}

View File

@ -226,7 +226,8 @@ Status SingleSegmentSegmenter::DoFinalizeSegment() {
UpdateProgress(vod_ref.subsegment_duration);
if (muxer_listener()) {
muxer_listener()->OnSampleDurationReady(sample_duration());
muxer_listener()->OnNewSegment(vod_ref.earliest_presentation_time,
muxer_listener()->OnNewSegment(options().output_file_name,
vod_ref.earliest_presentation_time,
vod_ref.subsegment_duration, segment_size);
}
return Status::OK;

View File

@ -120,6 +120,7 @@ Status Encryptor::CreateEncryptor(MuxerListener* muxer_listener,
const bool kInitialEncryptionInfo = true;
muxer_listener->OnEncryptionInfoReady(kInitialEncryptionInfo,
encryption_key->key_id,
encryptor->iv(),
encryption_key->key_system_info);
}

View File

@ -50,7 +50,8 @@ Status MultiSegmentSegmenter::FinalizeSegment() {
const uint64_t start_timescale = FromWebMTimecode(start_webm_timecode);
const uint64_t length = static_cast<uint64_t>(
cluster_length_sec() * info()->time_scale());
muxer_listener()->OnNewSegment(start_timescale, length, size);
muxer_listener()->OnNewSegment(writer_->file()->file_name(),
start_timescale, length, size);
}
VLOG(1) << "WEBM file '" << writer_->file()->file_name() << "' finalized.";