There are two steps that slow us down. Compress images in PNG / JPG and write them to disk. Both can be skipped if we directly encode the ffmpeg libs files instead of calling the ffmpeg command. (There are other improvements like GPU coding and multithreading, but much more complex.)
Some approaches to the code:
- Use only C / C ++ NDK for android programming. FFmpeg will happily work. But I think this is not an option here.
- Build it from scratch Java JNI. There is not much experience. I only know that this can bind java with c / C ++ libs.
- Some java shell. Fortunately, I found javacpp-presets . (There are others, but it's good enough and relevant.)
This library includes a good example , ported from the famous janger fpermg tutorial, although it is a demo.
We can try to write a multiplexer following the example of ffmpeg muxing.c .
import java.io.*; import org.bytedeco.javacpp.*; import static org.bytedeco.javacpp.avcodec.*; import static org.bytedeco.javacpp.avformat.*; import static org.bytedeco.javacpp.avutil.*; import static org.bytedeco.javacpp.swscale.*; public class Muxer { public class OutputStream { public AVStream Stream; public AVCodecContext Ctx; public AVFrame Frame; public SwsContext SwsCtx; public void setStream(AVStream s) { this.Stream = s; } public AVStream getStream() { return this.Stream; } public void setCodecCtx(AVCodecContext c) { this.Ctx = c; } public AVCodecContext getCodecCtx() { return this.Ctx; } public void setFrame(AVFrame f) { this.Frame = f; } public AVFrame getFrame() { return this.Frame; } public OutputStream() { Stream = null; Ctx = null; Frame = null; SwsCtx = null; } } public static void main(String[] args) throws IOException { Muxer t = new Muxer(); OutputStream VideoSt = t.new OutputStream(); AVOutputFormat Fmt = null; AVFormatContext FmtCtx = new AVFormatContext(null); AVCodec VideoCodec = null; AVDictionary Opt = null; SwsContext SwsCtx = null; AVPacket Pkt = new AVPacket(); int GotOutput; int InLineSize[] = new int[1]; String FilePath = "/path/xxx.mp4"; avformat_alloc_output_context2(FmtCtx, null, null, FilePath); Fmt = FmtCtx.oformat(); AVCodec codec = avcodec_find_encoder_by_name("libx264"); av_format_set_video_codec(FmtCtx, codec); VideoCodec = avcodec_find_encoder(Fmt.video_codec()); VideoSt.setStream(avformat_new_stream(FmtCtx, null)); AVStream stream = VideoSt.getStream(); VideoSt.getStream().id(FmtCtx.nb_streams() - 1); VideoSt.setCodecCtx(avcodec_alloc_context3(VideoCodec)); VideoSt.getCodecCtx().codec_id(Fmt.video_codec()); VideoSt.getCodecCtx().bit_rate(5120000); VideoSt.getCodecCtx().width(1920); VideoSt.getCodecCtx().height(1080); AVRational fps = new AVRational(); fps.den(25); fps.num(1); VideoSt.getStream().time_base(fps); VideoSt.getCodecCtx().time_base(fps); VideoSt.getCodecCtx().gop_size(10); VideoSt.getCodecCtx().max_b_frames(); VideoSt.getCodecCtx().pix_fmt(AV_PIX_FMT_YUV420P); if ((FmtCtx.oformat().flags() & AVFMT_GLOBALHEADER) != 0) VideoSt.getCodecCtx().flags(VideoSt.getCodecCtx().flags() | AV_CODEC_FLAG_GLOBAL_HEADER); avcodec_open2(VideoSt.getCodecCtx(), VideoCodec, Opt); VideoSt.setFrame(av_frame_alloc()); VideoSt.getFrame().format(VideoSt.getCodecCtx().pix_fmt()); VideoSt.getFrame().width(1920); VideoSt.getFrame().height(1080); av_frame_get_buffer(VideoSt.getFrame(), 32);
To port C code, you usually need to make a few changes:
- Basically, the job is to replace each access to an element of the C structure (
. And -> ) with java getter / setter. - There are also many C
& address operators, just delete them. - Change the C
NULL macro and C ++ nullptr pointer to a null Java object. - Codes used to check the result of a bool of type int in
if, for, while . You need to compare them with 0 in java.
And there may be other API changes, if referencing javacpp-presets docs , this will be fine.
Please note that here I have skipped all error handling codes. This may be required in real development / production.
source share