[HLS] Fixing AdCues handling in iframe playlists
Previously for the last iframe in a segment, we wait for the next segment to arrive before writing the EXTINF tag. If an Ad Cue comes in before the next segment, the EXT-X-PLACEMENT_OPPORTUNITY tag would be inserted before the iframe in previous segment. Fixes #378, #396. Change-Id: I1ede72a4d4edca94781c7b05bc25397d67916d1a
This commit is contained in:
parent
14caaf1e1c
commit
cf40accaa8
|
@ -4,10 +4,11 @@
|
||||||
#EXT-X-TARGETDURATION:2
|
#EXT-X-TARGETDURATION:2
|
||||||
#EXT-X-PLAYLIST-TYPE:VOD
|
#EXT-X-PLAYLIST-TYPE:VOD
|
||||||
#EXT-X-I-FRAMES-ONLY
|
#EXT-X-I-FRAMES-ONLY
|
||||||
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="data:text/plain;base64,MTIzNDU2Nzg5MDEyMzQ1Ng==",IV=0x3334353637383930,KEYFORMAT="identity"
|
|
||||||
#EXTINF:1.001,
|
#EXTINF:1.001,
|
||||||
#EXT-X-BYTERANGE:15604@376
|
#EXT-X-BYTERANGE:15604@376
|
||||||
bear-640x360-ac3-video-1.ts
|
bear-640x360-ac3-video-1.ts
|
||||||
|
#EXT-X-DISCONTINUITY
|
||||||
|
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="data:text/plain;base64,MTIzNDU2Nzg5MDEyMzQ1Ng==",IV=0x3334353637383930,KEYFORMAT="identity"
|
||||||
#EXTINF:1.001,
|
#EXTINF:1.001,
|
||||||
#EXT-X-BYTERANGE:18236@376
|
#EXT-X-BYTERANGE:18236@376
|
||||||
bear-640x360-ac3-video-2.ts
|
bear-640x360-ac3-video-2.ts
|
||||||
|
|
|
@ -4,10 +4,11 @@
|
||||||
#EXT-X-TARGETDURATION:2
|
#EXT-X-TARGETDURATION:2
|
||||||
#EXT-X-PLAYLIST-TYPE:VOD
|
#EXT-X-PLAYLIST-TYPE:VOD
|
||||||
#EXT-X-I-FRAMES-ONLY
|
#EXT-X-I-FRAMES-ONLY
|
||||||
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="data:text/plain;base64,MTIzNDU2Nzg5MDEyMzQ1Ng==",IV=0x3334353637383930,KEYFORMAT="identity"
|
|
||||||
#EXTINF:1.001,
|
#EXTINF:1.001,
|
||||||
#EXT-X-BYTERANGE:15604@376
|
#EXT-X-BYTERANGE:15604@376
|
||||||
bear-640x360-video-1.ts
|
bear-640x360-video-1.ts
|
||||||
|
#EXT-X-DISCONTINUITY
|
||||||
|
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="data:text/plain;base64,MTIzNDU2Nzg5MDEyMzQ1Ng==",IV=0x3334353637383930,KEYFORMAT="identity"
|
||||||
#EXTINF:1.001,
|
#EXTINF:1.001,
|
||||||
#EXT-X-BYTERANGE:18236@376
|
#EXT-X-BYTERANGE:18236@376
|
||||||
bear-640x360-video-2.ts
|
bear-640x360-video-2.ts
|
||||||
|
|
|
@ -4,10 +4,11 @@
|
||||||
#EXT-X-TARGETDURATION:2
|
#EXT-X-TARGETDURATION:2
|
||||||
#EXT-X-PLAYLIST-TYPE:VOD
|
#EXT-X-PLAYLIST-TYPE:VOD
|
||||||
#EXT-X-I-FRAMES-ONLY
|
#EXT-X-I-FRAMES-ONLY
|
||||||
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="data:text/plain;base64,MTIzNDU2Nzg5MDEyMzQ1Ng==",IV=0x3334353637383930,KEYFORMAT="identity"
|
|
||||||
#EXTINF:1.001,
|
#EXTINF:1.001,
|
||||||
#EXT-X-BYTERANGE:15604@376
|
#EXT-X-BYTERANGE:15604@376
|
||||||
bear-640x360-ac3-video-1.ts
|
bear-640x360-ac3-video-1.ts
|
||||||
|
#EXT-X-DISCONTINUITY
|
||||||
|
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="data:text/plain;base64,MTIzNDU2Nzg5MDEyMzQ1Ng==",IV=0x3334353637383930,KEYFORMAT="identity"
|
||||||
#EXTINF:1.001,
|
#EXTINF:1.001,
|
||||||
#EXT-X-BYTERANGE:18236@376
|
#EXT-X-BYTERANGE:18236@376
|
||||||
bear-640x360-ac3-video-2.ts
|
bear-640x360-ac3-video-2.ts
|
||||||
|
|
|
@ -10,3 +10,6 @@ bear-640x360-video-1.ts
|
||||||
#EXTINF:1.001,
|
#EXTINF:1.001,
|
||||||
#EXT-X-BYTERANGE:18236@376
|
#EXT-X-BYTERANGE:18236@376
|
||||||
bear-640x360-video-2.ts
|
bear-640x360-video-2.ts
|
||||||
|
#EXTINF:0.667,
|
||||||
|
#EXT-X-BYTERANGE:19928@376
|
||||||
|
bear-640x360-video-3.ts
|
||||||
|
|
|
@ -6,4 +6,4 @@
|
||||||
#EXT-X-STREAM-INF:BANDWIDTH=1217518,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
#EXT-X-STREAM-INF:BANDWIDTH=1217518,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||||
bear-640x360-video.m3u8
|
bear-640x360-video.m3u8
|
||||||
|
|
||||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=145742,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=238897,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||||
|
|
|
@ -2,12 +2,14 @@
|
||||||
#EXT-X-VERSION:6
|
#EXT-X-VERSION:6
|
||||||
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
|
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
|
||||||
#EXT-X-TARGETDURATION:2
|
#EXT-X-TARGETDURATION:2
|
||||||
|
#EXT-X-MEDIA-SEQUENCE:1
|
||||||
|
#EXT-X-DISCONTINUITY-SEQUENCE:1
|
||||||
#EXT-X-I-FRAMES-ONLY
|
#EXT-X-I-FRAMES-ONLY
|
||||||
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="data:text/plain;base64,MjM0NTY3ODkwMTIzNDU2MQ==",IV=0x3334353637383930,KEYFORMAT="identity"
|
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="data:text/plain;base64,MjM0NTY3ODkwMTIzNDU2MQ==",IV=0x3334353637383930,KEYFORMAT="identity"
|
||||||
#EXTINF:1.001,
|
#EXTINF:1.001,
|
||||||
#EXT-X-BYTERANGE:15604@376
|
|
||||||
bear-640x360-video-1.ts
|
|
||||||
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="data:text/plain;base64,MzQ1Njc4OTAxMjM0NTYxMg==",IV=0x3334353637383930,KEYFORMAT="identity"
|
|
||||||
#EXTINF:1.001,
|
|
||||||
#EXT-X-BYTERANGE:18236@376
|
#EXT-X-BYTERANGE:18236@376
|
||||||
bear-640x360-video-2.ts
|
bear-640x360-video-2.ts
|
||||||
|
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="data:text/plain;base64,MzQ1Njc4OTAxMjM0NTYxMg==",IV=0x3334353637383930,KEYFORMAT="identity"
|
||||||
|
#EXTINF:0.667,
|
||||||
|
#EXT-X-BYTERANGE:19928@376
|
||||||
|
bear-640x360-video-3.ts
|
||||||
|
|
|
@ -6,4 +6,4 @@
|
||||||
#EXT-X-STREAM-INF:BANDWIDTH=1217518,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
#EXT-X-STREAM-INF:BANDWIDTH=1217518,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||||
bear-640x360-video.m3u8
|
bear-640x360-video.m3u8
|
||||||
|
|
||||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=145742,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=238897,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||||
|
|
|
@ -2,10 +2,11 @@
|
||||||
#EXT-X-VERSION:6
|
#EXT-X-VERSION:6
|
||||||
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
|
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
|
||||||
#EXT-X-TARGETDURATION:2
|
#EXT-X-TARGETDURATION:2
|
||||||
|
#EXT-X-MEDIA-SEQUENCE:1
|
||||||
#EXT-X-I-FRAMES-ONLY
|
#EXT-X-I-FRAMES-ONLY
|
||||||
#EXTINF:1.001,
|
#EXTINF:1.001,
|
||||||
#EXT-X-BYTERANGE:15604@376
|
|
||||||
bear-640x360-video-1.ts
|
|
||||||
#EXTINF:1.001,
|
|
||||||
#EXT-X-BYTERANGE:18236@376
|
#EXT-X-BYTERANGE:18236@376
|
||||||
bear-640x360-video-2.ts
|
bear-640x360-video-2.ts
|
||||||
|
#EXTINF:0.667,
|
||||||
|
#EXT-X-BYTERANGE:19928@376
|
||||||
|
bear-640x360-video-3.ts
|
||||||
|
|
|
@ -6,4 +6,4 @@
|
||||||
#EXT-X-STREAM-INF:BANDWIDTH=1217518,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
#EXT-X-STREAM-INF:BANDWIDTH=1217518,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
|
||||||
bear-640x360-video.m3u8
|
bear-640x360-video.m3u8
|
||||||
|
|
||||||
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=145742,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=238897,CODECS="avc1.64001e",RESOLUTION=640x360,URI="bear-640x360-video-iframe.m3u8"
|
||||||
|
|
|
@ -4,10 +4,11 @@
|
||||||
#EXT-X-TARGETDURATION:2
|
#EXT-X-TARGETDURATION:2
|
||||||
#EXT-X-PLAYLIST-TYPE:VOD
|
#EXT-X-PLAYLIST-TYPE:VOD
|
||||||
#EXT-X-I-FRAMES-ONLY
|
#EXT-X-I-FRAMES-ONLY
|
||||||
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="skd://www.license.com/getkey?KeyId=31323334-3536-3738-3930-313233343536",KEYFORMATVERSIONS="1",KEYFORMAT="com.apple.streamingkeydelivery"
|
|
||||||
#EXTINF:1.001,
|
#EXTINF:1.001,
|
||||||
#EXT-X-BYTERANGE:15604@376
|
#EXT-X-BYTERANGE:15604@376
|
||||||
bear-640x360-video-1.ts
|
bear-640x360-video-1.ts
|
||||||
|
#EXT-X-DISCONTINUITY
|
||||||
|
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="skd://www.license.com/getkey?KeyId=31323334-3536-3738-3930-313233343536",KEYFORMATVERSIONS="1",KEYFORMAT="com.apple.streamingkeydelivery"
|
||||||
#EXTINF:1.001,
|
#EXTINF:1.001,
|
||||||
#EXT-X-BYTERANGE:18236@376
|
#EXT-X-BYTERANGE:18236@376
|
||||||
bear-640x360-video-2.ts
|
bear-640x360-video-2.ts
|
||||||
|
|
|
@ -4,10 +4,11 @@
|
||||||
#EXT-X-TARGETDURATION:2
|
#EXT-X-TARGETDURATION:2
|
||||||
#EXT-X-PLAYLIST-TYPE:VOD
|
#EXT-X-PLAYLIST-TYPE:VOD
|
||||||
#EXT-X-I-FRAMES-ONLY
|
#EXT-X-I-FRAMES-ONLY
|
||||||
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="data:text/plain;base64,MTIzNDU2Nzg5MDEyMzQ1Ng==",IV=0x3334353637383930,KEYFORMAT="identity"
|
|
||||||
#EXTINF:1.000,
|
#EXTINF:1.000,
|
||||||
#EXT-X-BYTERANGE:940@376
|
#EXT-X-BYTERANGE:940@376
|
||||||
sintel-1024x436-video-1.ts
|
sintel-1024x436-video-1.ts
|
||||||
|
#EXT-X-DISCONTINUITY
|
||||||
|
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="data:text/plain;base64,MTIzNDU2Nzg5MDEyMzQ1Ng==",IV=0x3334353637383930,KEYFORMAT="identity"
|
||||||
#EXTINF:1.000,
|
#EXTINF:1.000,
|
||||||
#EXT-X-BYTERANGE:376@376
|
#EXT-X-BYTERANGE:376@376
|
||||||
sintel-1024x436-video-2.ts
|
sintel-1024x436-video-2.ts
|
||||||
|
|
|
@ -4,10 +4,11 @@
|
||||||
#EXT-X-TARGETDURATION:2
|
#EXT-X-TARGETDURATION:2
|
||||||
#EXT-X-PLAYLIST-TYPE:VOD
|
#EXT-X-PLAYLIST-TYPE:VOD
|
||||||
#EXT-X-I-FRAMES-ONLY
|
#EXT-X-I-FRAMES-ONLY
|
||||||
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="data:text/plain;base64,MTIzNDU2Nzg5MDEyMzQ1Ng==",IV=0x3334353637383930,KEYFORMAT="identity"
|
|
||||||
#EXTINF:1.001,
|
#EXTINF:1.001,
|
||||||
#EXT-X-BYTERANGE:15604@376
|
#EXT-X-BYTERANGE:15604@376
|
||||||
bear-640x360-video-1.ts
|
bear-640x360-video-1.ts
|
||||||
|
#EXT-X-DISCONTINUITY
|
||||||
|
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="data:text/plain;base64,MTIzNDU2Nzg5MDEyMzQ1Ng==",IV=0x3334353637383930,KEYFORMAT="identity"
|
||||||
#EXTINF:1.001,
|
#EXTINF:1.001,
|
||||||
#EXT-X-BYTERANGE:18236@376
|
#EXT-X-BYTERANGE:18236@376
|
||||||
bear-640x360-video-2.ts
|
bear-640x360-video-2.ts
|
||||||
|
|
|
@ -4,10 +4,11 @@
|
||||||
#EXT-X-TARGETDURATION:2
|
#EXT-X-TARGETDURATION:2
|
||||||
#EXT-X-PLAYLIST-TYPE:VOD
|
#EXT-X-PLAYLIST-TYPE:VOD
|
||||||
#EXT-X-I-FRAMES-ONLY
|
#EXT-X-I-FRAMES-ONLY
|
||||||
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="data:text/plain;base64,MTIzNDU2Nzg5MDEyMzQ1Ng==",IV=0x3334353637383930,KEYFORMAT="identity"
|
|
||||||
#EXTINF:1.001,
|
#EXTINF:1.001,
|
||||||
#EXT-X-BYTERANGE:15604@376
|
#EXT-X-BYTERANGE:15604@376
|
||||||
bear-640x360-ec3-video-1.ts
|
bear-640x360-ec3-video-1.ts
|
||||||
|
#EXT-X-DISCONTINUITY
|
||||||
|
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="data:text/plain;base64,MTIzNDU2Nzg5MDEyMzQ1Ng==",IV=0x3334353637383930,KEYFORMAT="identity"
|
||||||
#EXTINF:1.001,
|
#EXTINF:1.001,
|
||||||
#EXT-X-BYTERANGE:18236@376
|
#EXT-X-BYTERANGE:18236@376
|
||||||
bear-640x360-ec3-video-2.ts
|
bear-640x360-ec3-video-2.ts
|
||||||
|
|
|
@ -8,10 +8,10 @@
|
||||||
#EXTINF:1.001,
|
#EXTINF:1.001,
|
||||||
#EXT-X-BYTERANGE:15581@80
|
#EXT-X-BYTERANGE:15581@80
|
||||||
bear-640x360-video-1.m4s
|
bear-640x360-video-1.m4s
|
||||||
#EXT-X-PLACEMENT-OPPORTUNITY
|
|
||||||
#EXTINF:1.001,
|
#EXTINF:1.001,
|
||||||
#EXT-X-BYTERANGE:18221@80
|
#EXT-X-BYTERANGE:18221@80
|
||||||
bear-640x360-video-2.m4s
|
bear-640x360-video-2.m4s
|
||||||
|
#EXT-X-PLACEMENT-OPPORTUNITY
|
||||||
#EXTINF:0.734,
|
#EXTINF:0.734,
|
||||||
#EXT-X-BYTERANGE:19663@80
|
#EXT-X-BYTERANGE:19663@80
|
||||||
bear-640x360-video-3.m4s
|
bear-640x360-video-3.m4s
|
||||||
|
|
|
@ -9,10 +9,10 @@
|
||||||
#EXTINF:1.001,
|
#EXTINF:1.001,
|
||||||
#EXT-X-BYTERANGE:15581@1159
|
#EXT-X-BYTERANGE:15581@1159
|
||||||
bear-640x360-video.mp4
|
bear-640x360-video.mp4
|
||||||
#EXT-X-PLACEMENT-OPPORTUNITY
|
|
||||||
#EXTINF:1.001,
|
#EXTINF:1.001,
|
||||||
#EXT-X-BYTERANGE:18754@100472
|
#EXT-X-BYTERANGE:18754@100472
|
||||||
bear-640x360-video.mp4
|
bear-640x360-video.mp4
|
||||||
|
#EXT-X-PLACEMENT-OPPORTUNITY
|
||||||
#EXTINF:0.734,
|
#EXTINF:0.734,
|
||||||
#EXT-X-BYTERANGE:20068@222812
|
#EXT-X-BYTERANGE:20068@222812
|
||||||
bear-640x360-video.mp4
|
bear-640x360-video.mp4
|
||||||
|
|
|
@ -151,6 +151,7 @@ class SegmentInfoEntry : public HlsEntry {
|
||||||
std::string ToString() override;
|
std::string ToString() override;
|
||||||
double start_time() const { return start_time_; }
|
double start_time() const { return start_time_; }
|
||||||
double duration() const { return duration_; }
|
double duration() const { return duration_; }
|
||||||
|
void set_duration(double duration) { duration_ = duration; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SegmentInfoEntry(const SegmentInfoEntry&) = delete;
|
SegmentInfoEntry(const SegmentInfoEntry&) = delete;
|
||||||
|
@ -158,7 +159,7 @@ class SegmentInfoEntry : public HlsEntry {
|
||||||
|
|
||||||
const std::string file_name_;
|
const std::string file_name_;
|
||||||
const double start_time_;
|
const double start_time_;
|
||||||
const double duration_;
|
double duration_;
|
||||||
const bool use_byte_range_;
|
const bool use_byte_range_;
|
||||||
const uint64_t start_byte_offset_;
|
const uint64_t start_byte_offset_;
|
||||||
const uint64_t segment_file_size_;
|
const uint64_t segment_file_size_;
|
||||||
|
@ -379,20 +380,20 @@ void MediaPlaylist::AddSegment(const std::string& file_name,
|
||||||
if (stream_type_ == MediaPlaylistStreamType::kVideoIFramesOnly) {
|
if (stream_type_ == MediaPlaylistStreamType::kVideoIFramesOnly) {
|
||||||
if (key_frames_.empty())
|
if (key_frames_.empty())
|
||||||
return;
|
return;
|
||||||
// Skip the last entry as the duration of the key frames are defined by the
|
|
||||||
// next key frame, which we don't know yet.
|
AdjustLastSegmentInfoEntryDuration(key_frames_.front().timestamp);
|
||||||
for (auto iter = key_frames_.begin(); iter != std::prev(key_frames_.end());
|
|
||||||
++iter) {
|
for (auto iter = key_frames_.begin(); iter != key_frames_.end(); ++iter) {
|
||||||
const std::string& segment_file_name =
|
// Last entry duration may be adjusted later when the next iframe becomes
|
||||||
iter->segment_file_name.empty() ? file_name : iter->segment_file_name;
|
// available.
|
||||||
AddSegmentInfoEntry(segment_file_name, iter->timestamp, iter->duration,
|
const uint64_t next_timestamp = std::next(iter) == key_frames_.end()
|
||||||
|
? (start_time + duration)
|
||||||
|
: std::next(iter)->timestamp;
|
||||||
|
AddSegmentInfoEntry(file_name, iter->timestamp,
|
||||||
|
next_timestamp - iter->timestamp,
|
||||||
iter->start_byte_offset, iter->size);
|
iter->start_byte_offset, iter->size);
|
||||||
}
|
}
|
||||||
|
key_frames_.clear();
|
||||||
key_frames_.erase(key_frames_.begin(), std::prev(key_frames_.end()));
|
|
||||||
KeyFrameInfo& key_frame = key_frames_.front();
|
|
||||||
key_frame.segment_file_name = file_name;
|
|
||||||
key_frame.duration = start_time + duration - key_frame.timestamp;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return AddSegmentInfoEntry(file_name, start_time, duration, start_byte_offset,
|
return AddSegmentInfoEntry(file_name, start_time, duration, start_byte_offset,
|
||||||
|
@ -411,9 +412,6 @@ void MediaPlaylist::AddKeyFrame(uint64_t timestamp,
|
||||||
stream_type_ = MediaPlaylistStreamType::kVideoIFramesOnly;
|
stream_type_ = MediaPlaylistStreamType::kVideoIFramesOnly;
|
||||||
use_byte_range_ = true;
|
use_byte_range_ = true;
|
||||||
}
|
}
|
||||||
if (!key_frames_.empty()) {
|
|
||||||
key_frames_.back().duration = timestamp - key_frames_.back().timestamp;
|
|
||||||
}
|
|
||||||
key_frames_.push_back({timestamp, start_byte_offset, size});
|
key_frames_.push_back({timestamp, start_byte_offset, size});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -439,18 +437,6 @@ void MediaPlaylist::AddPlacementOpportunity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MediaPlaylist::WriteToFile(const std::string& file_path) {
|
bool MediaPlaylist::WriteToFile(const std::string& file_path) {
|
||||||
if (!key_frames_.empty() &&
|
|
||||||
hls_params_.playlist_type == HlsPlaylistType::kVod) {
|
|
||||||
// Flush remaining key frames. This assumes |WriteToFile| is only called
|
|
||||||
// once at the end of the file in VOD.
|
|
||||||
CHECK_EQ(key_frames_.size(), 1u);
|
|
||||||
const KeyFrameInfo& key_frame = key_frames_.front();
|
|
||||||
AddSegmentInfoEntry(key_frame.segment_file_name, key_frame.timestamp,
|
|
||||||
key_frame.duration, key_frame.start_byte_offset,
|
|
||||||
key_frame.size);
|
|
||||||
key_frames_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!target_duration_set_) {
|
if (!target_duration_set_) {
|
||||||
SetTargetDuration(ceil(GetLongestSegmentDuration()));
|
SetTargetDuration(ceil(GetLongestSegmentDuration()));
|
||||||
}
|
}
|
||||||
|
@ -548,6 +534,25 @@ void MediaPlaylist::AddSegmentInfoEntry(const std::string& segment_file_name,
|
||||||
SlideWindow();
|
SlideWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MediaPlaylist::AdjustLastSegmentInfoEntryDuration(
|
||||||
|
uint64_t next_timestamp) {
|
||||||
|
if (time_scale_ == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const double scaled_next_timestamp =
|
||||||
|
static_cast<double>(next_timestamp) / time_scale_;
|
||||||
|
|
||||||
|
for (auto iter = entries_.rbegin(); iter != entries_.rend(); ++iter) {
|
||||||
|
if (iter->get()->type() == HlsEntry::EntryType::kExtInf) {
|
||||||
|
SegmentInfoEntry* segment_info =
|
||||||
|
reinterpret_cast<SegmentInfoEntry*>(iter->get());
|
||||||
|
segment_info->set_duration(scaled_next_timestamp -
|
||||||
|
segment_info->start_time());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MediaPlaylist::SlideWindow() {
|
void MediaPlaylist::SlideWindow() {
|
||||||
DCHECK(!entries_.empty());
|
DCHECK(!entries_.empty());
|
||||||
if (hls_params_.time_shift_buffer_depth <= 0.0 ||
|
if (hls_params_.time_shift_buffer_depth <= 0.0 ||
|
||||||
|
|
|
@ -185,6 +185,9 @@ class MediaPlaylist {
|
||||||
uint64_t duration,
|
uint64_t duration,
|
||||||
uint64_t start_byte_offset,
|
uint64_t start_byte_offset,
|
||||||
uint64_t size);
|
uint64_t size);
|
||||||
|
// Adjust the duration of the last SegmentInfoEntry to end on
|
||||||
|
// |next_timestamp|.
|
||||||
|
void AdjustLastSegmentInfoEntryDuration(uint64_t next_timestamp);
|
||||||
// Remove elements from |entries_| for live profile. Increments
|
// Remove elements from |entries_| for live profile. Increments
|
||||||
// |sequence_number_| by the number of segments removed.
|
// |sequence_number_| by the number of segments removed.
|
||||||
void SlideWindow();
|
void SlideWindow();
|
||||||
|
@ -231,7 +234,6 @@ class MediaPlaylist {
|
||||||
uint64_t timestamp;
|
uint64_t timestamp;
|
||||||
uint64_t start_byte_offset;
|
uint64_t start_byte_offset;
|
||||||
uint64_t size;
|
uint64_t size;
|
||||||
uint64_t duration;
|
|
||||||
std::string segment_file_name;
|
std::string segment_file_name;
|
||||||
};
|
};
|
||||||
std::list<KeyFrameInfo> key_frames_;
|
std::list<KeyFrameInfo> key_frames_;
|
||||||
|
|
|
@ -811,7 +811,7 @@ TEST_F(IFrameMediaPlaylistTest, SingleSegment) {
|
||||||
"#EXT-X-VERSION:6\n"
|
"#EXT-X-VERSION:6\n"
|
||||||
"## Generated with https://github.com/google/shaka-packager version "
|
"## Generated with https://github.com/google/shaka-packager version "
|
||||||
"test\n"
|
"test\n"
|
||||||
"#EXT-X-TARGETDURATION:9\n"
|
"#EXT-X-TARGETDURATION:8\n"
|
||||||
"#EXT-X-PLAYLIST-TYPE:VOD\n"
|
"#EXT-X-PLAYLIST-TYPE:VOD\n"
|
||||||
"#EXT-X-I-FRAMES-ONLY\n"
|
"#EXT-X-I-FRAMES-ONLY\n"
|
||||||
"#EXT-X-MAP:URI=\"file.mp4\",BYTERANGE=\"501@0\"\n"
|
"#EXT-X-MAP:URI=\"file.mp4\",BYTERANGE=\"501@0\"\n"
|
||||||
|
@ -875,6 +875,49 @@ TEST_F(IFrameMediaPlaylistTest, MultiSegment) {
|
||||||
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
|
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(IFrameMediaPlaylistTest, MultiSegmentWithPlacementOpportunity) {
|
||||||
|
valid_video_media_info_.set_reference_time_scale(90000);
|
||||||
|
valid_video_media_info_.set_segment_template_url("file$Number$.ts");
|
||||||
|
ASSERT_TRUE(media_playlist_->SetMediaInfo(valid_video_media_info_));
|
||||||
|
|
||||||
|
media_playlist_->AddKeyFrame(0, 1000, 2345);
|
||||||
|
media_playlist_->AddKeyFrame(2 * kTimeScale, 5000, 6345);
|
||||||
|
media_playlist_->AddSegment("file1.ts", 0, 10 * kTimeScale, kZeroByteOffset,
|
||||||
|
kMBytes);
|
||||||
|
media_playlist_->AddPlacementOpportunity();
|
||||||
|
media_playlist_->AddKeyFrame(11 * kTimeScale, 1000, 2345);
|
||||||
|
media_playlist_->AddKeyFrame(15 * kTimeScale, 3345, 12345);
|
||||||
|
media_playlist_->AddSegment("file2.ts", 10 * kTimeScale, 30 * kTimeScale,
|
||||||
|
kZeroByteOffset, 5 * kMBytes);
|
||||||
|
|
||||||
|
const char kExpectedOutput[] =
|
||||||
|
"#EXTM3U\n"
|
||||||
|
"#EXT-X-VERSION:6\n"
|
||||||
|
"## Generated with https://github.com/google/shaka-packager version "
|
||||||
|
"test\n"
|
||||||
|
"#EXT-X-TARGETDURATION:25\n"
|
||||||
|
"#EXT-X-PLAYLIST-TYPE:VOD\n"
|
||||||
|
"#EXT-X-I-FRAMES-ONLY\n"
|
||||||
|
"#EXTINF:2.000,\n"
|
||||||
|
"#EXT-X-BYTERANGE:2345@1000\n"
|
||||||
|
"file1.ts\n"
|
||||||
|
"#EXTINF:9.000,\n"
|
||||||
|
"#EXT-X-BYTERANGE:6345@5000\n"
|
||||||
|
"file1.ts\n"
|
||||||
|
"#EXT-X-PLACEMENT-OPPORTUNITY\n"
|
||||||
|
"#EXTINF:4.000,\n"
|
||||||
|
"#EXT-X-BYTERANGE:2345@1000\n"
|
||||||
|
"file2.ts\n"
|
||||||
|
"#EXTINF:25.000,\n"
|
||||||
|
"#EXT-X-BYTERANGE:12345\n"
|
||||||
|
"file2.ts\n"
|
||||||
|
"#EXT-X-ENDLIST\n";
|
||||||
|
|
||||||
|
const char kMemoryFilePath[] = "memory://media.m3u8";
|
||||||
|
EXPECT_TRUE(media_playlist_->WriteToFile(kMemoryFilePath));
|
||||||
|
ASSERT_FILE_STREQ(kMemoryFilePath, kExpectedOutput);
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const int kNumPreservedSegmentsOutsideLiveWindow = 3;
|
const int kNumPreservedSegmentsOutsideLiveWindow = 3;
|
||||||
const int kMaxNumSegmentsAvailable =
|
const int kMaxNumSegmentsAvailable =
|
||||||
|
|
Loading…
Reference in New Issue