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();
|
||||
}
|
||||
|
||||
void Muxer::SetProgressListener(
|
||||
scoped_ptr<ProgressListener> progress_listener) {
|
||||
progress_listener_ = progress_listener.Pass();
|
||||
}
|
||||
|
||||
Status Muxer::AddSample(const MediaStream* stream,
|
||||
scoped_refptr<MediaSample> sample) {
|
||||
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/status.h"
|
||||
#include "packager/media/event/muxer_listener.h"
|
||||
#include "packager/media/event/progress_listener.h"
|
||||
|
||||
namespace edash_packager {
|
||||
namespace media {
|
||||
|
@ -62,6 +63,10 @@ class Muxer {
|
|||
/// @param muxer_listener should not be NULL.
|
||||
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_; }
|
||||
|
||||
/// Inject clock, mainly used for testing.
|
||||
|
@ -85,6 +90,7 @@ class Muxer {
|
|||
return crypto_period_duration_in_seconds_;
|
||||
}
|
||||
MuxerListener* muxer_listener() { return muxer_listener_.get(); }
|
||||
ProgressListener* progress_listener() { return progress_listener_.get(); }
|
||||
base::Clock* clock() { return clock_; }
|
||||
|
||||
private:
|
||||
|
@ -114,6 +120,7 @@ class Muxer {
|
|||
bool cancelled_;
|
||||
|
||||
scoped_ptr<MuxerListener> muxer_listener_;
|
||||
scoped_ptr<ProgressListener> progress_listener_;
|
||||
// An external injected clock, can be NULL.
|
||||
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 =
|
||||
segmenter_->Initialize(streams(),
|
||||
muxer_listener(),
|
||||
progress_listener(),
|
||||
encryption_key_source(),
|
||||
max_sd_pixels(),
|
||||
clear_lead_in_seconds(),
|
||||
|
|
|
@ -65,6 +65,7 @@ Status MultiSegmentSegmenter::DoInitialize() {
|
|||
}
|
||||
|
||||
Status MultiSegmentSegmenter::DoFinalize() {
|
||||
SetComplete();
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
|
@ -173,17 +174,22 @@ Status MultiSegmentSegmenter::WriteSegment() {
|
|||
if (!file->Close())
|
||||
LOG(WARNING) << "Failed to close the file properly: " << file_name;
|
||||
|
||||
if (status.ok() && muxer_listener()) {
|
||||
uint64_t segment_duration = 0;
|
||||
// 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.
|
||||
for (size_t i = 0; i < sidx()->references.size(); ++i)
|
||||
segment_duration += sidx()->references[i].subsegment_duration;
|
||||
if (!status.ok())
|
||||
return status;
|
||||
|
||||
uint64_t segment_duration = 0;
|
||||
// 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.
|
||||
for (size_t i = 0; i < sidx()->references.size(); ++i)
|
||||
segment_duration += sidx()->references[i].subsegment_duration;
|
||||
|
||||
UpdateProgress(segment_duration);
|
||||
if (muxer_listener()) {
|
||||
muxer_listener()->OnNewSegment(
|
||||
sidx()->earliest_presentation_time, segment_duration, segment_size);
|
||||
}
|
||||
|
||||
return status;
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
} // namespace mp4
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "packager/media/base/media_stream.h"
|
||||
#include "packager/media/base/muxer_options.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/key_rotation_fragmenter.h"
|
||||
|
||||
|
@ -123,18 +124,24 @@ Segmenter::Segmenter(const MuxerOptions& options,
|
|||
sidx_(new SegmentIndex()),
|
||||
segment_initialized_(false),
|
||||
end_of_segment_(false),
|
||||
muxer_listener_(NULL) {}
|
||||
muxer_listener_(NULL),
|
||||
progress_listener_(NULL),
|
||||
progress_target_(0),
|
||||
accumulated_progress_(0) {
|
||||
}
|
||||
|
||||
Segmenter::~Segmenter() { STLDeleteElements(&fragmenters_); }
|
||||
|
||||
Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
||||
MuxerListener* muxer_listener,
|
||||
ProgressListener* progress_listener,
|
||||
KeySource* encryption_key_source,
|
||||
uint32_t max_sd_pixels,
|
||||
double clear_lead_in_seconds,
|
||||
double crypto_period_duration_in_seconds) {
|
||||
DCHECK_LT(0u, streams.size());
|
||||
muxer_listener_ = muxer_listener;
|
||||
progress_listener_ = progress_listener;
|
||||
moof_->header.sequence_number = 0;
|
||||
|
||||
moof_->tracks.resize(streams.size());
|
||||
|
@ -202,6 +209,9 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
|||
sidx_->reference_id = 1;
|
||||
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.
|
||||
moov_->header.timescale = sidx_->timescale;
|
||||
moof_->header.sequence_number = 1;
|
||||
|
@ -303,6 +313,27 @@ double Segmenter::GetDuration() const {
|
|||
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() {
|
||||
sidx_->references.clear();
|
||||
end_of_segment_ = false;
|
||||
|
|
|
@ -24,6 +24,7 @@ class KeySource;
|
|||
class MediaSample;
|
||||
class MediaStream;
|
||||
class MuxerListener;
|
||||
class ProgressListener;
|
||||
|
||||
namespace mp4 {
|
||||
|
||||
|
@ -51,6 +52,8 @@ class Segmenter {
|
|||
/// Calling other public methods of this class without this method returning
|
||||
/// Status::OK results in an undefined behavior.
|
||||
/// @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
|
||||
/// the encryption keys. It can be NULL to indicate that no encryption
|
||||
/// is required.
|
||||
|
@ -62,6 +65,7 @@ class Segmenter {
|
|||
/// @return OK on success, an error status otherwise.
|
||||
Status Initialize(const std::vector<MediaStream*>& streams,
|
||||
MuxerListener* muxer_listener,
|
||||
ProgressListener* progress_listener,
|
||||
KeySource* encryption_key_source,
|
||||
uint32_t max_sd_pixels,
|
||||
double clear_lead_in_seconds,
|
||||
|
@ -93,12 +97,22 @@ class Segmenter {
|
|||
double GetDuration() const;
|
||||
|
||||
protected:
|
||||
/// Update segmentation progress using ProgressListener.
|
||||
void UpdateProgress(uint64_t progress);
|
||||
/// Set progress to 100%.
|
||||
void SetComplete();
|
||||
|
||||
const MuxerOptions& options() const { return options_; }
|
||||
FileType* ftyp() { return ftyp_.get(); }
|
||||
Movie* moov() { return moov_.get(); }
|
||||
BufferWriter* fragment_buffer() { return fragment_buffer_.get(); }
|
||||
SegmentIndex* sidx() { return sidx_.get(); }
|
||||
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:
|
||||
virtual Status DoInitialize() = 0;
|
||||
|
@ -123,6 +137,9 @@ class Segmenter {
|
|||
bool segment_initialized_;
|
||||
bool end_of_segment_;
|
||||
MuxerListener* muxer_listener_;
|
||||
ProgressListener* progress_listener_;
|
||||
uint64_t progress_target_;
|
||||
uint64_t accumulated_progress_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Segmenter);
|
||||
};
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "packager/media/base/media_stream.h"
|
||||
#include "packager/media/base/muxer_options.h"
|
||||
#include "packager/media/event/muxer_listener.h"
|
||||
#include "packager/media/event/progress_listener.h"
|
||||
#include "packager/media/file/file.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() {
|
||||
// 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()) {
|
||||
base::FilePath temp_file_path;
|
||||
if (!base::CreateTemporaryFile(&temp_file_path)) {
|
||||
|
@ -122,6 +131,9 @@ Status SingleSegmentSegmenter::DoFinalize() {
|
|||
"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.
|
||||
scoped_ptr<uint8_t[]> buf(new uint8_t[kBufSize]);
|
||||
while (true) {
|
||||
|
@ -137,7 +149,10 @@ Status SingleSegmentSegmenter::DoFinalize() {
|
|||
return Status(error::FILE_FAILURE,
|
||||
"Failed to write file " + options().output_file_name);
|
||||
}
|
||||
UpdateProgress(static_cast<double>(size) / temp_file->Size() *
|
||||
re_segment_progress_target);
|
||||
}
|
||||
SetComplete();
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
|
@ -204,6 +219,8 @@ Status SingleSegmentSegmenter::DoFinalizeSegment() {
|
|||
size_t segment_size = fragment_buffer()->Size();
|
||||
Status status = fragment_buffer()->WriteToFile(temp_file_.get());
|
||||
if (!status.ok()) return status;
|
||||
|
||||
UpdateProgress(vod_ref.subsegment_duration);
|
||||
if (muxer_listener()) {
|
||||
muxer_listener()->OnNewSegment(vod_ref.earliest_presentation_time,
|
||||
vod_ref.subsegment_duration, segment_size);
|
||||
|
|
Loading…
Reference in New Issue