有没有办法在Android上进行图像压缩和保存更快?

Ars*_*nch 5 java compression android ffmpeg save

情况

我应该在我的应用程序中显示200-350帧动画.图像的分辨率为500x300.如果用户想要共享动画,我必须将其转换为视频.对于转换我使用ffmpeg命令.

ffmpeg -y -r 1 -i /sdcard/videokit/pic00%d.jpg -i /sdcard/videokit/in.mp3 -strict experimental -ar 44100 -ac 2 -ab 256k -b 2097152 -ar 22050 -vcodec mpeg4 -b 2097152 -s 320x240 /sdcard/videokit/out.mp4
Run Code Online (Sandbox Code Playgroud)

要将图像转换为视频,ffmpeg需要实际文件而不是Bitmap或byte [].

问题

将位图压缩到图像文件需要花费很多时间.210图像转换大约需要1分钟才能完成平均设备(HTC ONE m7).在同一设备上将图像文件转换为mp4大约需要15秒.所有用户必须等待大约1.5分钟.

我试过的

  1. 我改变comrpession格式形式PNG到JPEG(1.5分钟结果与JPEG压缩(质量= 80来实现),用PNG大约需要2-2.5分钟)成功
  2. 试图找到如何将字节[]或位图传递给ffmpeg - 没有成功.

  1. 有没有办法(库(甚至本机))使保存过程更快.
  2. 有没有办法将byte []或Bitmap对象(我的意思是png文件解压缩到Android Bitmap Class Object)传递给ffmpeg库视频创建方法
  3. 是否有任何其他工作库将在大约30秒(200帧)中从byte []或Bitmap对象创建mp4(或任何支持的格式(由主社交网络支持)).

hal*_*elf 1

有两个步骤会减慢我们的速度。将图像压缩为 PNG/JPG 并将其写入磁盘。如果我们直接针对 ffmpeg 库进行编码,而不是调用 ffmpeg 命令,则可以跳过这两个步骤。(还有其他改进,例如 GPU 编码和多线程,但要复杂得多。)

一些编码方法:

  1. 仅使用 C/C++ NDK 进行 android 编程。FFmpeg 会愉快地工作。但我想这不是一个选择。
  2. 通过 Java JNI 从头开始​​构建它。这里经验不多。我只知道这可以将 java 链接到 c/c++ 库。
  3. 一些java包装器。幸运的是我找到了javacpp-presets。(还有其他的,但这个已经足够好并且是最新的。)

该库包含一个从著名 dranger 的 ffmpeg 教程移植的很好的示例,尽管它是一个解复用器。

我们可以尝试按照 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);

        // should be at least Long or even BigInteger
        // it is a unsigned long in C
        int nextpts = 0;

        av_dump_format(FmtCtx, 0, FilePath, 1);
        avio_open(FmtCtx.pb(), FilePath, AVIO_FLAG_WRITE);

        avformat_write_header(FmtCtx, Opt);

        int[] got_output = { 0 };
        while (still_has_input) {

            // convert or directly copy your Bytes[] into VideoSt.Frame here
            // AVFrame structure has two important data fields: 
            // AVFrame.data (uint8_t*[]) and AVFrame.linesize (int[]) 
            // data includes pixel values in some formats and linesize is size of each picture line.
            // For example, if formats is RGB. linesize should has 3 valid values equaling to `image_width * 3`. And data will point to three arrays containing rgb values.
            // But I guess we'll need swscale() to convert pixel format here. From RGB to yuv420p (or other yuv family formats).

            Pkt = new AVPacket();
            av_init_packet(Pkt);

            VideoSt.getFrame().pts(nextpts++);
            avcodec_encode_video2(VideoSt.getCodecCtx(), Pkt, VideoSt.getFrame(), got_output);

            av_packet_rescale_ts(Pkt, VideoSt.getCodecCtx().time_base(), VideoSt.getStream().time_base());
            Pkt.stream_index(VideoSt.getStream().index());
            av_interleaved_write_frame(FmtCtx, Pkt);

            av_packet_unref(Pkt);
        }

        // get delayed frames
        for (got_output[0] = 1; got_output[0] != 0;) {
            Pkt = new AVPacket();
            av_init_packet(Pkt);

            avcodec_encode_video2(VideoSt.getCodecCtx(), Pkt, null, got_output);
            if (got_output[0] > 0) {
                av_packet_rescale_ts(Pkt, VideoSt.getCodecCtx().time_base(), VideoSt.getStream().time_base());
                Pkt.stream_index(VideoSt.getStream().index());
                av_interleaved_write_frame(FmtCtx, Pkt);
            }

            av_packet_unref(Pkt);
        }

        // free c structs
        avcodec_free_context(VideoSt.getCodecCtx());
        av_frame_free(VideoSt.getFrame());
        avio_closep(FmtCtx.pb());
        avformat_free_context(FmtCtx);
    }
}
Run Code Online (Sandbox Code Playgroud)

对于移植 C 代码,通常应该进行一些更改:

  • 主要工作是替换每个 C 结构成员对 java getter/setter 的访问(.和)。->
  • 另外还有很多C地址运算符&,删除即可。
  • 将 CNULL宏和 C++nullptr指针更改为 Java 空对象。
  • C 代码用于检查if, for, while. 必须将它们与java中的0进行比较。

并且可能还有其他API的变化,只要参考javacpp-presets docs就可以了。

请注意,我在这里省略了所有错误处理代码。实际开发/生产中可能需要它。