forked from j62/ctbrec
1
0
Fork 0

Fix handling of recording structure

This commit is contained in:
0xboobface 2019-12-21 17:04:27 +01:00
parent ce103853a3
commit bdf7d99ef3
4 changed files with 58 additions and 13 deletions

View File

@ -143,7 +143,7 @@ public class RecordingManager {
} }
} }
private void deleteEmptyParents(File parent) throws IOException { public static void deleteEmptyParents(File parent) throws IOException {
File recDir = new File(Config.getInstance().getSettings().recordingsDir); File recDir = new File(Config.getInstance().getSettings().recordingsDir);
while (parent != null && parent.list() != null && parent.list().length == 0) { while (parent != null && parent.list() != null && parent.list().length == 0) {
if (parent.equals(recDir)) { if (parent.equals(recDir)) {

View File

@ -11,6 +11,7 @@ import java.nio.file.Path;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.time.ZonedDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -31,6 +32,7 @@ import ctbrec.Model;
import ctbrec.Recording; import ctbrec.Recording;
import ctbrec.io.HttpClient; import ctbrec.io.HttpClient;
import ctbrec.recorder.download.Download; import ctbrec.recorder.download.Download;
import ctbrec.recorder.download.dash.FfmpegMuxer.ProcessExitedUncleanException;
import ctbrec.recorder.download.dash.SegmentTimelineType.S; import ctbrec.recorder.download.dash.SegmentTimelineType.S;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
@ -54,8 +56,10 @@ public class DashDownload implements Download {
private Path downloadDir; private Path downloadDir;
private String manifestUrl; private String manifestUrl;
private boolean running = false; private boolean running = false;
private ZonedDateTime splitRecStartTime;
private File targetFile; private File targetFile;
private File finalFile;
public DashDownload(HttpClient httpClient, String manifestUrl) { public DashDownload(HttpClient httpClient, String manifestUrl) {
this.httpClient = httpClient; this.httpClient = httpClient;
@ -194,18 +198,26 @@ public class DashDownload implements Download {
this.config = config; this.config = config;
this.model = model; this.model = model;
startTime = Instant.now(); startTime = Instant.now();
targetFile = Config.getInstance().getFileForRecording(model, "mp4"); finalFile = Config.getInstance().getFileForRecording(model, "mp4");
downloadDir = targetFile.getParentFile().toPath(); targetFile = new File(finalFile.getParentFile(), finalFile.getName() + ".part");
downloadDir = targetFile.toPath();
} }
@Override @Override
public void start() throws IOException { public void start() throws IOException {
try { try {
running = true; running = true;
splitRecStartTime = ZonedDateTime.now();
JAXBContext jc = JAXBContext.newInstance(MPDtype.class.getPackage().getName()); JAXBContext jc = JAXBContext.newInstance(MPDtype.class.getPackage().getName());
Unmarshaller u = jc.createUnmarshaller(); Unmarshaller u = jc.createUnmarshaller();
while (running && !Thread.currentThread().isInterrupted()) { while (running && !Thread.currentThread().isInterrupted()) {
downloadManifestAndItsSegments(u); downloadManifestAndItsSegments(u);
// split recordings
boolean split = splitRecording();
if (split) {
break;
}
} }
} catch (Exception e) { } catch (Exception e) {
LOG.error("Error while downloading dash stream", e); LOG.error("Error while downloading dash stream", e);
@ -218,6 +230,18 @@ public class DashDownload implements Download {
} }
} }
private boolean splitRecording() {
if(config.getSettings().splitRecordings > 0) {
Duration recordingDuration = Duration.between(splitRecStartTime, ZonedDateTime.now());
long seconds = recordingDuration.getSeconds();
if(seconds >= config.getSettings().splitRecordings) {
internalStop();
return true;
}
}
return false;
}
private void downloadManifestAndItsSegments(Unmarshaller u) throws IOException, JAXBException, ExecutionException { private void downloadManifestAndItsSegments(Unmarshaller u) throws IOException, JAXBException, ExecutionException {
String manifest = getManifest(manifestUrl); String manifest = getManifest(manifestUrl);
LOG.trace("Manifest: {}", manifest); LOG.trace("Manifest: {}", manifest);
@ -310,8 +334,11 @@ public class DashDownload implements Download {
@Override @Override
public void postprocess(Recording recording) { public void postprocess(Recording recording) {
try { try {
new FfmpegMuxer(downloadDir.toFile(), targetFile.getName()); new FfmpegMuxer(downloadDir.toFile(), finalFile);
} catch (IOException e) { targetFile = finalFile;
String path = recording.getPath();
recording.setPath(path.substring(0, path.length() - 5));
} catch (ProcessExitedUncleanException | IOException e) {
LOG.error("Error while merging dash segments", e); LOG.error("Error while merging dash segments", e);
} }
} }

View File

@ -19,7 +19,7 @@ public class FfmpegMuxer {
File segmentDir; File segmentDir;
public FfmpegMuxer(File segmentDir, String targetFileName) throws IOException { public FfmpegMuxer(File segmentDir, File targetFile) throws IOException {
this.segmentDir = segmentDir; this.segmentDir = segmentDir;
String[] videoSegments = segmentDir.list((dir, name) -> name.startsWith("video_")); String[] videoSegments = segmentDir.list((dir, name) -> name.startsWith("video_"));
Arrays.sort(videoSegments); Arrays.sort(videoSegments);
@ -31,7 +31,7 @@ public class FfmpegMuxer {
File mp4AudioTrack = new File(segmentDir, "audio.mp4"); File mp4AudioTrack = new File(segmentDir, "audio.mp4");
mergeSegments(audioSegments, mp4AudioTrack); mergeSegments(audioSegments, mp4AudioTrack);
int exitCode = mergeTracks(mp4VideoTrack, mp4AudioTrack, new File(segmentDir, targetFileName)); int exitCode = mergeTracks(mp4VideoTrack, mp4AudioTrack, targetFile);
if (exitCode == ALL_GOOD) { if (exitCode == ALL_GOOD) {
LOG.debug("Deleting merged video and audio tracks"); LOG.debug("Deleting merged video and audio tracks");
Files.delete(mp4VideoTrack.toPath()); Files.delete(mp4VideoTrack.toPath());
@ -39,6 +39,9 @@ public class FfmpegMuxer {
LOG.debug("Deleting segments"); LOG.debug("Deleting segments");
deleteFiles(segmentDir, videoSegments); deleteFiles(segmentDir, videoSegments);
deleteFiles(segmentDir, audioSegments); deleteFiles(segmentDir, audioSegments);
if (segmentDir.list().length == 0) {
Files.delete(segmentDir.toPath());
}
} else { } else {
throw new ProcessExitedUncleanException("FFMPEG exited with " + exitCode); throw new ProcessExitedUncleanException("FFMPEG exited with " + exitCode);
} }

View File

@ -18,20 +18,30 @@ import com.iheartradio.m3u8.PlaylistException;
import ctbrec.Config; import ctbrec.Config;
import ctbrec.Hmac; import ctbrec.Hmac;
import ctbrec.Model;
import ctbrec.OS; import ctbrec.OS;
import ctbrec.io.HttpClient; import ctbrec.io.HttpClient;
import ctbrec.io.StreamRedirectThread; import ctbrec.io.StreamRedirectThread;
import ctbrec.recorder.ProgressListener; import ctbrec.recorder.ProgressListener;
import ctbrec.recorder.RecordingManager;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
public class MergedHlsDownload extends HlsDownload { public class MergedHlsDownload extends HlsDownload {
private static final Logger LOG = LoggerFactory.getLogger(MergedHlsDownload.class); private static final Logger LOG = LoggerFactory.getLogger(MergedHlsDownload.class);
private File finalFile;
public MergedHlsDownload(HttpClient client) { public MergedHlsDownload(HttpClient client) {
super(client); super(client);
} }
@Override
public void init(Config config, Model model) {
super.init(config, model);
finalFile = Config.getInstance().getFileForRecording(model, "mp4");
}
@Override @Override
public void postprocess(ctbrec.Recording recording) { public void postprocess(ctbrec.Recording recording) {
super.postprocess(recording); super.postprocess(recording);
@ -40,11 +50,16 @@ public class MergedHlsDownload extends HlsDownload {
if (!playlist.exists()) { if (!playlist.exists()) {
super.generatePlaylist(recording); super.generatePlaylist(recording);
} }
File targetFile = new File(dir, "0merged.mp4");
try { try {
postprocess(playlist, targetFile); postprocess(playlist, finalFile);
recording.setPath(recording.getPath() + '/' + "0merged.mp4"); // TODO set the actual name String recordingsDir = Config.getInstance().getSettings().recordingsDir;
} catch (PostProcessingException e) { String path = finalFile.getAbsolutePath().substring(recordingsDir.length());
recording.setPath(path);
if (dir.list().length == 0) {
RecordingManager.deleteEmptyParents(dir);
}
} catch (PostProcessingException | IOException e) {
LOG.error("An error occurred during post-processing", e); LOG.error("An error occurred during post-processing", e);
} }
} }