Add ProgressListener for progress updates
Bug: 19730025 Change-Id: If60d7629c69d84b245f0c96fb564704d7af82ea2
This commit is contained in:
parent
e9f402a328
commit
8277236fe7
|
@ -83,6 +83,11 @@ void Muxer::SetMuxerListener(scoped_ptr<MuxerListener> muxer_listener) {
|
||||||
muxer_listener_ = muxer_listener.Pass();
|
muxer_listener_ = muxer_listener.Pass();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Muxer::SetProgressListener(
|
||||||
|
scoped_ptr<ProgressListener> progress_listener) {
|
||||||
|
progress_listener_ = progress_listener.Pass();
|
||||||
|
}
|
||||||
|
|
||||||
Status Muxer::AddSample(const MediaStream* stream,
|
Status Muxer::AddSample(const MediaStream* stream,
|
||||||
scoped_refptr<MediaSample> sample) {
|
scoped_refptr<MediaSample> sample) {
|
||||||
DCHECK(std::find(streams_.begin(), streams_.end(), stream) != streams_.end());
|
DCHECK(std::find(streams_.begin(), streams_.end(), stream) != streams_.end());
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "packager/media/base/muxer_options.h"
|
#include "packager/media/base/muxer_options.h"
|
||||||
#include "packager/media/base/status.h"
|
#include "packager/media/base/status.h"
|
||||||
#include "packager/media/event/muxer_listener.h"
|
#include "packager/media/event/muxer_listener.h"
|
||||||
|
#include "packager/media/event/progress_listener.h"
|
||||||
|
|
||||||
namespace edash_packager {
|
namespace edash_packager {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
@ -62,6 +63,10 @@ class Muxer {
|
||||||
/// @param muxer_listener should not be NULL.
|
/// @param muxer_listener should not be NULL.
|
||||||
void SetMuxerListener(scoped_ptr<MuxerListener> muxer_listener);
|
void SetMuxerListener(scoped_ptr<MuxerListener> muxer_listener);
|
||||||
|
|
||||||
|
/// Set a ProgressListener event handler for this object.
|
||||||
|
/// @param progress_listener should not be NULL.
|
||||||
|
void SetProgressListener(scoped_ptr<ProgressListener> progress_listener);
|
||||||
|
|
||||||
const std::vector<MediaStream*>& streams() const { return streams_; }
|
const std::vector<MediaStream*>& streams() const { return streams_; }
|
||||||
|
|
||||||
/// Inject clock, mainly used for testing.
|
/// Inject clock, mainly used for testing.
|
||||||
|
@ -85,6 +90,7 @@ class Muxer {
|
||||||
return crypto_period_duration_in_seconds_;
|
return crypto_period_duration_in_seconds_;
|
||||||
}
|
}
|
||||||
MuxerListener* muxer_listener() { return muxer_listener_.get(); }
|
MuxerListener* muxer_listener() { return muxer_listener_.get(); }
|
||||||
|
ProgressListener* progress_listener() { return progress_listener_.get(); }
|
||||||
base::Clock* clock() { return clock_; }
|
base::Clock* clock() { return clock_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -114,6 +120,7 @@ class Muxer {
|
||||||
bool cancelled_;
|
bool cancelled_;
|
||||||
|
|
||||||
scoped_ptr<MuxerListener> muxer_listener_;
|
scoped_ptr<MuxerListener> muxer_listener_;
|
||||||
|
scoped_ptr<ProgressListener> progress_listener_;
|
||||||
// An external injected clock, can be NULL.
|
// An external injected clock, can be NULL.
|
||||||
base::Clock* clock_;
|
base::Clock* clock_;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright 2015 Google Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://developers.google.com/open-source/licenses/bsd
|
||||||
|
//
|
||||||
|
// Event handler for progress updates.
|
||||||
|
|
||||||
|
#ifndef MEDIA_EVENT_PROGRESS_LISTENER_H_
|
||||||
|
#define MEDIA_EVENT_PROGRESS_LISTENER_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "packager/base/macros.h"
|
||||||
|
|
||||||
|
namespace edash_packager {
|
||||||
|
namespace media {
|
||||||
|
|
||||||
|
/// This class listens to progress updates events.
|
||||||
|
class ProgressListener {
|
||||||
|
public:
|
||||||
|
virtual ~ProgressListener() {}
|
||||||
|
|
||||||
|
/// Called when there is a progress update.
|
||||||
|
/// @param progress is the current progress metric, ranges from 0 to 1.
|
||||||
|
virtual void OnProgress(double progress) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ProgressListener() {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(ProgressListener);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace media
|
||||||
|
} // namespace edash_packager
|
||||||
|
|
||||||
|
#endif // MEDIA_EVENT_PROGRESS_LISTENER_H_
|
|
@ -102,6 +102,7 @@ Status MP4Muxer::Initialize() {
|
||||||
Status segmenter_initialized =
|
Status segmenter_initialized =
|
||||||
segmenter_->Initialize(streams(),
|
segmenter_->Initialize(streams(),
|
||||||
muxer_listener(),
|
muxer_listener(),
|
||||||
|
progress_listener(),
|
||||||
encryption_key_source(),
|
encryption_key_source(),
|
||||||
max_sd_pixels(),
|
max_sd_pixels(),
|
||||||
clear_lead_in_seconds(),
|
clear_lead_in_seconds(),
|
||||||
|
|
|
@ -65,6 +65,7 @@ Status MultiSegmentSegmenter::DoInitialize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Status MultiSegmentSegmenter::DoFinalize() {
|
Status MultiSegmentSegmenter::DoFinalize() {
|
||||||
|
SetComplete();
|
||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,17 +174,22 @@ Status MultiSegmentSegmenter::WriteSegment() {
|
||||||
if (!file->Close())
|
if (!file->Close())
|
||||||
LOG(WARNING) << "Failed to close the file properly: " << file_name;
|
LOG(WARNING) << "Failed to close the file properly: " << file_name;
|
||||||
|
|
||||||
if (status.ok() && muxer_listener()) {
|
if (!status.ok())
|
||||||
|
return status;
|
||||||
|
|
||||||
uint64_t segment_duration = 0;
|
uint64_t segment_duration = 0;
|
||||||
// ISO/IEC 23009-1:2012: the value shall be identical to sum of the the
|
// ISO/IEC 23009-1:2012: the value shall be identical to sum of the the
|
||||||
// values of all Subsegment_duration fields in the first ‘sidx’ box.
|
// values of all Subsegment_duration fields in the first ‘sidx’ box.
|
||||||
for (size_t i = 0; i < sidx()->references.size(); ++i)
|
for (size_t i = 0; i < sidx()->references.size(); ++i)
|
||||||
segment_duration += sidx()->references[i].subsegment_duration;
|
segment_duration += sidx()->references[i].subsegment_duration;
|
||||||
|
|
||||||
|
UpdateProgress(segment_duration);
|
||||||
|
if (muxer_listener()) {
|
||||||
muxer_listener()->OnNewSegment(
|
muxer_listener()->OnNewSegment(
|
||||||
sidx()->earliest_presentation_time, segment_duration, segment_size);
|
sidx()->earliest_presentation_time, segment_duration, segment_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mp4
|
} // namespace mp4
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "packager/media/base/media_stream.h"
|
#include "packager/media/base/media_stream.h"
|
||||||
#include "packager/media/base/muxer_options.h"
|
#include "packager/media/base/muxer_options.h"
|
||||||
#include "packager/media/base/video_stream_info.h"
|
#include "packager/media/base/video_stream_info.h"
|
||||||
|
#include "packager/media/event/progress_listener.h"
|
||||||
#include "packager/media/formats/mp4/box_definitions.h"
|
#include "packager/media/formats/mp4/box_definitions.h"
|
||||||
#include "packager/media/formats/mp4/key_rotation_fragmenter.h"
|
#include "packager/media/formats/mp4/key_rotation_fragmenter.h"
|
||||||
|
|
||||||
|
@ -123,18 +124,24 @@ Segmenter::Segmenter(const MuxerOptions& options,
|
||||||
sidx_(new SegmentIndex()),
|
sidx_(new SegmentIndex()),
|
||||||
segment_initialized_(false),
|
segment_initialized_(false),
|
||||||
end_of_segment_(false),
|
end_of_segment_(false),
|
||||||
muxer_listener_(NULL) {}
|
muxer_listener_(NULL),
|
||||||
|
progress_listener_(NULL),
|
||||||
|
progress_target_(0),
|
||||||
|
accumulated_progress_(0) {
|
||||||
|
}
|
||||||
|
|
||||||
Segmenter::~Segmenter() { STLDeleteElements(&fragmenters_); }
|
Segmenter::~Segmenter() { STLDeleteElements(&fragmenters_); }
|
||||||
|
|
||||||
Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
||||||
MuxerListener* muxer_listener,
|
MuxerListener* muxer_listener,
|
||||||
|
ProgressListener* progress_listener,
|
||||||
KeySource* encryption_key_source,
|
KeySource* encryption_key_source,
|
||||||
uint32_t max_sd_pixels,
|
uint32_t max_sd_pixels,
|
||||||
double clear_lead_in_seconds,
|
double clear_lead_in_seconds,
|
||||||
double crypto_period_duration_in_seconds) {
|
double crypto_period_duration_in_seconds) {
|
||||||
DCHECK_LT(0u, streams.size());
|
DCHECK_LT(0u, streams.size());
|
||||||
muxer_listener_ = muxer_listener;
|
muxer_listener_ = muxer_listener;
|
||||||
|
progress_listener_ = progress_listener;
|
||||||
moof_->header.sequence_number = 0;
|
moof_->header.sequence_number = 0;
|
||||||
|
|
||||||
moof_->tracks.resize(streams.size());
|
moof_->tracks.resize(streams.size());
|
||||||
|
@ -202,6 +209,9 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
||||||
sidx_->reference_id = 1;
|
sidx_->reference_id = 1;
|
||||||
sidx_->timescale = streams[GetReferenceStreamId()]->info()->time_scale();
|
sidx_->timescale = streams[GetReferenceStreamId()]->info()->time_scale();
|
||||||
|
|
||||||
|
// Use media duration as progress target.
|
||||||
|
progress_target_ = streams[GetReferenceStreamId()]->info()->duration();
|
||||||
|
|
||||||
// Use the reference stream's time scale as movie time scale.
|
// Use the reference stream's time scale as movie time scale.
|
||||||
moov_->header.timescale = sidx_->timescale;
|
moov_->header.timescale = sidx_->timescale;
|
||||||
moof_->header.sequence_number = 1;
|
moof_->header.sequence_number = 1;
|
||||||
|
@ -303,6 +313,27 @@ double Segmenter::GetDuration() const {
|
||||||
return static_cast<double>(moov_->header.duration) / moov_->header.timescale;
|
return static_cast<double>(moov_->header.duration) / moov_->header.timescale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Segmenter::UpdateProgress(uint64_t progress) {
|
||||||
|
accumulated_progress_ += progress;
|
||||||
|
|
||||||
|
if (!progress_listener_) return;
|
||||||
|
if (progress_target_ == 0) return;
|
||||||
|
// It might happen that accumulated progress exceeds progress_target due to
|
||||||
|
// computation errors, e.g. rounding error. Cap it so it never reports > 100%
|
||||||
|
// progress.
|
||||||
|
if (accumulated_progress_ >= progress_target_) {
|
||||||
|
progress_listener_->OnProgress(1.0);
|
||||||
|
} else {
|
||||||
|
progress_listener_->OnProgress(static_cast<double>(accumulated_progress_) /
|
||||||
|
progress_target_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Segmenter::SetComplete() {
|
||||||
|
if (!progress_listener_) return;
|
||||||
|
progress_listener_->OnProgress(1.0);
|
||||||
|
}
|
||||||
|
|
||||||
void Segmenter::InitializeSegment() {
|
void Segmenter::InitializeSegment() {
|
||||||
sidx_->references.clear();
|
sidx_->references.clear();
|
||||||
end_of_segment_ = false;
|
end_of_segment_ = false;
|
||||||
|
|
|
@ -24,6 +24,7 @@ class KeySource;
|
||||||
class MediaSample;
|
class MediaSample;
|
||||||
class MediaStream;
|
class MediaStream;
|
||||||
class MuxerListener;
|
class MuxerListener;
|
||||||
|
class ProgressListener;
|
||||||
|
|
||||||
namespace mp4 {
|
namespace mp4 {
|
||||||
|
|
||||||
|
@ -51,6 +52,8 @@ class Segmenter {
|
||||||
/// Calling other public methods of this class without this method returning
|
/// Calling other public methods of this class without this method returning
|
||||||
/// Status::OK results in an undefined behavior.
|
/// Status::OK results in an undefined behavior.
|
||||||
/// @param streams contains the vector of MediaStreams to be segmented.
|
/// @param streams contains the vector of MediaStreams to be segmented.
|
||||||
|
/// @param muxer_listener receives muxer events. Can be NULL.
|
||||||
|
/// @param progress_listener receives progress updates. Can be NULL.
|
||||||
/// @param encryption_key_source points to the key source which contains
|
/// @param encryption_key_source points to the key source which contains
|
||||||
/// the encryption keys. It can be NULL to indicate that no encryption
|
/// the encryption keys. It can be NULL to indicate that no encryption
|
||||||
/// is required.
|
/// is required.
|
||||||
|
@ -62,6 +65,7 @@ class Segmenter {
|
||||||
/// @return OK on success, an error status otherwise.
|
/// @return OK on success, an error status otherwise.
|
||||||
Status Initialize(const std::vector<MediaStream*>& streams,
|
Status Initialize(const std::vector<MediaStream*>& streams,
|
||||||
MuxerListener* muxer_listener,
|
MuxerListener* muxer_listener,
|
||||||
|
ProgressListener* progress_listener,
|
||||||
KeySource* encryption_key_source,
|
KeySource* encryption_key_source,
|
||||||
uint32_t max_sd_pixels,
|
uint32_t max_sd_pixels,
|
||||||
double clear_lead_in_seconds,
|
double clear_lead_in_seconds,
|
||||||
|
@ -93,12 +97,22 @@ class Segmenter {
|
||||||
double GetDuration() const;
|
double GetDuration() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
/// Update segmentation progress using ProgressListener.
|
||||||
|
void UpdateProgress(uint64_t progress);
|
||||||
|
/// Set progress to 100%.
|
||||||
|
void SetComplete();
|
||||||
|
|
||||||
const MuxerOptions& options() const { return options_; }
|
const MuxerOptions& options() const { return options_; }
|
||||||
FileType* ftyp() { return ftyp_.get(); }
|
FileType* ftyp() { return ftyp_.get(); }
|
||||||
Movie* moov() { return moov_.get(); }
|
Movie* moov() { return moov_.get(); }
|
||||||
BufferWriter* fragment_buffer() { return fragment_buffer_.get(); }
|
BufferWriter* fragment_buffer() { return fragment_buffer_.get(); }
|
||||||
SegmentIndex* sidx() { return sidx_.get(); }
|
SegmentIndex* sidx() { return sidx_.get(); }
|
||||||
MuxerListener* muxer_listener() { return muxer_listener_; }
|
MuxerListener* muxer_listener() { return muxer_listener_; }
|
||||||
|
uint64_t progress_target() { return progress_target_; }
|
||||||
|
|
||||||
|
void set_progress_target(uint64_t progress_target) {
|
||||||
|
progress_target_ = progress_target;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual Status DoInitialize() = 0;
|
virtual Status DoInitialize() = 0;
|
||||||
|
@ -123,6 +137,9 @@ class Segmenter {
|
||||||
bool segment_initialized_;
|
bool segment_initialized_;
|
||||||
bool end_of_segment_;
|
bool end_of_segment_;
|
||||||
MuxerListener* muxer_listener_;
|
MuxerListener* muxer_listener_;
|
||||||
|
ProgressListener* progress_listener_;
|
||||||
|
uint64_t progress_target_;
|
||||||
|
uint64_t accumulated_progress_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(Segmenter);
|
DISALLOW_COPY_AND_ASSIGN(Segmenter);
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "packager/media/base/media_stream.h"
|
#include "packager/media/base/media_stream.h"
|
||||||
#include "packager/media/base/muxer_options.h"
|
#include "packager/media/base/muxer_options.h"
|
||||||
#include "packager/media/event/muxer_listener.h"
|
#include "packager/media/event/muxer_listener.h"
|
||||||
|
#include "packager/media/event/progress_listener.h"
|
||||||
#include "packager/media/file/file.h"
|
#include "packager/media/file/file.h"
|
||||||
#include "packager/media/formats/mp4/box_definitions.h"
|
#include "packager/media/formats/mp4/box_definitions.h"
|
||||||
|
|
||||||
|
@ -68,6 +69,14 @@ bool SingleSegmentSegmenter::GetIndexRange(size_t* offset, size_t* size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Status SingleSegmentSegmenter::DoInitialize() {
|
Status SingleSegmentSegmenter::DoInitialize() {
|
||||||
|
// Single segment segmentation involves two stages:
|
||||||
|
// Stage 1: Create media subsegments from media samples
|
||||||
|
// Stage 2: Update media header (moov) which involves copying of media
|
||||||
|
// subsegments
|
||||||
|
// Assumes stage 2 takes similar amount of time as stage 1. The previous
|
||||||
|
// progress_target was set for stage 1. Times two to account for stage 2.
|
||||||
|
set_progress_target(progress_target() * 2);
|
||||||
|
|
||||||
if (options().temp_dir.empty()) {
|
if (options().temp_dir.empty()) {
|
||||||
base::FilePath temp_file_path;
|
base::FilePath temp_file_path;
|
||||||
if (!base::CreateTemporaryFile(&temp_file_path)) {
|
if (!base::CreateTemporaryFile(&temp_file_path)) {
|
||||||
|
@ -122,6 +131,9 @@ Status SingleSegmentSegmenter::DoFinalize() {
|
||||||
"Cannot open file to read " + temp_file_name_);
|
"Cannot open file to read " + temp_file_name_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The target of 2nd stage of single segment segmentation.
|
||||||
|
const uint64_t re_segment_progress_target = progress_target() * 0.5;
|
||||||
|
|
||||||
const int kBufSize = 0x200000; // 2MB.
|
const int kBufSize = 0x200000; // 2MB.
|
||||||
scoped_ptr<uint8_t[]> buf(new uint8_t[kBufSize]);
|
scoped_ptr<uint8_t[]> buf(new uint8_t[kBufSize]);
|
||||||
while (true) {
|
while (true) {
|
||||||
|
@ -137,7 +149,10 @@ Status SingleSegmentSegmenter::DoFinalize() {
|
||||||
return Status(error::FILE_FAILURE,
|
return Status(error::FILE_FAILURE,
|
||||||
"Failed to write file " + options().output_file_name);
|
"Failed to write file " + options().output_file_name);
|
||||||
}
|
}
|
||||||
|
UpdateProgress(static_cast<double>(size) / temp_file->Size() *
|
||||||
|
re_segment_progress_target);
|
||||||
}
|
}
|
||||||
|
SetComplete();
|
||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,6 +219,8 @@ Status SingleSegmentSegmenter::DoFinalizeSegment() {
|
||||||
size_t segment_size = fragment_buffer()->Size();
|
size_t segment_size = fragment_buffer()->Size();
|
||||||
Status status = fragment_buffer()->WriteToFile(temp_file_.get());
|
Status status = fragment_buffer()->WriteToFile(temp_file_.get());
|
||||||
if (!status.ok()) return status;
|
if (!status.ok()) return status;
|
||||||
|
|
||||||
|
UpdateProgress(vod_ref.subsegment_duration);
|
||||||
if (muxer_listener()) {
|
if (muxer_listener()) {
|
||||||
muxer_listener()->OnNewSegment(vod_ref.earliest_presentation_time,
|
muxer_listener()->OnNewSegment(vod_ref.earliest_presentation_time,
|
||||||
vod_ref.subsegment_duration, segment_size);
|
vod_ref.subsegment_duration, segment_size);
|
||||||
|
|
Loading…
Reference in New Issue