From bc929cc6e1303ae1776fea79f9a96c12e89cdcb6 Mon Sep 17 00:00:00 2001 From: 0xboobface <0xboobface@gmail.com> Date: Sat, 22 Feb 2020 14:33:51 +0100 Subject: [PATCH] Switch to MergedFfmpegHlsDownload --- .../download/hls/MergedHlsDownload.java | 201 ------------------ .../sites/fc2live/Fc2MergedHlsDownload.java | 4 +- .../jasmin/LiveJasminMergedHlsDownload.java | 4 +- 3 files changed, 4 insertions(+), 205 deletions(-) delete mode 100644 common/src/main/java/ctbrec/recorder/download/hls/MergedHlsDownload.java diff --git a/common/src/main/java/ctbrec/recorder/download/hls/MergedHlsDownload.java b/common/src/main/java/ctbrec/recorder/download/hls/MergedHlsDownload.java deleted file mode 100644 index 54209a07..00000000 --- a/common/src/main/java/ctbrec/recorder/download/hls/MergedHlsDownload.java +++ /dev/null @@ -1,201 +0,0 @@ -package ctbrec.recorder.download.hls; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.nio.file.Files; -import java.time.Duration; -import java.time.Instant; -import java.util.Arrays; -import java.util.Objects; -import java.util.regex.Pattern; - -import org.jcodec.containers.mp4.MP4Util; -import org.jcodec.containers.mp4.boxes.MovieBox; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import ctbrec.Config; -import ctbrec.Hmac; -import ctbrec.Model; -import ctbrec.OS; -import ctbrec.io.HttpClient; -import ctbrec.io.HttpException; -import ctbrec.io.StreamRedirectThread; -import ctbrec.recorder.ProgressListener; -import ctbrec.recorder.RecordingManager; -import ctbrec.recorder.download.ProcessExitedUncleanException; -import okhttp3.Request; -import okhttp3.Response; - -public class MergedHlsDownload extends HlsDownload { - private static final Logger LOG = LoggerFactory.getLogger(MergedHlsDownload.class); - - private File finalFile; - private File targetFile; - - public MergedHlsDownload(HttpClient client) { - super(client); - } - - @Override - public void init(Config config, Model model, Instant startTime) { - super.init(config, model, startTime); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - finalFile = Config.getInstance().getFileForRecording(model, "mp4", startTime); - targetFile = new File(finalFile.getParentFile(), finalFile.getName() + ".part"); - downloadDir = targetFile.toPath(); - } - - @Override - public void postprocess(ctbrec.Recording recording) { - Thread.currentThread().setName("PP " + model.getName()); - try { - File playlist = super.generatePlaylist(recording); - Objects.requireNonNull(playlist, "Generated playlist is null"); - - postprocess(playlist, finalFile); - String recordingsDir = Config.getInstance().getSettings().recordingsDir; - String path = finalFile.getAbsolutePath().substring(recordingsDir.length()); - recording.setPath(path); - File dir = playlist.getParentFile(); - if (dir.list().length == 0) { - RecordingManager.deleteEmptyParents(dir); - } - runPostProcessingScript(recording); - } catch (Exception e) { - throw new PostProcessingException(e); - } - } - - private void postprocess(File playlist, File target) { - File mergeLog = new File(playlist.getParentFile(), "merge.log"); - try { - File dir = playlist.getParentFile(); - // @formatter:off - String[] cmdline = OS.getFFmpegCommand( - "-i", playlist.getAbsolutePath(), - "-c:v", "copy", - "-c:a", "copy", - "-movflags", "faststart", // for streaming - "-y", // overwrite existing files - "-f", "mp4", - target.getAbsolutePath() - ); - // @formatter:on - LOG.debug("Command line: {}", Arrays.toString(cmdline)); - Process ffmpeg = Runtime.getRuntime().exec(cmdline, new String[0], playlist.getParentFile()); - int exitCode = 1; - try (FileOutputStream mergeLogStream = new FileOutputStream(mergeLog)) { - Thread stdout = new Thread(new StreamRedirectThread(ffmpeg.getInputStream(), mergeLogStream)); - Thread stderr = new Thread(new StreamRedirectThread(ffmpeg.getErrorStream(), mergeLogStream)); - stdout.start(); - stderr.start(); - exitCode = ffmpeg.waitFor(); - stdout.join(); - stderr.join(); - mergeLogStream.flush(); - } - if (exitCode == 0) { - Files.delete(playlist.toPath()); - Files.deleteIfExists(mergeLog.toPath()); - File[] segments = dir.listFiles((directory, filename) -> filename.endsWith(".ts") || filename.endsWith(".corrupt")); - for (File segment : segments) { - Files.delete(segment.toPath()); - } - } else { - throw new ProcessExitedUncleanException("FFmpeg exit code was " + exitCode); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - LOG.error("Interrupted while waiting for FFMPEG", e); - } catch (IOException e) { - LOG.error("Couldn't execute FFMPEG", e); - } - } - - public void downloadFinishedRecording(String segmentPlaylistUri, File target, ProgressListener progressListener) - throws Exception { - if (Config.getInstance().getSettings().requireAuthentication) { - URL u = new URL(segmentPlaylistUri); - String path = u.getPath(); - byte[] key = Config.getInstance().getSettings().key; - if (!Config.getInstance().getContextPath().isEmpty()) { - path = path.substring(Config.getInstance().getContextPath().length()); - } - String hmac = Hmac.calculate(path, key); - segmentPlaylistUri = segmentPlaylistUri + "?hmac=" + hmac; - } - - File tempDir = new File(target.getParentFile(), "ctbrec-download-tmp-" + target.getName()); - Files.createDirectories(tempDir.toPath()); - - downloadFile(segmentPlaylistUri, tempDir); - SegmentPlaylist segmentPlaylist = getNextSegments(segmentPlaylistUri); - int fileCounter = 0; - for (String segmentUrl : segmentPlaylist.segments) { - downloadFile(segmentUrl, tempDir); - fileCounter++; - int total = segmentPlaylist.segments.size(); - int progress = (int) (fileCounter / (double) total * 100); - progressListener.update(progress); - } - - File downloadedPlaylist = new File(tempDir, "playlist.m3u8"); - postprocess(downloadedPlaylist, target); - Files.delete(tempDir.toPath()); - } - - private void downloadFile(String fileUri, File tempDir) throws IOException { - LOG.trace("Downloading file {} to {}", fileUri, tempDir); - Request request = new Request.Builder().url(fileUri).addHeader("connection", "keep-alive").build(); - try (Response response = client.execute(request)) { - if (response.isSuccessful()) { - InputStream in = null; - File file = new File(request.url().encodedPath()); - File downloadedSegment = new File(tempDir, file.getName()); - try (FileOutputStream fos = new FileOutputStream(downloadedSegment)) { - in = response.body().byteStream(); - byte[] b = new byte[1024 * 100]; - int length = -1; - while ((length = in.read(b)) >= 0) { - fos.write(b, 0, length); - } - } - } else { - throw new HttpException(response.code(), response.message()); - } - } - } - - @Override - public Duration getLength() { - try { - MovieBox movieBox = MP4Util.parseMovie(finalFile); - double lengthInSeconds = (double) movieBox.getDuration() / movieBox.getTimescale(); - return Duration.ofSeconds((long) Math.ceil(lengthInSeconds)); - } catch (IOException e) { - LOG.error("Couldn't determine length of MP4 file {}", getTarget(), e); - return Duration.ofSeconds(0); - } - } - - @Override - public File getTarget() { - return targetFile; - } - - @Override - public String getPath(Model model) { - String absolutePath = targetFile.getAbsolutePath(); - String recordingsDir = Config.getInstance().getSettings().recordingsDir; - String relativePath = absolutePath.replaceFirst(Pattern.quote(recordingsDir), ""); - return relativePath; - } -} diff --git a/common/src/main/java/ctbrec/sites/fc2live/Fc2MergedHlsDownload.java b/common/src/main/java/ctbrec/sites/fc2live/Fc2MergedHlsDownload.java index 446f7408..f6fac7e5 100644 --- a/common/src/main/java/ctbrec/sites/fc2live/Fc2MergedHlsDownload.java +++ b/common/src/main/java/ctbrec/sites/fc2live/Fc2MergedHlsDownload.java @@ -6,9 +6,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ctbrec.io.HttpClient; -import ctbrec.recorder.download.hls.MergedHlsDownload; +import ctbrec.recorder.download.hls.MergedFfmpegHlsDownload; -public class Fc2MergedHlsDownload extends MergedHlsDownload { +public class Fc2MergedHlsDownload extends MergedFfmpegHlsDownload { private static final Logger LOG = LoggerFactory.getLogger(Fc2MergedHlsDownload.class); diff --git a/common/src/main/java/ctbrec/sites/jasmin/LiveJasminMergedHlsDownload.java b/common/src/main/java/ctbrec/sites/jasmin/LiveJasminMergedHlsDownload.java index ae93a6c1..046aa7d2 100644 --- a/common/src/main/java/ctbrec/sites/jasmin/LiveJasminMergedHlsDownload.java +++ b/common/src/main/java/ctbrec/sites/jasmin/LiveJasminMergedHlsDownload.java @@ -13,9 +13,9 @@ import com.iheartradio.m3u8.ParseException; import com.iheartradio.m3u8.PlaylistException; import ctbrec.io.HttpClient; -import ctbrec.recorder.download.hls.MergedHlsDownload; +import ctbrec.recorder.download.hls.MergedFfmpegHlsDownload; -public class LiveJasminMergedHlsDownload extends MergedHlsDownload { +public class LiveJasminMergedHlsDownload extends MergedFfmpegHlsDownload { private static final Logger LOG = LoggerFactory.getLogger(LiveJasminMergedHlsDownload.class); private long lastMasterPlaylistUpdate = 0;