4.1.0 release
Two new features, three fixes, and a large clean up of the build and the documentation.
The bundled ffmpeg is unchanged at 9.0.1, and the set of published packages is unchanged from 4.0.0.
Read this before upgrading
Two changes alter behaviour rather than only adding to it.
abortEncoding() now actually aborts
ProcessWrapper.destroy() closed the process streams before killing the process. Closing a pipe does not wake a thread that is already blocked reading it, so control never reached the kill until the reader returned by itself, which for a process that had not finished meant not at all.
Reading ffmpeg's output while it runs is the normal case, so in practice Encoder.abortEncoding() waited for the encoding to finish and then reported success, and MultimediaObject.getInfo(long) did the same with its deadline. Both looked correct from the outside, which is presumably why this went unnoticed for so long. A test that took 120 seconds to report a 250 millisecond timeout now takes 0.3.
If you built waiting or timeout logic around the old behaviour, it is no longer needed.
slf4j-api moved from 1.7.36 to 2.0.18
slf4j 2 finds its binding through the ServiceLoader rather than through StaticLoggerBinder. If your project still uses an slf4j 1.7 binding, logback-classic 1.2.x or slf4j-simple 1.7.x, and picks 2.0.18 up transitively from here, you will see No SLF4J providers were found and lose your logging until the binding is moved to a 2.x one.
Only the api is used, so pinning slf4j-api to 1.7.36 in your own build is equally valid.
New
Two pass encoding (#156)
VideoAttributes video = new VideoAttributes();
video.setCodec("libx264");
video.setBitRate(1200000); // the budget the two passes exist to spend well
EncodingAttributes attrs = new EncodingAttributes();
attrs.setOutputFormat("mp4");
attrs.setAudioAttributes(audio);
attrs.setVideoAttributes(video);
attrs.setTwoPass(true);ffmpeg is run twice over the same input. The first run encodes the video only to measure it, writing what it learns to a statistics file and discarding the pictures; the second reads those measurements back and decides where the bitrate is worth spending. On material that is not uniformly difficult the same budget buys noticeably better quality, at roughly twice the time.
A video bitrate is required, since that is the budget being planned, and validate() says so rather than running ffmpeg twice for nothing. It is the wrong tool alongside setCrf, which asks for a quality rather than a size. The statistics file is unique to each encoding, lives in the temporary directory, and is removed afterwards. The two passes are reported to an EncoderProgressListener as one encoding, each taking half of the 0..1000 range, with sourceInfo called once at the start and done() once at the end.
Every channel layout is recognised (#45)
AudioInfo.getChannels() matched only mono, stereo and quad, so a 5.1 or 7.1 file reported -1, meaning unavailable.
Layouts whose name carries the count are now worked out arithmetically, so 5.1 is 6 and 7.1.4 is 12, and a layout ffmpeg adds later needs no change here. Only the names that say nothing about their size are tabulated. A bracketed qualifier such as 5.1(side) says which channels, not how many. ffmpeg's unnamed fallback, 6 channels, is handled too.
The new test checks this against ffmpeg -layouts itself rather than against a list written by hand, so the two cannot drift apart. All 40 standard layouts pass.
Fixed
Nonsense progress on sources with no duration (#269)
A percentage needs a total, and some sources do not declare one: live streams, and webm files from browser recorders, which commonly carry no duration in the header. Concatenated sources have no single duration either.
The permil was calculated anyway, dividing by -1 or by 0, and the only clamp was an upper one, so a large negative number went straight to the listener looking like progress. That is what made progress bars misbehave on webm input.
progress() is now called with the new EncoderProgressListener.PROGRESS_UNKNOWN when no proportion can be calculated, and a real permil is clamped at both ends. Listeners are still called at the same points, so code that only wanted to know work was happening is unaffected, and anything drawing a bar now has a documented value to switch to an indeterminate one on.
An intermittent test failure
DefaultFFMPEGLocatorTest cleared the shared temporary directory so the locator had to extract a binary, but that directory belongs to the whole build and windows will not delete an executable a process has just finished with, so the cleanup threw and took the test with it. Cleanup is now best effort, and the test asserts on what the locator returns instead.
Build and dependencies
selenium-java 2.44.0 and com.opera:operadriver 1.5 were test dependencies that nothing referenced. They dated from 2014 and pulled about forty transitive artifacts of the same vintage onto the test classpath, commons-collections 3.2.1, guava 14.0, httpclient 4.3.4, xalan 2.7.1 among them, which is a lot for any vulnerability scanner to complain about in exchange for nothing at all. The test dependency tree drops from 56 artifacts to 13. commons-lang3 was reaching EncoderTest through that accident and is now declared properly.
jave-core-test depended on logback-classic, which nothing configured, while the api resolved to slf4j 1.7. The binding never matched the api, so the library's own debug logging silently produced nothing while tests ran. It works now.
Build plugins to their latest stable releases: compiler 3.15.0, resources 3.5.0, surefire 3.5.6, javadoc 3.12.0, jar 3.5.1, source 3.4.0, gpg 3.2.8, deploy 3.1.4, release 3.3.1, scm-provider-gitexe 2.2.1, buildnumber 3.3.0, templating 3.1.0. JUnit moves to 5.14.4 rather than to 6.x, which requires Java 17 and would raise the floor for the test modules.
Documentation
The wiki had drifted a long way from the library. Usage still documented version 2.4.2, attrs.setFormat() and the retired jave-native-* artifact names, and every example on the Examples page used setFormat, which has not existed for years, so none of them compiled. Supported formats predated this fork entirely, listing codecs ffmpeg dropped over a decade ago while omitting libopus, libvpx-vp9, libx265 and libaom-av1.
- Usage, rewritten
- Examples, and Examples.md, which now carry the same content
- Encoding Attributes, regenerated from the source, every setter listed
- Custom ffmpeg arguments, a new page
- Supported formats, generated from the bundled ffmpeg 9.0.1
- Developers guide lines, expanded from seven lines
The usage examples have moved out of the README, so there is one place to keep current instead of three. Every Java snippet in the documentation was compiled against jave-core before publishing, which caught six wrong API calls.
Closes #45, #59, #155, #156, #233, #269.
Full changelog: 4.0.0...4.1.0