diff --git a/src/main/java/com/blazemeter/jmeter/hls/logic/VideoStreamingSamplerFactory.java b/src/main/java/com/blazemeter/jmeter/hls/logic/VideoStreamingSamplerFactory.java index 846f136..a9844b5 100644 --- a/src/main/java/com/blazemeter/jmeter/hls/logic/VideoStreamingSamplerFactory.java +++ b/src/main/java/com/blazemeter/jmeter/hls/logic/VideoStreamingSamplerFactory.java @@ -12,7 +12,7 @@ public class VideoStreamingSamplerFactory { VideoStreamingHttpClient httpClient, TimeMachine timeMachine, SampleResultProcessor sampleResultProcessor) { //HLS Master Playlist must contain this .m3u8 extension in their URLs - if (url.contains(".m3u8")) { + if (url.contains(".m3u8")||url.contains("format=m3u8")) { return createHlsSampler(baseSampler, httpClient, timeMachine, sampleResultProcessor); } else { return createDashSampler(baseSampler, httpClient, timeMachine, sampleResultProcessor); diff --git a/src/main/java/com/blazemeter/jmeter/videostreaming/dash/DashSampler.java b/src/main/java/com/blazemeter/jmeter/videostreaming/dash/DashSampler.java index ef45f44..207eb87 100644 --- a/src/main/java/com/blazemeter/jmeter/videostreaming/dash/DashSampler.java +++ b/src/main/java/com/blazemeter/jmeter/videostreaming/dash/DashSampler.java @@ -67,7 +67,9 @@ public MediaRepresentation findMatchingVariant(List variant boolean initialLoop = true; while (!mediaPlayback.hasEnded()) { if (mediaPlayback.needsManifestUpdate() && !initialLoop) { - long awaitMillis = manifest.getReloadTimeMillis(timeMachine.now()); + long segmentDurationMillis = (long) mediaPlayback.getLastSegment().getDurationSeconds() * 1000; + long awaitMillis = manifest.getReloadTimeMillis(segmentDurationMillis); + if (awaitMillis > 0) { timeMachine.awaitMillis(awaitMillis); } @@ -187,7 +189,7 @@ private void awaitSegmentAvailable(DashMediaSegment segment) throws InterruptedE Instant availabilityTime = segment.getStartAvailabilityTime(); Instant now = timeMachine.now(); if (availabilityTime.isAfter(now)) { - timeMachine.awaitMillis(Duration.between(availabilityTime, now).toMillis()); + timeMachine.awaitMillis(Duration.between(now, availabilityTime).toMillis()); } } @@ -219,11 +221,9 @@ private boolean hasContents() { return segmentBuilder != null; } + // dynamic manifests (live) or empty segment list require manifest get (timing determined elsewhere) private boolean needsManifestUpdate() { - return manifest.isDynamic() - && manifest.getMinimumUpdatePeriod() != null - && (manifest.getReloadTimeMillis(timeMachine.now()) <= 0 - || !segmentBuilder.hasNext() && !periods.hasNext()); + return manifest.isDynamic() || !segmentBuilder.hasNext() && !periods.hasNext(); } } diff --git a/src/main/java/com/blazemeter/jmeter/videostreaming/dash/Manifest.java b/src/main/java/com/blazemeter/jmeter/videostreaming/dash/Manifest.java index 7c8cd6d..6d3044c 100644 --- a/src/main/java/com/blazemeter/jmeter/videostreaming/dash/Manifest.java +++ b/src/main/java/com/blazemeter/jmeter/videostreaming/dash/Manifest.java @@ -6,6 +6,9 @@ import io.lindstrom.mpd.data.MPD; import io.lindstrom.mpd.data.Period; import io.lindstrom.mpd.data.PresentationType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.net.URI; import java.time.Duration; import java.time.Instant; @@ -17,13 +20,15 @@ public class Manifest { private final URI uri; private final MPD mpd; - private final Instant downloadTime; + private final Instant lastDownLoadTime; private final List periods; + private Instant playbackStartTime; + private static final Logger LOG = LoggerFactory.getLogger(Manifest.class); private Manifest(URI uri, MPD mpd, Instant timestamp) { this.uri = uri; this.mpd = mpd; - this.downloadTime = timestamp; + this.lastDownLoadTime = timestamp; this.periods = buildPeriods(mpd); } @@ -81,9 +86,13 @@ public Duration getMinimumUpdatePeriod() { return mpd.getMinimumUpdatePeriod(); } - public long getReloadTimeMillis(Instant now) { - return Math - .max(mpd.getMinimumUpdatePeriod().minus(Duration.between(downloadTime, now)).toMillis(), 0); + public long getReloadTimeMillis(long segmentDurationMillis) { + Duration minUpdatePeriod = mpd.getMinimumUpdatePeriod(); + // wait at least segment duration if minUpdatePeriod is 0 (or very small) + long maxIntervalTime = Math.max(minUpdatePeriod.toMillis(), segmentDurationMillis); + Instant now = Instant.now(); + + return Math.max(maxIntervalTime - Duration.between(lastDownLoadTime, now).toMillis(), 0); } public Duration getBufferStartTime() { @@ -97,7 +106,12 @@ public Duration getBufferStartTime() { public Instant getAvailabilityStartTime() { OffsetDateTime time = mpd.getAvailabilityStartTime(); - return time != null ? time.toInstant() : Instant.MIN; + // simulating availabilityStartTime for VOD enables pacing of segment GETs according to segment duration + if (time == null && playbackStartTime == null) { + LOG.info("setting playbackStartTime to now()"); + playbackStartTime = Instant.now(); + } + return time != null ? time.toInstant() : playbackStartTime; } } diff --git a/src/main/java/com/blazemeter/jmeter/videostreaming/hls/HlsSampler.java b/src/main/java/com/blazemeter/jmeter/videostreaming/hls/HlsSampler.java index 914800c..dfb7495 100644 --- a/src/main/java/com/blazemeter/jmeter/videostreaming/hls/HlsSampler.java +++ b/src/main/java/com/blazemeter/jmeter/videostreaming/hls/HlsSampler.java @@ -179,7 +179,7 @@ private Playlist tryDownloadPlaylist(URI uri, Function namer) } try { HTTPSampleResult playlistResult = httpClient.downloadUri(uri); - if (!uri.toString().contains(".m3u8")) { + if (!uri.toString().contains(".m3u8") && !uri.toString().contains("format=m3u8")) { String playlistName = namer.apply(null); sampleResultProcessor.accept(playlistName, playlistResult); return null;