Add a muxer option to normalize PTS to start from 0
Some players do not like non-zero starting PTS. We need to do PTS normalization to make it work on those players. Bug: 12686658 Change-Id: I0958c25395e4ea87d8208db9a5f6c5816827eb99
This commit is contained in:
parent
1e79eeb086
commit
522048b0d9
|
@ -37,6 +37,10 @@ DEFINE_bool(fragment_sap_aligned,
|
||||||
true,
|
true,
|
||||||
"Force fragments to begin with stream access points. This flag "
|
"Force fragments to begin with stream access points. This flag "
|
||||||
"implies segment_sap_aligned.");
|
"implies segment_sap_aligned.");
|
||||||
|
DEFINE_bool(normalize_presentation_timestamp,
|
||||||
|
true,
|
||||||
|
"Set to true to normalize the presentation timestamps to start"
|
||||||
|
"from zero.");
|
||||||
DEFINE_int32(num_subsegments_per_sidx,
|
DEFINE_int32(num_subsegments_per_sidx,
|
||||||
1,
|
1,
|
||||||
"For ISO BMFF only. Set the number of subsegments in each "
|
"For ISO BMFF only. Set the number of subsegments in each "
|
||||||
|
|
|
@ -88,6 +88,8 @@ bool GetMuxerOptions(MuxerOptions* muxer_options) {
|
||||||
muxer_options->fragment_duration = FLAGS_fragment_duration;
|
muxer_options->fragment_duration = FLAGS_fragment_duration;
|
||||||
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->normalize_presentation_timestamp =
|
||||||
|
FLAGS_normalize_presentation_timestamp;
|
||||||
muxer_options->num_subsegments_per_sidx = FLAGS_num_subsegments_per_sidx;
|
muxer_options->num_subsegments_per_sidx = FLAGS_num_subsegments_per_sidx;
|
||||||
muxer_options->output_file_name = FLAGS_output;
|
muxer_options->output_file_name = FLAGS_output;
|
||||||
muxer_options->segment_template = FLAGS_segment_template;
|
muxer_options->segment_template = FLAGS_segment_template;
|
||||||
|
|
|
@ -12,6 +12,7 @@ MuxerOptions::MuxerOptions()
|
||||||
fragment_duration(0),
|
fragment_duration(0),
|
||||||
segment_sap_aligned(false),
|
segment_sap_aligned(false),
|
||||||
fragment_sap_aligned(false),
|
fragment_sap_aligned(false),
|
||||||
|
normalize_presentation_timestamp(false),
|
||||||
num_subsegments_per_sidx(0) {}
|
num_subsegments_per_sidx(0) {}
|
||||||
MuxerOptions::~MuxerOptions() {}
|
MuxerOptions::~MuxerOptions() {}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,9 @@ struct MuxerOptions {
|
||||||
// that segment_sap_aligned is true as well.
|
// that segment_sap_aligned is true as well.
|
||||||
bool fragment_sap_aligned;
|
bool fragment_sap_aligned;
|
||||||
|
|
||||||
|
// Set to true to normalize the presentation timestamps to start from zero.
|
||||||
|
bool normalize_presentation_timestamp;
|
||||||
|
|
||||||
// For ISO BMFF only.
|
// For ISO BMFF only.
|
||||||
// Set the number of subsegments in each SIDX box. If 0, a single SIDX box
|
// Set the number of subsegments in each SIDX box. If 0, a single SIDX box
|
||||||
// is used per segment. If -1, no SIDX box is used. Otherwise, the Muxer
|
// is used per segment. If -1, no SIDX box is used. Otherwise, the Muxer
|
||||||
|
|
|
@ -42,14 +42,17 @@ namespace mp4 {
|
||||||
MP4Fragmenter::MP4Fragmenter(TrackFragment* traf,
|
MP4Fragmenter::MP4Fragmenter(TrackFragment* traf,
|
||||||
scoped_ptr<AesCtrEncryptor> encryptor,
|
scoped_ptr<AesCtrEncryptor> encryptor,
|
||||||
int64 clear_time,
|
int64 clear_time,
|
||||||
uint8 nalu_length_size)
|
uint8 nalu_length_size,
|
||||||
|
bool normalize_presentation_timestamp)
|
||||||
: encryptor_(encryptor.Pass()),
|
: encryptor_(encryptor.Pass()),
|
||||||
nalu_length_size_(nalu_length_size),
|
nalu_length_size_(nalu_length_size),
|
||||||
traf_(traf),
|
traf_(traf),
|
||||||
fragment_finalized_(false),
|
fragment_finalized_(false),
|
||||||
fragment_duration_(0),
|
fragment_duration_(0),
|
||||||
earliest_presentation_time_(0),
|
normalize_presentation_timestamp_(normalize_presentation_timestamp),
|
||||||
first_sap_time_(0),
|
presentation_start_time_(kInvalidTime),
|
||||||
|
earliest_presentation_time_(kInvalidTime),
|
||||||
|
first_sap_time_(kInvalidTime),
|
||||||
clear_time_(clear_time) {}
|
clear_time_(clear_time) {}
|
||||||
|
|
||||||
MP4Fragmenter::~MP4Fragmenter() {}
|
MP4Fragmenter::~MP4Fragmenter() {}
|
||||||
|
@ -78,12 +81,30 @@ Status MP4Fragmenter::AddSample(scoped_refptr<MediaSample> sample) {
|
||||||
data_->AppendArray(sample->data(), sample->data_size());
|
data_->AppendArray(sample->data(), sample->data_size());
|
||||||
fragment_duration_ += sample->duration();
|
fragment_duration_ += sample->duration();
|
||||||
|
|
||||||
if (earliest_presentation_time_ > sample->pts())
|
int64 pts = sample->pts();
|
||||||
earliest_presentation_time_ = sample->pts();
|
if (normalize_presentation_timestamp_) {
|
||||||
|
// Normalize PTS to start from 0. Some players do not like non-zero
|
||||||
|
// presentation starting time.
|
||||||
|
// TODO(kqyang): Do we need to add an EditList?
|
||||||
|
if (presentation_start_time_ == kInvalidTime) {
|
||||||
|
presentation_start_time_ = pts;
|
||||||
|
pts = 0;
|
||||||
|
} else {
|
||||||
|
// Can we safely assume the first sample in the media has the earliest
|
||||||
|
// presentation timestamp?
|
||||||
|
DCHECK_GT(pts, presentation_start_time_);
|
||||||
|
pts -= presentation_start_time_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set |earliest_presentation_time_| to |pts| if |pts| is smaller or if it is
|
||||||
|
// not yet initialized (kInvalidTime > pts is always true).
|
||||||
|
if (earliest_presentation_time_ > pts)
|
||||||
|
earliest_presentation_time_ = pts;
|
||||||
|
|
||||||
if (sample->is_key_frame()) {
|
if (sample->is_key_frame()) {
|
||||||
if (kInvalidTime == first_sap_time_)
|
if (first_sap_time_ == kInvalidTime)
|
||||||
first_sap_time_ = sample->pts();
|
first_sap_time_ = pts;
|
||||||
}
|
}
|
||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,11 +31,13 @@ class MP4Fragmenter {
|
||||||
// Caller retains the ownership of |traf| and transfers ownership of
|
// Caller retains the ownership of |traf| and transfers ownership of
|
||||||
// |encryptor|. |clear_time| specifies clear time in the current track
|
// |encryptor|. |clear_time| specifies clear time in the current track
|
||||||
// timescale. |nalu_length_size| specifies NAL unit length size, for
|
// timescale. |nalu_length_size| specifies NAL unit length size, for
|
||||||
// subsample encryption.
|
// subsample encryption. |normalize_presentation_timestamp| defines whether
|
||||||
|
// PTS should be normalized to start from zero.
|
||||||
MP4Fragmenter(TrackFragment* traf,
|
MP4Fragmenter(TrackFragment* traf,
|
||||||
scoped_ptr<AesCtrEncryptor> encryptor,
|
scoped_ptr<AesCtrEncryptor> encryptor,
|
||||||
int64 clear_time,
|
int64 clear_time,
|
||||||
uint8 nalu_length_size);
|
uint8 nalu_length_size,
|
||||||
|
bool normalize_presentation_timestamp);
|
||||||
~MP4Fragmenter();
|
~MP4Fragmenter();
|
||||||
|
|
||||||
virtual Status AddSample(scoped_refptr<MediaSample> sample);
|
virtual Status AddSample(scoped_refptr<MediaSample> sample);
|
||||||
|
@ -81,6 +83,8 @@ class MP4Fragmenter {
|
||||||
TrackFragment* traf_;
|
TrackFragment* traf_;
|
||||||
bool fragment_finalized_;
|
bool fragment_finalized_;
|
||||||
uint64 fragment_duration_;
|
uint64 fragment_duration_;
|
||||||
|
bool normalize_presentation_timestamp_;
|
||||||
|
int64 presentation_start_time_;
|
||||||
uint64 earliest_presentation_time_;
|
uint64 earliest_presentation_time_;
|
||||||
uint64 first_sap_time_;
|
uint64 first_sap_time_;
|
||||||
int64 clear_time_;
|
int64 clear_time_;
|
||||||
|
|
|
@ -70,7 +70,8 @@ Status MP4Segmenter::Initialize(EncryptorSource* encryptor_source,
|
||||||
&moof_->tracks[i],
|
&moof_->tracks[i],
|
||||||
encryptor.Pass(),
|
encryptor.Pass(),
|
||||||
clear_lead_in_seconds * streams[i]->info()->time_scale(),
|
clear_lead_in_seconds * streams[i]->info()->time_scale(),
|
||||||
nalu_length_size);
|
nalu_length_size,
|
||||||
|
options_.normalize_presentation_timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Choose the first stream if there is no VIDEO.
|
// Choose the first stream if there is no VIDEO.
|
||||||
|
|
Loading…
Reference in New Issue