Add new class RecordingDownload for recording downloads

This class inherits from MergedFfmpegHlsDownload, but doesn't create a
directory and start FFmpeg in the init method.
Moved the dedicated methods for recording downloads from
MergedFfmpegHlsDownload to RecordingDownload.
This commit is contained in:
0xb00bface 2021-01-30 13:03:42 +01:00
parent e48df6cd1f
commit 73eb13944c
3 changed files with 91 additions and 60 deletions

View File

@ -0,0 +1,88 @@
package ctbrec;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.util.concurrent.ExecutorService;
import com.iheartradio.m3u8.ParseException;
import com.iheartradio.m3u8.PlaylistException;
import ctbrec.io.HttpClient;
import ctbrec.io.HttpException;
import ctbrec.recorder.ProgressListener;
import ctbrec.recorder.download.hls.MergedFfmpegHlsDownload;
import ctbrec.recorder.download.hls.SegmentPlaylist;
import okhttp3.Request;
import okhttp3.Response;
public class RecordingDownload extends MergedFfmpegHlsDownload {
public RecordingDownload(HttpClient client) {
super(client);
}
@Override
public void init(Config config, Model model, Instant startTime, ExecutorService executorService) throws IOException {
this.config = config;
this.model = model;
this.startTime = startTime;
this.downloadExecutor = executorService;
splittingStrategy = initSplittingStrategy(config.getSettings());
}
public void downloadFinishedRecording(String segmentPlaylistUri, File target, ProgressListener progressListener, long sizeInBytes)
throws InvalidKeyException, NoSuchAlgorithmException, IllegalStateException, InterruptedException, IOException, ParseException, PlaylistException {
running = true;
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;
}
startFfmpegProcess(target);
for (int i = 0; i < 10 && ffmpegStdIn == null; i++) {
Thread.sleep(100);
}
SegmentPlaylist segmentPlaylist = getNextSegments(segmentPlaylistUri);
long loadedBytes = 0;
for (String segmentUrl : segmentPlaylist.segments) {
loadedBytes += downloadFile(segmentUrl, loadedBytes, sizeInBytes, progressListener);
int progress = (int) (loadedBytes / (double) sizeInBytes * 100);
progressListener.update(progress);
}
internalStop();
}
private long downloadFile(String fileUri, long loadedBytes, long totalBytes, ProgressListener progressListener) throws IOException {
long fileLoadedBytes = 0;
Request request = new Request.Builder().url(fileUri).addHeader("connection", "keep-alive").build();
try (Response response = client.execute(request)) {
if (response.isSuccessful()) {
InputStream in = response.body().byteStream();
byte[] b = new byte[1024 * 100];
int length = -1;
while ((length = in.read(b)) >= 0) {
ffmpegStdIn.write(b, 0, length);
fileLoadedBytes += length;
int progress = (int) ((loadedBytes + fileLoadedBytes) / (double) totalBytes * 100);
progressListener.update(progress);
}
} else {
throw new HttpException(response.code(), response.message());
}
}
return fileLoadedBytes;
}
}

View File

@ -32,6 +32,7 @@ import ctbrec.GlobalThreadPool;
import ctbrec.Model;
import ctbrec.Recording;
import ctbrec.Recording.State;
import ctbrec.RecordingDownload;
import ctbrec.StringUtil;
import ctbrec.event.EventBusHolder;
import ctbrec.event.RecordingStateChangedEvent;
@ -39,7 +40,6 @@ import ctbrec.io.UrlUtil;
import ctbrec.recorder.ProgressListener;
import ctbrec.recorder.Recorder;
import ctbrec.recorder.RecordingPinnedException;
import ctbrec.recorder.download.hls.MergedFfmpegHlsDownload;
import ctbrec.ui.AutosizeAlert;
import ctbrec.ui.CamrecApplication;
import ctbrec.ui.DesktopIntegration;
@ -702,7 +702,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener, Shutdown
download.start(url, target);
} else {
URL url = new URL(hlsBase + '/' + recording.getId() + "/playlist.m3u8");
MergedFfmpegHlsDownload download = new MergedFfmpegHlsDownload(CamrecApplication.httpClient);
RecordingDownload download = new RecordingDownload(CamrecApplication.httpClient);
download.init(config, recording.getModel(), Instant.now(), Executors.newSingleThreadExecutor());
LOG.info("Downloading {}", url);
download.downloadFinishedRecording(url.toString(), target, createDownloadListener(recording), recording.getSizeInByte());

View File

@ -3,9 +3,7 @@ package ctbrec.recorder.download.hls;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.nio.file.Files;
import java.time.Instant;
import java.util.concurrent.BlockingQueue;
@ -21,17 +19,12 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ctbrec.Config;
import ctbrec.Hmac;
import ctbrec.Model;
import ctbrec.OS;
import ctbrec.Recording;
import ctbrec.io.HttpClient;
import ctbrec.io.HttpException;
import ctbrec.recorder.FFmpeg;
import ctbrec.recorder.ProgressListener;
import ctbrec.recorder.download.ProcessExitedUncleanException;
import okhttp3.Request;
import okhttp3.Response;
public class MergedFfmpegHlsDownload extends AbstractHlsDownload {
@ -118,7 +111,7 @@ public class MergedFfmpegHlsDownload extends AbstractHlsDownload {
internalStop();
}
private void startFfmpegProcess(File target) {
protected void startFfmpegProcess(File target) {
try {
String[] cmdline = prepareCommandLine(target);
ffmpeg = new FFmpeg.Builder()
@ -204,56 +197,6 @@ public class MergedFfmpegHlsDownload extends AbstractHlsDownload {
// nothing to do
}
public void downloadFinishedRecording(String segmentPlaylistUri, File target, ProgressListener progressListener, long sizeInBytes) throws Exception {
running = true;
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;
}
startFfmpegProcess(target);
for (int i = 0; i < 10 && ffmpegStdIn == null; i++) {
Thread.sleep(100);
}
SegmentPlaylist segmentPlaylist = getNextSegments(segmentPlaylistUri);
long loadedBytes = 0;
for (String segmentUrl : segmentPlaylist.segments) {
loadedBytes += downloadFile(segmentUrl, loadedBytes, sizeInBytes, progressListener);
int progress = (int) (loadedBytes / (double) sizeInBytes * 100);
progressListener.update(progress);
}
internalStop();
}
private long downloadFile(String fileUri, long loadedBytes, long totalBytes, ProgressListener progressListener) throws IOException {
long fileLoadedBytes = 0;
Request request = new Request.Builder().url(fileUri).addHeader("connection", "keep-alive").build();
try (Response response = client.execute(request)) {
if (response.isSuccessful()) {
InputStream in = response.body().byteStream();
byte[] b = new byte[1024 * 100];
int length = -1;
while ((length = in.read(b)) >= 0) {
ffmpegStdIn.write(b, 0, length);
fileLoadedBytes += length;
int progress = (int) ((loadedBytes + fileLoadedBytes) / (double) totalBytes * 100);
progressListener.update(progress);
}
} else {
throw new HttpException(response.code(), response.message());
}
}
return fileLoadedBytes;
}
@Override
public boolean isSingleFile() {
return true;