-
macOS / Linux / Windows
-
Java 17/11 LTS
-
On macOS, SDKMAN! can be used to install one
sdk install java 17.0.4-amzn
-
In addition to the runtime environment
-
SBT 1.7.1
-
On macOS, SDKMAN! can be used to install one
sdk install sbt
-
On Windows MSI installer
- It might be older version but the correct version is automatically downloaded and used
-
Start sbt
-
Build and run the tool
tool/run
-
Examples
tool/run verify -in ../evip/out/ESA.pdf
-
-
Build the fat jar for distribution
tool/assembly
-
To run locally
java -jar tool/target/scala-3.1.3/tool-assembly-0.1.0.jar
-
The program to run is constructed by
-
The following imports are inserted at the beginning
import java.nio.file._ import scala.util._ import scala.util.Properties.{userHome => HOME, userName => USER} import cats._ import cats.syntax.all._ import cats.effect._ import cats.effect.syntax._ import fs2._ import jp.ken1ma.ffmpeg.FFmpegStream val ffmpegStream = FFmpegStream[IO] import ffmpegStream._
-
-i
and-o
are translated into value definitions--in=text
will bein = Path.of(s"$text")
--out=text
will beout = Path.of(s"$text")
-
-e
is is a raw Scala expression, except-
name = value
becomesval name = value
- When it starts with
name =
,val
is inserted at the beginning
- When it starts with
-
TODO: expr { name =>
-
TODO: Multiple expressions will be connected by
flatMap
?
-
-
Extract video frames
-
All the frames as JPEG files
tool/run run -i "$HOME/Downloads/Record of Lodoss War Opening [HD]-kagzOJsHBg4.mp4" -o "out/Lodoss-allFrames" -e "readFile(in).flatMap { fmtCtx =>" -e "stream = fmtCtx.videoStreams.head" -e "FrameFileGen.jpeg(dir = out, stream.width, stream.height).flatMap { fileGen =>" -e "streamFrames(stream, fmtCtx).map(_.frm).flatMap(fileGen)" --show-scala
-
The key frames as PNG files
tool/run run -i "$HOME/Downloads/Record of Lodoss War Opening [HD]-kagzOJsHBg4.mp4" -o "out/Lodoss-keyFrames" -e "readFile(in, dump = true).flatMap { fmtCtx =>" -e "stream = fmtCtx.videoStreams.head" -e "FrameFileGen.png(dir = out, stream.width, stream.height).flatMap { fileGen =>" -e "streamFrames(stream, fmtCtx).map(_.frm).filter(_.keyFrame).flatMap(fileGen)" --show-scala
-
-
Transcode a video to H264
tool/run run -i "$HOME/Downloads/Record of Lodoss War Opening [HD]-kagzOJsHBg4.mp4" -o "out/Lodoss.h264" -e "readFile(in).flatMap { fmtCtx =>" -e "stream = fmtCtx.videoStreams.head" -e "FileWrite.h264(out, stream.width, stream.height).flatMap { fileWrite =>" -e "streamFrames(stream, fmtCtx).map(_.frm).flatMap(fileWrite)" --show-scala
-
Skip even frames (frame_number starts at 1)
tool/run run -i "$HOME/Downloads/Record of Lodoss War Opening [HD]-kagzOJsHBg4.mp4" -o "out/Lodoss-oddFrames.h264" -e "readFile(in).flatMap { fmtCtx =>" -e "stream = fmtCtx.videoStreams.head" -e "FileWrite.h264(out, stream.width, stream.height).flatMap { fileWrite =>" -e "streamFrames(stream, fmtCtx).filter(_.decodeCtx.codec_ctx.frame_number % 2 == 1).map(_.frm).flatMap(fileWrite)" --show-scala
-
Halve the dimension by swscale
tool/run run -i "$HOME/Downloads/Record of Lodoss War Opening [HD]-kagzOJsHBg4.mp4" -o "out/Lodoss-scaled.h264" -e "readFile(in).flatMap { fmtCtx =>" -e "stream = fmtCtx.videoStreams.head" -e "FileWrite.h264(out, stream.width / 2, stream.height / 2).flatMap { fileWrite =>" -e "allocSwScaleContextWithDstFrm(stream.width, stream.height, AV_PIX_FMT_YUV420P, stream.width / 2, stream.height / 2, AV_PIX_FMT_YUV420P, logCtx = fmtCtx).flatMap { swScaleCtx =>" -e "streamFrames(stream, fmtCtx).map(_.frm).flatMap(srcFrm => scaleFrame(srcFrm, swScaleCtx)).flatMap(_ => fileWrite(swScaleCtx.dstFrm))" --show-scala
-
Halve the dimension by filter, add time text, only even frames
tool/run run -i "$HOME/Downloads/Record of Lodoss War Opening [HD]-kagzOJsHBg4.mp4" -o "out/Lodoss-filtered.h264" -e "timeOffset = Instant.now.getEpochSecond" -e "readFile(in).flatMap { fmtCtx =>" -e "stream = fmtCtx.videoStreams.head" -e "FileWrite.h264(out, stream.width / 2, stream.height / 2).flatMap { fileWrite =>" -e "allocFilter(s\\u0022scale=\${stream.width / 2}:\${stream.height / 2},drawtext=text='%{pts\\\\\\\\:localtime\\\\\\\\:\\u0022 + timeOffset + \\u0022\\\\\\\\:%Y/%m/%d %T}':fontfile=data/font/ipaexg.ttf:fontsize=20:fontcolor=red:x=w-tw-10:y=h-th-10\\u0022, stream.width, stream.height, AV_PIX_FMT_YUV420P, stream.time_base.num, stream.time_base.den, stream.sample_aspect_ratio.num, stream.sample_aspect_ratio.den, parentLogCtx = fmtCtx).flatMap { filterCtx =>" -e "streamFrames(stream, fmtCtx).filter(_.decodeCtx.codec_ctx.frame_number % 2 == 1).map(_.frm).flatMap(srcFrm => filterCtx.filterFrame(srcFrm, fileWrite))" --show-scala
- The unescaped drawText is
drawtext=text='%{pts\\:localtime\\:" + timeOffset + "\\:%Y/%m/%d %T}'
- The unescaped drawText is
-
-
Transcode an audio to aac
tool/run run -i "$HOME/Documents/convivial/Moomin.wav" -o "out/Moomin.aac" -e "readFile(in).flatMap { fmtCtx =>" -e "stream = fmtCtx.audioStreams.head" -e "FileWrite.aac(out, stream).flatMap { fileWrite =>" -e "streamFrames(stream, fmtCtx).flatMap(fileWrite)"
-
Run the unit tests
test
-
Delete the built files
clean
-
Search for dependency updates
dependencyUpdates
-
Show the dependencies
dependencyTree