[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:
parent
2c1faa71a0
commit
55050fe6b5
|
@ -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
|
||||
|
|
Binary file not shown.
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -152,19 +152,21 @@ 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)
|
||||
// - 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.
|
||||
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;
|
||||
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.
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue