Skip to content

Commit

Permalink
Deeply refactor FFprobeJAXB to manage multiple ffprobe XSD versions #73
Browse files Browse the repository at this point in the history
  • Loading branch information
hdsdi3g committed Jan 16, 2024
1 parent 4c73e44 commit 7d87ebe
Show file tree
Hide file tree
Showing 166 changed files with 87,157 additions and 815 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
secret-application.yml
test*.mov
test*.ts
test*.mkv
ffprobejaxb/.ffmpeg-compile/*

# Windows / macOS
desktop.ini
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# All Medialib projects upgrades needs

## 15.0.0

Deeply refactor FFprobeJAXB to manage multiple ffprobe XSD versions #73

## 14.1.0

Update parent starter project to 19.0.0 #80
Expand Down
9 changes: 0 additions & 9 deletions fflauncher/src/main/java/tv/hd3g/fflauncher/FFmpeg.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,10 @@
import java.io.File;
import java.time.Duration;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;

import org.ffmpeg.ffprobe.StreamType;

import tv.hd3g.fflauncher.progress.ProgressCallback;
import tv.hd3g.fflauncher.progress.ProgressListener;
import tv.hd3g.ffprobejaxb.FFprobeJAXB;
import tv.hd3g.processlauncher.ProcesslauncherBuilder;
import tv.hd3g.processlauncher.cmdline.Parameters;

Expand Down Expand Up @@ -103,11 +99,6 @@ public FFmpeg addFastStartMovMp4File() {
return this;
}

@Deprecated(forRemoval = true, since = "9.1.0")
public static Optional<StreamType> getFirstVideoStream(final FFprobeJAXB analysingResult) {// NOSONAR S1133
return analysingResult.getFirstVideoStream();
}

/**
* Used with hardware transcoding.
* @param deviceIdToUse -1 by default
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,10 @@ default void addHardwareVideoDecoding(final String source,
final var videoStream = oVideoStream.get();

final var codec = about.getCodecs().stream()
.filter(c -> (c.decodingSupported && c.name.equals(videoStream.getCodecName())))
.filter(c -> (c.decodingSupported && c.name.equals(videoStream.codecName())))
.findFirst()
.orElseThrow(() -> new MediaException("Can't found a valid decoder codec for " + videoStream
.getCodecName() + " in \"" + source + "\""));
.codecName() + " in \"" + source + "\""));

if (hardwareCodec == FFHardwareCodec.NV && about.isNVToolkitIsAvaliable()) {
final var oSourceCuvidCodecEngine = codec.decoders.stream().filter(decoder -> decoder
Expand All @@ -134,7 +134,7 @@ default void addHardwareVideoDecoding(final String source,
}

throw new MediaException("Can't found a valid hardware decoder on \"" + source + "\" (\"" + videoStream
.getCodecLongName() + "\")");
.codecLongName() + "\")");
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* This file is part of fflauncher.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* Copyright (C) hdsdi3g for hd3g.tv 2024
*
*/
package tv.hd3g.fflauncher.acm;

public record ACMSelectedInputChannel(InputAudioStream inputAudioStream, InputAudioChannelSelector channelSelector) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.ffmpeg.ffprobe.StreamType;

import tv.hd3g.fflauncher.acm.ACMSplitInStreamDefinitionFilter.SplittedOut;
import tv.hd3g.fflauncher.acm.OutputAudioStream.OutputAudioChannel;
import tv.hd3g.fflauncher.enums.ChannelLayout;
import tv.hd3g.fflauncher.filtering.FilterChains;
import tv.hd3g.ffprobejaxb.FFprobeJAXB;
import tv.hd3g.ffprobejaxb.FFprobeReference;
import tv.hd3g.ffprobejaxb.data.FFProbeStream;
import tv.hd3g.processlauncher.cmdline.Parameters;

/**
Expand Down Expand Up @@ -279,13 +279,14 @@ public List<Parameters> getMapParameters(final List<String> prependToMapList) {
* @return add non-audio sources (video, data) + getMapParameters
*/
public List<Parameters> getMapParameters(final List<FFprobeJAXB> sourceFiles,
final BiPredicate<Integer, StreamType> addNonAudioStreamFromSources) {
final BiPredicate<Integer, FFProbeStream> addNonAudioStreamFromSources) {

final var selectedFileStreams = new LinkedHashMap<Integer, StreamType>();
final var selectedFileStreams = new LinkedHashMap<Integer, FFProbeStream>();
for (var pos = 0; pos < sourceFiles.size(); pos++) {
final var fileIndex = pos;
sourceFiles.get(pos).getStreams().stream()
.filter(s -> FFprobeJAXB.filterVideoStream.test(s) || FFprobeJAXB.filterDataStream.test(s))
.filter(s -> FFprobeReference.filterVideoStream.test(s) || FFprobeReference.filterDataStream.test(
s))
.filter(s -> addNonAudioStreamFromSources.test(fileIndex, s))
.forEach(s -> selectedFileStreams.put(fileIndex, s));
}
Expand All @@ -294,7 +295,7 @@ public List<Parameters> getMapParameters(final List<FFprobeJAXB> sourceFiles,
.map(entry -> {
final var fileIndex = entry.getKey();
final var streamInFile = entry.getValue();
return fileIndex + ":" + streamInFile.getIndex();
return fileIndex + ":" + streamInFile.index();
})
.toList());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import java.util.Optional;
import java.util.regex.Pattern;

import tv.hd3g.fflauncher.acm.InputAudioStream.SelectedInputChannel;
import tv.hd3g.fflauncher.enums.ChannelLayout;
import tv.hd3g.fflauncher.enums.SourceNotFoundPolicy;
import tv.hd3g.fflauncher.enums.SourceNotFoundPolicy.SourceNotFoundException;
Expand Down Expand Up @@ -130,17 +129,17 @@ void mapFullStream() {
final var sourceFileIndex = Integer.parseInt(outputStreamTopologyRaw.split(":")[0]);
final var relativeSourceStreamIndex = Integer.parseInt(outputStreamTopologyRaw.split(":")[1]);
final var inputAudioStream = InputAudioStream.getFromRelativeIndexes(inputStreams,
sourceFileIndex,
relativeSourceStreamIndex);
sourceFileIndex,
relativeSourceStreamIndex);
if (inputAudioStream == null) {
applySourceNotFoundBehavior("Can't found input channel by file/stream indexes: "
+ sourceFileIndex + "/" + relativeSourceStreamIndex);
+ sourceFileIndex + "/" + relativeSourceStreamIndex);
return;
}

outputLayout = Optional.ofNullable(outputLayout).orElse(inputAudioStream.getLayout());
final var outputStream = new OutputAudioStream(outputLayout, outputFileIndex,
relativeOutStrmIdx);
relativeOutStrmIdx);

final var chSize = inputAudioStream.getLayout().getChannelSize();
for (var pos = 0; pos < chSize; pos++) {
Expand All @@ -157,17 +156,16 @@ void mapFullStream() {
* @return true for cancel out stream
*/
boolean mapSingleSourceAbsoluteChannel(final int absoluteInputChannel,
final List<SelectedInputChannel> selectedInputChannels) {
final List<ACMSelectedInputChannel> aCMSelectedInputChannels) {
final var selected = InputAudioStream.getFromAbsoluteIndex(inputStreams,
absoluteInputChannel);
absoluteInputChannel);
if (selected == null) {
return applySourceNotFoundBehavior("Can't found absolute input channel index: "
+ absoluteInputChannel);
+ absoluteInputChannel);
}
final var inputStream = selected.getInputAudioStream();
final var channelSelector = selected.getChannelSelector();
selectedInputChannels.add(inputStream.new SelectedInputChannel(inputStream,
channelSelector));
final var inputStream = selected.inputAudioStream();
final var channelSelector = selected.channelSelector();
aCMSelectedInputChannels.add(new ACMSelectedInputChannel(inputStream, channelSelector));
return false;
}

Expand All @@ -183,7 +181,7 @@ boolean applySourceNotFoundBehavior(final String message) {
* @return true for cancel out stream
*/
boolean mapSingleSourceRelativeChannel(final String absoluteInputRaw,
final List<SelectedInputChannel> selectedInputChannels) {
final List<ACMSelectedInputChannel> aCMSelectedInputChannels) {
final var absoluteInput = absoluteInputRaw.split(":");
if (absoluteInput.length != 3) {
throw new IllegalArgumentException("Invalid channel map entry: " + absoluteInputRaw);
Expand All @@ -192,20 +190,20 @@ boolean mapSingleSourceRelativeChannel(final String absoluteInputRaw,
final var relativeSourceStreamIndex = parseInt(absoluteInput[1]);
final var channelSelector = new InputAudioChannelSelector(parseInt(absoluteInput[2]));
final var inputStream = InputAudioStream.getFromRelativeIndexes(inputStreams,
sourceFileIndex,
relativeSourceStreamIndex);
sourceFileIndex,
relativeSourceStreamIndex);
if (inputStream == null) {
return applySourceNotFoundBehavior("Can't found file/stream index: "
+ sourceFileIndex + "/"
+ relativeSourceStreamIndex);
+ sourceFileIndex + "/"
+ relativeSourceStreamIndex);
}
selectedInputChannels.add(inputStream.new SelectedInputChannel(inputStream,
channelSelector));
aCMSelectedInputChannels.add(new ACMSelectedInputChannel(inputStream,
channelSelector));
return false;
}

void mapSingleChannels() {
final var selectedInputChannels = new ArrayList<SelectedInputChannel>();
final var selectedInputChannels = new ArrayList<ACMSelectedInputChannel>();
final var outputChannelTopologyRaw = outputStreamTopologyRaw.split("\\+");
/**
* For each source channel
Expand All @@ -215,10 +213,10 @@ void mapSingleChannels() {
boolean isStop;
try {
isStop = mapSingleSourceAbsoluteChannel(parseInt(absoluteInputRaw),
selectedInputChannels);
selectedInputChannels);
} catch (final NumberFormatException e) {
isStop = mapSingleSourceRelativeChannel(absoluteInputRaw,
selectedInputChannels);
selectedInputChannels);
}
if (isStop) {
return;
Expand All @@ -230,7 +228,7 @@ void mapSingleChannels() {
}
final var outputStream = new OutputAudioStream(outputLayout, outputFileIndex, relativeOutStrmIdx);
selectedInputChannels.forEach(selectedInput -> outputStream
.mapChannel(selectedInput.getInputAudioStream(), selectedInput.getChannelSelector()));
.mapChannel(selectedInput.inputAudioStream(), selectedInput.channelSelector()));
outputStreams.add(outputStream);
}

Expand All @@ -242,12 +240,12 @@ void extractMap() {
}

relativeOutStrmIdx = relativeOutStrIdxByOutFileIdx
.getOrDefault(outputFileIndex, -1) + 1;
.getOrDefault(outputFileIndex, -1) + 1;
relativeOutStrIdxByOutFileIdx.put(outputFileIndex, relativeOutStrmIdx);

outputStreamTopologyRaw = layoutMatcher.replaceAll("");
if (outputStreamTopologyRaw.indexOf('+') == -1
&& outputStreamTopologyRaw.split(":").length == 2) {
&& outputStreamTopologyRaw.split(":").length == 2) {
mapFullStream();
} else {
mapSingleChannels();
Expand All @@ -262,9 +260,9 @@ private void onChannelMapEntry(final String entry) {

List<OutputAudioStream> build() {
channelMap.stream()
.map(entry -> entry.trim().replace(" ", ""))
.filter(entry -> entry.isEmpty() == false)
.forEach(this::onChannelMapEntry);
.map(entry -> entry.trim().replace(" ", ""))
.filter(entry -> entry.isEmpty() == false)
.forEach(this::onChannelMapEntry);
return unmodifiableList(outputStreams);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,15 @@ public static List<InputAudioStream> getListFromAnalysis(final List<FFprobeJAXB>
for (var pos = 0; pos < sourcesAnalysis.size(); pos++) {
final var analysis = sourcesAnalysis.get(pos);
final var absoluteSourceIndex = pos;
analysis.getAudiosStreams()
.sorted((l, r) -> Integer.compare(l.getIndex(), r.getIndex()))
analysis.getAudioStreams()
.sorted((l, r) -> Integer.compare(l.index(), r.index()))
.map(as -> {
final var layout = as.getChannelLayout();
final var layout = as.channelLayout();
if (layout == null || layout.isEmpty()) {
return new InputAudioStream(ChannelLayout.getByChannelSize(as.getChannels()),
absoluteSourceIndex, as.getIndex());
return new InputAudioStream(ChannelLayout.getByChannelSize(as.channels()),
absoluteSourceIndex, as.index());
}
return new InputAudioStream(ChannelLayout.parse(layout), absoluteSourceIndex, as.getIndex());
return new InputAudioStream(ChannelLayout.parse(layout), absoluteSourceIndex, as.index());
})
.forEach(allSourceStreams::add);
}
Expand All @@ -87,35 +87,15 @@ public static InputAudioStream getFromRelativeIndexes(final List<InputAudioStrea
return null;
}

public class SelectedInputChannel {
private final InputAudioStream inputAudioStream;
private final InputAudioChannelSelector channelSelector;

SelectedInputChannel(final InputAudioStream inputAudioStream,
final InputAudioChannelSelector channelSelector) {
this.inputAudioStream = inputAudioStream;
this.channelSelector = channelSelector;
}

InputAudioStream getInputAudioStream() {
return inputAudioStream;
}

InputAudioChannelSelector getChannelSelector() {
return channelSelector;
}

}

public static SelectedInputChannel getFromAbsoluteIndex(final List<InputAudioStream> streamList,
public static ACMSelectedInputChannel getFromAbsoluteIndex(final List<InputAudioStream> streamList,
final int channelIndex) {
var totalChannelCount = 0;
for (final var inStream : streamList) {
final var layout = inStream.getLayout();
final var layoutSize = layout.getChannelSize();
final var relativeChannelIndex = channelIndex - totalChannelCount;
if (relativeChannelIndex < layoutSize) {
return inStream.new SelectedInputChannel(inStream,
return new ACMSelectedInputChannel(inStream,
new InputAudioChannelSelector(relativeChannelIndex));
}
totalChannelCount += layoutSize;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ private FFmpeg prepareFFmpeg() {
}

if (ffprobeResult != null) {
if (ffprobeResult.getAudiosStreams().count() == 0 && audioFilters.isEmpty() == false) {
if (ffprobeResult.getAudioStreams().count() == 0 && audioFilters.isEmpty() == false) {
throw new IllegalStateException("Can't apply audio filters if source don't have an audio stream!");
} else if (ffprobeResult.getFirstVideoStream().isEmpty() && videoFilters.isEmpty() == false) {
throw new IllegalStateException("Can't apply video filters if source don't have an video stream!");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,13 @@
import java.util.Objects;
import java.util.concurrent.ScheduledExecutorService;

import lombok.extern.slf4j.Slf4j;
import tv.hd3g.fflauncher.FFprobe;
import tv.hd3g.fflauncher.FFprobe.FFPrintFormat;
import tv.hd3g.fflauncher.enums.FFLogLevel;
import tv.hd3g.ffprobejaxb.FFprobeJAXB;
import tv.hd3g.processlauncher.cmdline.ExecutableFinder;
import tv.hd3g.processlauncher.cmdline.Parameters;

@Slf4j
public class ProbeMedia {

private final String execName;
Expand All @@ -42,7 +40,7 @@ public ProbeMedia(final ExecutableFinder executableFinder, final ScheduledExecut
}

public ProbeMedia(final String execName, final ExecutableFinder executableFinder,
final ScheduledExecutorService maxExecTimeScheduler) {
final ScheduledExecutorService maxExecTimeScheduler) {
this.execName = Objects.requireNonNull(execName);
this.executableFinder = Objects.requireNonNull(executableFinder);
this.maxExecTimeScheduler = Objects.requireNonNull(maxExecTimeScheduler);
Expand All @@ -56,7 +54,7 @@ private FFprobe internal() {
ffprobe.setMaxExecTimeScheduler(maxExecTimeScheduler);
ffprobe.setLogLevel(FFLogLevel.ERROR, false, false);
ffprobe.setFilterForLinesEventsToDisplay(l -> (l.isStdErr() && ffprobe.filterOutErrorLines().test(l
.getLine())));
.getLine())));

return ffprobe;
}
Expand All @@ -65,7 +63,7 @@ private FFprobeJAXB execute(final FFprobe ffprobe) {
final var rtFFprobe = ffprobe.execute(executableFinder);
final var textRetention = rtFFprobe.checkExecutionGetText();
final var stdOut = textRetention.getStdout(false, System.lineSeparator());
return new FFprobeJAXB(stdOut, warn -> log.warn(warn));
return FFprobeJAXB.load(stdOut);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ void testGetFirstVideoStream() throws IOException, InterruptedException, Executi
assertTrue(test_file.exists());

final var s = probeMedia.doAnalysing(test_file.getPath()).getFirstVideoStream().get();
assertEquals("ffv1", s.getCodecName());
assertEquals("ffv1", s.codecName());
}

@Test
Expand Down

0 comments on commit 7d87ebe

Please sign in to comment.