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:
Kongqun Yang 2014-01-22 15:51:26 -08:00 committed by KongQun Yang
parent 1e79eeb086
commit 522048b0d9
7 changed files with 46 additions and 10 deletions

View File

@ -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 "

View File

@ -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;

View File

@ -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() {}

View File

@ -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

View File

@ -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;
} }

View File

@ -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_;

View File

@ -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.