[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 bear-640x360-video1.mp4
#EXT-X-PLACEMENT-OPPORTUNITY #EXT-X-PLACEMENT-OPPORTUNITY
#EXT-X-KEY:METHOD=SAMPLE-AES-CTR,URI="data:text/plain;base64,MTIzNDU2Nzg5MDEyMzQ1Ng==",KEYFORMAT="identity" #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 #EXT-X-BYTERANGE:80067@1135
bear-640x360-video2.mp4 bear-640x360-video2.mp4
#EXT-X-ENDLIST #EXT-X-ENDLIST

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>--> <!--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"> <Period id="0" duration="PT2.0687333333333333S">
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9"> <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 value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
@ -28,15 +28,15 @@
</Representation> </Representation>
</AdaptationSet> </AdaptationSet>
</Period> </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"> <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 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"> <ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b">
<cenc:pssh>AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==</cenc:pssh> <cenc:pssh>AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==</cenc:pssh>
</ContentProtection> </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> <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"/> <Initialization range="0-1090"/>
</SegmentBase> </SegmentBase>
</Representation> </Representation>
@ -49,7 +49,7 @@
<Representation id="1" bandwidth="108126" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100"> <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"/> <AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
<BaseURL>bear-640x360-audio2.mp4</BaseURL> <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"/> <Initialization range="0-966"/>
</SegmentBase> </SegmentBase>
</Representation> </Representation>

View File

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

View File

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

View File

@ -29,6 +29,13 @@ struct MuxerOptions {
/// Otherwise, it specifies the init segment name. /// Otherwise, it specifies the init segment name.
std::string output_file_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 /// Specify output segment name pattern for generated segments. It can
/// furthermore be configured by using a subset of the SegmentTemplate /// furthermore be configured by using a subset of the SegmentTemplate
/// identifiers: $RepresentationID$, $Number$, $Bandwidth$ and $Time. /// identifiers: $RepresentationID$, $Number$, $Bandwidth$ and $Time.

View File

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

View File

@ -76,6 +76,15 @@ class Fragmenter {
use_decoding_timestamp_in_timeline_ = use_decoding_timestamp_in_timeline; 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: protected:
TrackFragment* traf() { return traf_; } TrackFragment* traf() { return traf_; }
@ -99,6 +108,7 @@ class Fragmenter {
uint64_t fragment_duration_; uint64_t fragment_duration_;
int64_t earliest_presentation_time_; int64_t earliest_presentation_time_;
bool first_fragment_ = true; bool first_fragment_ = true;
bool allow_adjust_earliest_presentation_time_ = false;
int64_t first_sap_time_; int64_t first_sap_time_;
std::unique_ptr<BufferWriter> data_; std::unique_ptr<BufferWriter> data_;
// Saves key frames information, for Video. // Saves key frames information, for Video.

View File

@ -75,6 +75,11 @@ Status Segmenter::Initialize(
for (uint32_t i = 0; i < streams.size(); ++i) for (uint32_t i = 0; i < streams.size(); ++i)
fragmenters_[i]->set_use_decoding_timestamp_in_timeline(true); 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. // Choose the first stream if there is no VIDEO.
if (sidx_->reference_id == 0) if (sidx_->reference_id == 0)