forked from j62/ctbrec
Fix handling of recording structure
This commit is contained in:
parent
ce103853a3
commit
bdf7d99ef3
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue