Manevolent / ffmpeg4j

A Java OOP FFmpeg wrapper around the JavaCPP FFmpeg native library bindings
Apache License 2.0
105 stars 15 forks source link

Transcode usage question (MP4) #5

Closed pomadchin closed 1 year ago

pomadchin commented 3 years ago

Hello,

I'm trying to read a video file, convert it into another format and keep the output as the OutputStream.

I've tried to follow README, and the read of the input file part works great.

However, I'm trying to use Transcoder and I could not figure out the right way to specify the TargetStream.

val targetStream = new FFmpegTargetStream(
  outputFormatName,
  output,
  new FFmpegTargetStream.FFmpegNativeOutput()
)

targetStream.getSubstreams().size() // is always 0

Transcoder.convert(sourceStream, targetStream, 2d)

And it causes Transcoder failure:

Output #0, mp4, to '(null)':
[mp4 @ 0x7f859a06fa00] No streams to mux were specified
[info] - convert avi to ffmpeg *** FAILED ***
[info]   java.lang.RuntimeException: com.github.manevolent.ffmpeg4j.FFmpegException: @pool-1-thread-1-ScalaTest-running-FfmpegSpec: ffmpeg/avformat_write_header: Invalid argument (code=-22)
[info]   at com.github.manevolent.ffmpeg4j.stream.output.FFmpegTargetStream.writeHeader(FFmpegTargetStream.java:117)
[info]   at com.github.manevolent.ffmpeg4j.transcoder.Transcoder.transcode(Transcoder.java:49)

I've tried to set manually output sub streams:

targetStream.registerVideoSubstream(
  avcodec.avcodec_find_encoder_by_name("libopenh264"),
  defaultVideoSubstream.getFormat.getWidth,
  defaultVideoSubstream.getFormat.getHeight,
  defaultVideoSubstream.getFormat.getFramesPerSecond,
  new util.HashMap[String, String]()
)

But it leads to another issue:

Input #0, avi, from '':
  Metadata:
    encoder         : Lavf55.0.0
  Duration: 00:02:27.64, start: 0.000000, bitrate: N/A
    Stream #0:0: Video: wmv2 (WMV2 / 0x32564D57), yuv420p, 640x480, 25 fps, 25 tbr, 25 tbn, 25 tbc
null
640x480 @25.0FPS 0Kbps
---------
---------
[libx264 @ 0x7fd2444f1e00] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2
[libx264 @ 0x7fd2444f1e00] profile High 4:4:4 Predictive, level 3.0, 4:4:4 8-bit
[libx264 @ 0x7fd2444f1e00] 264 - core 155 - H.264/MPEG-4 AVC codec - Copyleft 2003-2018 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x1:0x111 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=0 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=4 threads=6 lookahead_threads=1 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=25 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=crf mbtree=1 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00
Output #0, mp4, to '(null)':
    Stream #0:0: Unknown: none (libx264)
[mp4 @ 0x7fd2444f1800] Using AVStream.codec to pass codec parameters to muxers is deprecated, use AVStream.codecpar instead.
[mp4 @ 0x7fd2444f1800] muxer does not support non seekable output
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x0000000122e29f74, pid=84800, tid=0x0000000000004403
#
# JRE version: Java(TM) SE Runtime Environment (8.0_162-b12) (build 1.8.0_162-b12)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.162-b12 mixed mode bsd-amd64 compressed oops)
# Problematic frame:
# C  [libavformat.58.dylib+0xa7f74]  avformat_get_mov_audio_tags+0x33dc4
#
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#
# An error report file with more information is saved as:
# /Users/daunnc/subversions/git/github/alimentiv-video-annotation/application/hs_err_pid84800.log
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#

Could you guide me into a proper direction? I guess I'm just using the API wrong.

pomadchin commented 3 years ago

Ouf, I guess this is an mp4 limitation, hm, is it still possible to transcode into mp4 via ffmpeg4j?

Manevolent commented 3 years ago

To preface this, I have just read up on some of the comments in this SO post: https://stackoverflow.com/questions/34123272/ffmpeg-transmux-mpegts-to-mp4-gives-error-muxer-does-not-support-non-seekable Looking at the muxer output error message, I am inclined to believe that there is a compatibility issue between the nature of how ffmpeg4j streams I/O using InputStream/OutputStream, and not extending any seek support into ffmpeg itself. Somewhere in the code-base, I either set or allow seek support to 0 to indicate this to ffmpeg. It probably would be possible to add another sister layer that leverages SeekableByteChannel, etc. Though, one of the main objectives of ffmpeg4j for me was to build a framework that didn't necessarily require seeking, as this project is what I use in https://github.com/Manebot/audio to do a lot of in-memory piping of audio data to listeners on Discord, TS3, etc.

But, for now, I do think that's correct -- given the limitations of MP4 and ffmpeg4j, I don't think this is possible right now.

pomadchin commented 3 years ago

@Manevolent perfect, thanks for a very quick answer.

Manevolent commented 1 year ago

Seekable output is now supported and lightly tested for MP4. Refer to new method introduced by commit a6712074388323f02764797b32356c26cd00e5a7:

FFmpegIO.openChannel(SeekableByteChannel, int)