[Ad Insertion] Avoid adjusting EPT except for the first file

EPT (earliest presentation time) may be adjusted not to be lower than
the decoding timestamp (dts), but the adjustment should only be done
on the first file when there is one file per Representation per Period.
The second file and onwards should not be adjusted otherwise a GAP
would be created.

Closes #384
Closes b/78517422

Change-Id: I56771ad8fbbe6a87b832ec58854cfbf37d5f1817
This commit is contained in:
KongQun Yang 2018-05-30 15:02:33 -07:00
parent 2c1faa71a0
commit 55050fe6b5
9 changed files with 53 additions and 26 deletions

View File

@ -13,7 +13,7 @@ bear-640x360-video1.mp4
bear-640x360-video1.mp4
#EXT-X-PLACEMENT-OPPORTUNITY
#EXT-X-KEY:METHOD=SAMPLE-AES-CTR,URI="data:text/plain;base64,MTIzNDU2Nzg5MDEyMzQ1Ng==",KEYFORMAT="identity"
#EXTINF:0.801,
#EXTINF:0.734,
#EXT-X-BYTERANGE:80067@1135
bear-640x360-video2.mp4
#EXT-X-ENDLIST

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT2.8695333003997803S">
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT2.802799940109253S">
<Period id="0" duration="PT2.0687333333333333S">
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9">
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
@ -28,15 +28,15 @@
</Representation>
</AdaptationSet>
</Period>
<Period id="1" duration="PT.8008000000000002S">
<Period id="1" duration="PT.7340666666666666S">
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9">
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b">
<cenc:pssh>AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==</cenc:pssh>
</ContentProtection>
<Representation id="0" bandwidth="799871" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
<Representation id="0" bandwidth="872586" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
<BaseURL>bear-640x360-video2.mp4</BaseURL>
<SegmentBase indexRange="1091-1134" timescale="30000" presentationTimeOffset="60059">
<SegmentBase indexRange="1091-1134" timescale="30000" presentationTimeOffset="62062">
<Initialization range="0-1090"/>
</SegmentBase>
</Representation>
@ -49,7 +49,7 @@
<Representation id="1" bandwidth="108126" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
<BaseURL>bear-640x360-audio2.mp4</BaseURL>
<SegmentBase indexRange="967-1010" timescale="44100" presentationTimeOffset="88288">
<SegmentBase indexRange="967-1010" timescale="44100" presentationTimeOffset="91231">
<Initialization range="0-966"/>
</SegmentBase>
</Representation>

View File

@ -110,8 +110,9 @@ Status Muxer::ReinitializeMuxer(int64_t timestamp) {
current_key_id_ = encryption_config.key_id;
}
if (!output_file_template_.empty()) {
// Update |output_file_name| with an actual file name, which will be used by
// the subclasses.
// Update |output_file_index| and |output_file_name| with an actual file
// name, which will be used by the subclasses.
options_.output_file_index = output_file_index_;
options_.output_file_name =
GetSegmentName(output_file_template_, timestamp, output_file_index_++,
options_.bandwidth);

View File

@ -75,11 +75,9 @@ class Muxer : public MediaHandler {
Muxer(const Muxer&) = delete;
Muxer& operator=(const Muxer&) = delete;
// Re-initialize Muxer. Could be called on StreamInfo or CueEvent.
// |timestamp| may be used to set the output file name.
Status ReinitializeMuxer(int64_t timestamp);
// Initialize the muxer.
// Initialize the muxer. InitializeMuxer may be called multiple times with
// |options()| updated between calls, which is used to support separate file
// per Representation per Period for Ad Insertion.
virtual Status InitializeMuxer() = 0;
// Final clean up.
@ -95,6 +93,10 @@ class Muxer : public MediaHandler {
size_t stream_id,
const SegmentInfo& segment_info) = 0;
// Re-initialize Muxer. Could be called on StreamInfo or CueEvent.
// |timestamp| may be used to set the output file name.
Status ReinitializeMuxer(int64_t timestamp);
MuxerOptions options_;
std::vector<std::shared_ptr<const StreamInfo>> streams_;
std::vector<uint8_t> current_key_id_;

View File

@ -29,6 +29,13 @@ struct MuxerOptions {
/// Otherwise, it specifies the init segment name.
std::string output_file_name;
/// Output file index. With one file per Representation per Period, there
/// could be more than one file generated with Ad Cues present. This is the
/// 0-based index of the output file.
/// TODO(kqyang): Remove when the EPT adjustment logic in
/// Fragmenter::FinalizeFragment is removed.
size_t output_file_index = 0;
/// Specify output segment name pattern for generated segments. It can
/// furthermore be configured by using a subset of the SegmentTemplate
/// identifiers: $RepresentationID$, $Number$, $Bandwidth$ and $Time.

View File

@ -152,20 +152,22 @@ Status Fragmenter::FinalizeFragment() {
}
if (first_fragment_) {
// Chrome (as of v66 https://crbug.com/398141) does not like negative values
// for adjusted dts = dts + Period@start (0 for first Period)
if (allow_adjust_earliest_presentation_time_) {
// Chrome (as of v66 https://crbug.com/398141) does not like negative
// values for adjusted dts = dts + Period@start (0 for first Period)
// - presentationTimeOffset
// Since |earliest_presentation_time| of the first fragment will be used to
// set presentationTimeOffset, the adjusted dts can become negative for the
// frames in the first segment in the first Period. To avoid seeing that,
// |earliest_presentation_time| is adjusted so it is not larger than the
// dts.
// Since |earliest_presentation_time| of the first fragment will be used
// to set presentationTimeOffset, the adjusted dts can become negative for
// the frames in the first segment in the first Period. To avoid seeing
// that, |earliest_presentation_time| is adjusted so it is not larger than
// the dts.
const int64_t dts = traf_->decode_time.decode_time;
if (earliest_presentation_time_ > dts) {
const uint64_t delta = earliest_presentation_time_ - dts;
earliest_presentation_time_ = dts;
fragment_duration_ += delta;
}
}
first_fragment_ = false;
}

View File

@ -76,6 +76,15 @@ class Fragmenter {
use_decoding_timestamp_in_timeline_ = use_decoding_timestamp_in_timeline;
}
/// Set the flag allow_use_adjust_earliest_presentation_time, which if set to
/// true, earlist_presentation_time (EPT) may be adjusted not to be smaller
/// than the decoding timestamp (dts) for the first fragment.
void set_allow_adjust_earliest_presentation_time(
bool allow_adjust_earliest_presentation_time) {
allow_adjust_earliest_presentation_time_ =
allow_adjust_earliest_presentation_time;
}
protected:
TrackFragment* traf() { return traf_; }
@ -99,6 +108,7 @@ class Fragmenter {
uint64_t fragment_duration_;
int64_t earliest_presentation_time_;
bool first_fragment_ = true;
bool allow_adjust_earliest_presentation_time_ = false;
int64_t first_sap_time_;
std::unique_ptr<BufferWriter> data_;
// Saves key frames information, for Video.

View File

@ -75,6 +75,11 @@ Status Segmenter::Initialize(
for (uint32_t i = 0; i < streams.size(); ++i)
fragmenters_[i]->set_use_decoding_timestamp_in_timeline(true);
}
// Only allow |EPT| to be adjusted for the first file.
if (options_.output_file_index == 0) {
for (uint32_t i = 0; i < streams.size(); ++i)
fragmenters_[i]->set_allow_adjust_earliest_presentation_time(true);
}
// Choose the first stream if there is no VIDEO.
if (sidx_->reference_id == 0)