Switch to MergedFfmpegHlsDownload
This commit is contained in:
parent
4f9c1606fc
commit
bc929cc6e1
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue