Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Develop #239

Merged
merged 10 commits into from
Oct 19, 2021
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
strategy:
matrix:
os: [ ubuntu-latest, macos-latest, windows-latest ]
java-version: [ 8, 11, 14 ]
java-version: [ 8, 11, 17 ]
fail-fast: false

steps:
Expand Down
2 changes: 1 addition & 1 deletion .mvn/extensions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<extension>
<groupId>me.qoomon</groupId>
<artifactId>maven-git-versioning-extension</artifactId>
<version>6.5.0</version>
<version>7.1.1</version>
</extension>

</extensions>
9 changes: 1 addition & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Inspired by [ffmpeg-cli-wrapper](https://github.com/bramp/ffmpeg-cli-wrapper)

**OS**: Ubuntu, MacOS, Windows

**JDK**: 8, 11, 14
**JDK**: 8, 11, 17

# Usage

Expand Down Expand Up @@ -533,10 +533,3 @@ will have Java 9 bytecode (version 53).
```shell
mvn clean install -PJ9-module
```

## Unit Tests

Some code in the unit tests relies on having ffmpeg / ffprobe available on your `PATH`.
Other parts of the unit test code requires that you have configured `FFMPEG_BIN` as either an environment variable or as a Java system property.

You will need to ensure that both are set up correctly in order to run the unit tests successfully.
1 change: 1 addition & 0 deletions checkstyle.xml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
<module name="JavadocStyle"/>
<module name="MissingJavadocType"/>
<module name="MissingJavadocMethod"/>
<module name="NonEmptyAtclauseDescription"/>

<!-- Checks for Naming Conventions. -->
<!-- See https://checkstyle.org/config_naming.html -->
Expand Down
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.3.0</version>
<version>3.3.1</version>
<configuration>
<source>8</source>
</configuration>
Expand Down Expand Up @@ -251,7 +251,7 @@
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>8.45.1</version>
<version>9.0.1</version>
</dependency>
</dependencies>
<executions>
Expand All @@ -266,7 +266,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>3.14.0</version>
<version>3.15.0</version>
<configuration>
<failOnViolation>true</failOnViolation>
<printFailingErrors>true</printFailingErrors>
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/com/github/kokorin/jaffree/LogLevel.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,34 @@ public int code() {
return code;
}

/**
* Checks if passed in log level has the same or higher severity.
*
* @param other log level to compare with
* @return true if log level has the same or higher severity.
*/
public boolean isEqualOrHigher(final LogLevel other) {
return this.code() <= other.code();
}

/**
* Checks if this log level is {@link #INFO} or higher.
*
* @return true if {@link #INFO} or higher.
*/
public boolean isInfoOrHigher() {
return isEqualOrHigher(INFO);
}

/**
* Checks if this log level is {@link #ERROR} or higher.
*
* @return true if {@link #ERROR} or higher.
*/
public boolean isErrorOrHigher() {
return isEqualOrHigher(ERROR);
}

/**
* Returns LogLevel with specified code.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public BaseInput(final String input) {
* Set number of times input stream shall be looped. Loop 0 means no loop,
* loop -1 means infinite loop.
*
* @param streamLoop
* @param streamLoop number of loops
* @return this
*/
@SuppressWarnings("checkstyle:hiddenfield")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,10 @@ public ChannelInput(final String fileName, final SeekableByteChannel channel) {

/**
* Creates {@link ChannelInput}.
* <p>
* ffmpeg uses fileName's extension to autodetect input format
*
* @param channel byte channel
* @return ChannelInput
* @see #fromChannel(String, SeekableByteChannel)
*/
public static ChannelInput fromChannel(final SeekableByteChannel channel) {
return new ChannelInput(channel);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ public class FFmpegResultFuture {
/**
* Creates {@link FFmpegResultFuture}.
*
* @param resultFuture
* @param stopper
* @param resultFuture result future
* @param stopper stopper
*/
public FFmpegResultFuture(final CompletableFuture<FFmpegResult> resultFuture,
final Stopper stopper) {
final Stopper stopper) {
this.resultFuture = resultFuture;
this.stopper = stopper;
}
Expand Down Expand Up @@ -144,9 +144,10 @@ public boolean isDone() {
/**
* Returns a completion that can be used to chain operations after FFmpeg completes, using the
* {@link CompletionStage} Java 8 API.
*
* @return completion that will complete when ffmpeg completes normally, and will complete
* exceptionally with a {@link CancellationException} if ffmpeg is forcefully stopped or with a
* {@link JaffreeException} if an error occurs.
* exceptionally with a {@link CancellationException} if ffmpeg is forcefully stopped or with a
* {@link JaffreeException} if an error occurs.
*/
public CompletableFuture<FFmpegResult> toCompletableFuture() {
return resultFuture;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,17 @@

package com.github.kokorin.jaffree.ffmpeg;

import com.github.kokorin.jaffree.JaffreeException;
import com.github.kokorin.jaffree.LogLevel;
import com.github.kokorin.jaffree.log.LogMessage;
import com.github.kokorin.jaffree.log.LogMessageIterator;
import com.github.kokorin.jaffree.process.StdReader;
import com.github.kokorin.jaffree.util.LineIterator;
import com.github.kokorin.jaffree.process.BaseStdReader;
import com.github.kokorin.jaffree.util.ParseUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
* {@link FFmpegResultReader} reads ffmpeg stderr output, parses {@link FFmpegProgress} and
* {@link FFmpegResult} and passes unparsed output to {@link OutputListener} (if provided).
*/
public class FFmpegResultReader implements StdReader<FFmpegResult> {
public class FFmpegResultReader extends BaseStdReader<FFmpegResult> {
private final OutputListener outputListener;

private static final Logger LOGGER = LoggerFactory.getLogger(FFmpegResultReader.class);

/**
* Creates {@link FFmpegResultReader}.
*
Expand All @@ -50,92 +38,10 @@ public FFmpegResultReader(final OutputListener outputListener) {
}

/**
* Reads provided {@link InputStream} until it's depleted.
* <p>
* This method parses every line to detect ffmpeg log level. Lines with INFO log level are
* additionally parsed to get ffmpeg result (which may be null because of too strict log level)
*
* @param stdOut input stream to read from
* @return FFmpegResult if found or null
* @throws JaffreeException if IOException appears or ffmpeg ends with error message.
* @see FFmpeg#setLogLevel(LogLevel)
* {@inheritDoc}
*/
@SuppressWarnings("checkstyle:MissingSwitchDefault")
@Override
public FFmpegResult read(final InputStream stdOut) {
LogMessageIterator logMessageIterator = new LogMessageIterator(
new LineIterator(
new BufferedReader(new InputStreamReader(stdOut))
)
);

FFmpegResult result = null;
String errorMessage = null;

while (logMessageIterator.hasNext()) {
LogMessage logMessage = logMessageIterator.next();

if (logMessage.logLevel != null) {
switch (logMessage.logLevel) {
case TRACE:
LOGGER.trace(logMessage.message);
break;
case VERBOSE:
case DEBUG:
LOGGER.debug(logMessage.message);
break;
case INFO:
LOGGER.info(logMessage.message);
break;
case WARNING:
LOGGER.warn(logMessage.message);
break;
case ERROR:
case FATAL:
case PANIC:
case QUIET:
LOGGER.error(logMessage.message);
break;
}
} else {
LOGGER.info(logMessage.message);
}

if (logMessage.logLevel == LogLevel.INFO) {
FFmpegResult possibleResult = ParseUtil.parseResult(logMessage.message);

if (possibleResult != null) {
result = possibleResult;
}
}

if (outputListener != null && logMessage.logLevel != null
&& logMessage.logLevel.code() <= LogLevel.INFO.code()) {
outputListener.onOutput(logMessage.message);
}

if (logMessage.logLevel != null
&& logMessage.logLevel.code() <= LogLevel.ERROR.code()) {
if (errorMessage == null) {
errorMessage = logMessage.message;
} else {
errorMessage += "\n" + logMessage.message;
}
}
}

if (result != null) {
if (errorMessage != null) {
LOGGER.warn("One or more errors appeared during ffmpeg execution, "
+ "ignoring since result is available");
}
return result;
}

if (errorMessage != null) {
throw new JaffreeException("ffmpeg exited with message: " + errorMessage);
}

protected FFmpegResult defaultResult() {
return new FFmpegResult(
null,
null,
Expand All @@ -145,4 +51,17 @@ public FFmpegResult read(final InputStream stdOut) {
null
);
}

/**
* {@inheritDoc}
*/
@Override
protected FFmpegResult handleLogMessage(final LogMessage logMessage) {
if (outputListener != null && logMessage.logLevel != null
&& logMessage.logLevel.isInfoOrHigher()) {
outputListener.onOutput(logMessage.message);
}

return ParseUtil.parseResult(logMessage.message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,85 +17,22 @@

package com.github.kokorin.jaffree.ffprobe;

import com.github.kokorin.jaffree.JaffreeException;
import com.github.kokorin.jaffree.LogLevel;
import com.github.kokorin.jaffree.log.LogMessage;
import com.github.kokorin.jaffree.log.LogMessageIterator;
import com.github.kokorin.jaffree.process.StdReader;
import com.github.kokorin.jaffree.util.LineIterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import com.github.kokorin.jaffree.process.BaseStdReader;

/**
* {@link FFprobeLogReader} reads ffprobe output (from stderr), parses {@link FFprobeResult} and
* logs and sends logs to SLF4J with corresponding log level.
* {@link FFprobeLogReader} reads ffprobe output (from stderr), parses logs and sends logs
* to SLF4J with corresponding log level.
*/
public class FFprobeLogReader implements StdReader<FFprobeResult> {
private static final Logger LOGGER = LoggerFactory.getLogger(FFprobeLogReader.class);

public class FFprobeLogReader extends BaseStdReader<FFprobeResult> {
/**
* Reads ffprobe output from provided {@link InputStream} and parses {@link FFprobeResult} and
* logs.
* @param inputStream input stream to read from
* @return parsed {@link FFprobeResult}
* Does nothing as ffprobe prints logs and result in different output streams.
*
* @param logMessage log message
* @return null
*/
@SuppressWarnings("checkstyle:MissingSwitchDefault")
@Override
public FFprobeResult read(final InputStream inputStream) {
LogMessageIterator logMessageIterator = new LogMessageIterator(
new LineIterator(
new BufferedReader(new InputStreamReader(inputStream))
)
);

String errorMessage = null;
while (logMessageIterator.hasNext()) {
LogMessage logMessage = logMessageIterator.next();

if (logMessage.logLevel != null) {
switch (logMessage.logLevel) {
case TRACE:
LOGGER.trace(logMessage.message);
break;
case VERBOSE:
case DEBUG:
LOGGER.debug(logMessage.message);
break;
case INFO:
LOGGER.info(logMessage.message);
break;
case WARNING:
LOGGER.warn(logMessage.message);
break;
case ERROR:
case FATAL:
case PANIC:
case QUIET:
LOGGER.error(logMessage.message);
break;
}
} else {
LOGGER.info(logMessage.message);
}

if (logMessage.logLevel != null
&& logMessage.logLevel.code() <= LogLevel.ERROR.code()) {
if (errorMessage == null) {
errorMessage = logMessage.message;
} else {
errorMessage += "\n" + logMessage.message;
}
}
}

if (errorMessage != null) {
throw new JaffreeException("Finished with error message: " + errorMessage);
}

protected FFprobeResult handleLogMessage(final LogMessage logMessage) {
return null;
}
}
Loading