From a33d3045c13155731aad1c1126cf68d605b3b966 Mon Sep 17 00:00:00 2001 From: 0xboobface <0xboobface@gmail.com> Date: Wed, 25 Dec 2019 19:23:53 +0100 Subject: [PATCH] Improve segment download retry code --- .../recorder/download/dash/DashDownload.java | 88 ++++++++++--------- 1 file changed, 47 insertions(+), 41 deletions(-) diff --git a/common/src/main/java/ctbrec/recorder/download/dash/DashDownload.java b/common/src/main/java/ctbrec/recorder/download/dash/DashDownload.java index c7e9d061..95ae64df 100644 --- a/common/src/main/java/ctbrec/recorder/download/dash/DashDownload.java +++ b/common/src/main/java/ctbrec/recorder/download/dash/DashDownload.java @@ -41,6 +41,8 @@ import okhttp3.Request; import okhttp3.Response; public class DashDownload extends AbstractDownload { + private static final String CONTENT_LENGTH = "Content-Length"; + private static final Logger LOG = LoggerFactory.getLogger(DashDownload.class); private int audioCounter = 0; @@ -139,16 +141,18 @@ public class DashDownload extends AbstractDownload { private int downloadInitChunksForVideoAndAudio(boolean isVideo, MPDtype mpd, SegmentTemplateType segmentTemplate, RepresentationType representation) throws IOException { + int loadedFileCount = 0; if (isVideo && !videoInitLoaded || !isVideo && !audioInitLoaded) { String initialization = segmentTemplate.getInitializationAttribute(); initialization = initialization.replaceAll("\\$RepresentationID\\$", representation.getId()); URL initUrl = new URL(new URL(mpd.getLocation().get(0)), initialization); File file = download(downloadDir.toFile().getCanonicalPath(), initUrl, isVideo); - setInitState(isVideo, file); - return 1; - } else { - return 0; + if (file != null) { + setInitState(isVideo, file); + loadedFileCount++; + } } + return loadedFileCount; } private void setInitState(boolean isVideo, File file) { @@ -177,34 +181,34 @@ public class DashDownload extends AbstractDownload { .header("Connection", "keep-alive") .build(); // @formatter:on int tries = 1; - try (Response response = httpClient.execute(request)) { - InputStream in = response.body().byteStream(); - String absFile = url.getFile(); - String prefix = isVideo ? "video" : "audio"; - int c = isVideo ? videoCounter++ : audioCounter++; - File segmentFile = new File(dir, prefix + '_' + df.format(c) + '_' + new File(absFile).getName()); - while (tries <= 10) { - if (!segmentFile.exists() || segmentFile.length() == 0) { - if (tries == 10) { - LOG.debug("Loading segment, try {}, {} {} {}", tries, response.code(), response.headers().values("Content-Length"), url); - } else { - LOG.trace("Loading segment, try {}, {} {} {}", tries, response.code(), response.headers().values("Content-Length"), url); - } - try (FileOutputStream out = new FileOutputStream(segmentFile)) { - byte[] b = new byte[1024]; - int len = -1; - while ((len = in.read(b)) >= 0) { - out.write(b, 0, len); - } - out.flush(); - } + while (tries <= 10) { + try (Response response = httpClient.execute(request)) { + if (!response.isSuccessful()) { + LOG.trace("Loading segment failed, try {}, {} size:{} {}", tries, response.code(), response.headers().values(CONTENT_LENGTH), url); + tries++; + waitSomeTime(tries * 80); } else { - break; + InputStream in = response.body().byteStream(); + String absFile = url.getFile(); + String prefix = isVideo ? "video" : "audio"; + int c = isVideo ? videoCounter++ : audioCounter++; + File segmentFile = new File(dir, prefix + '_' + df.format(c) + '_' + new File(absFile).getName()); + if (!segmentFile.exists() || segmentFile.length() == 0) { + try (FileOutputStream out = new FileOutputStream(segmentFile)) { + byte[] b = new byte[1024]; + int len = -1; + while ((len = in.read(b)) >= 0) { + out.write(b, 0, len); + } + out.flush(); + } + } + return segmentFile; } - tries++; } - return segmentFile; } + LOG.warn("Loading segment finally failed after {} tries {}", --tries, url); + return null; } @Override @@ -266,16 +270,7 @@ public class DashDownload extends AbstractDownload { for (PeriodType period : periods) { List videoStreams = new ArrayList<>(); List audioStreams = new ArrayList<>(); - - List adaptationSets = period.getAdaptationSet(); - for (AdaptationSetType adaptationSet : adaptationSets) { - String mimeType = adaptationSet.getMimeType(); - if (mimeType.equalsIgnoreCase("video/mp4")) { - videoStreams.add(adaptationSet); - } else if (mimeType.equalsIgnoreCase("audio/mp4")) { - audioStreams.add(adaptationSet); - } - } + splitAdaptionSets(period.getAdaptationSet(), videoStreams, audioStreams); downloadDir.toFile().mkdirs(); @@ -290,12 +285,23 @@ public class DashDownload extends AbstractDownload { if (downloaded == 0) { LOG.trace("No new segments - Sleeping a bit"); - waitSomeTime(); + waitSomeTime(1000); } } } } + private void splitAdaptionSets(List adaptationSets, List videoStreams, List audioStreams) { + for (AdaptationSetType adaptationSet : adaptationSets) { + String mimeType = adaptationSet.getMimeType(); + if (mimeType.equalsIgnoreCase("video/mp4")) { + videoStreams.add(adaptationSet); + } else if (mimeType.equalsIgnoreCase("audio/mp4")) { + audioStreams.add(adaptationSet); + } + } + } + private AdaptationSetType chooseBestVideo(List videoStreams) { AdaptationSetType best = null; int maxHeight = config.getSettings().maximumResolution; @@ -373,9 +379,9 @@ public class DashDownload extends AbstractDownload { * Causes the current thread to sleep for a short amount of time. This is used to slow down retries, if something is wrong with the playlist. E.g. HTTP 403 * or 404 */ - private void waitSomeTime() { + private void waitSomeTime(long ms) { try { - Thread.sleep(1000); + Thread.sleep(ms); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }