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);
while (parent != null && parent.list() != null && parent.list().length == 0) {
if (parent.equals(recDir)) {

View File

@ -11,6 +11,7 @@ import java.nio.file.Path;
import java.text.DecimalFormat;
import java.time.Duration;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@ -31,6 +32,7 @@ import ctbrec.Model;
import ctbrec.Recording;
import ctbrec.io.HttpClient;
import ctbrec.recorder.download.Download;
import ctbrec.recorder.download.dash.FfmpegMuxer.ProcessExitedUncleanException;
import ctbrec.recorder.download.dash.SegmentTimelineType.S;
import okhttp3.Request;
import okhttp3.Response;
@ -54,8 +56,10 @@ public class DashDownload implements Download {
private Path downloadDir;
private String manifestUrl;
private boolean running = false;
private ZonedDateTime splitRecStartTime;
private File targetFile;
private File finalFile;
public DashDownload(HttpClient httpClient, String manifestUrl) {
this.httpClient = httpClient;
@ -166,7 +170,7 @@ public class DashDownload implements Download {
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) {
while (tries <= 10) {
if (!segmentFile.exists() || segmentFile.length() == 0) {
if (tries > 1) {
LOG.debug("Loading segment, try {}, {} {} {}", tries, response.code(), response.headers().values("Content-Length"), url);
@ -194,18 +198,26 @@ public class DashDownload implements Download {
this.config = config;
this.model = model;
startTime = Instant.now();
targetFile = Config.getInstance().getFileForRecording(model, "mp4");
downloadDir = targetFile.getParentFile().toPath();
finalFile = Config.getInstance().getFileForRecording(model, "mp4");
targetFile = new File(finalFile.getParentFile(), finalFile.getName() + ".part");
downloadDir = targetFile.toPath();
}
@Override
public void start() throws IOException {
try {
running = true;
splitRecStartTime = ZonedDateTime.now();
JAXBContext jc = JAXBContext.newInstance(MPDtype.class.getPackage().getName());
Unmarshaller u = jc.createUnmarshaller();
while (running && !Thread.currentThread().isInterrupted()) {
downloadManifestAndItsSegments(u);
// split recordings
boolean split = splitRecording();
if (split) {
break;
}
}
} catch (Exception 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 {
String manifest = getManifest(manifestUrl);
LOG.trace("Manifest: {}", manifest);
@ -276,7 +300,7 @@ public class DashDownload implements Download {
if (running) {
internalStop();
try {
while(running) {
while (running) {
synchronized (downloadFinished) {
downloadFinished.wait(TimeUnit.SECONDS.toMillis(1));
}
@ -310,8 +334,11 @@ public class DashDownload implements Download {
@Override
public void postprocess(Recording recording) {
try {
new FfmpegMuxer(downloadDir.toFile(), targetFile.getName());
} catch (IOException e) {
new FfmpegMuxer(downloadDir.toFile(), finalFile);
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);
}
}

View File

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

View File

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