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

时间:2016-10-11 11:44:46

标签: java android ffmpeg save compression

情况

我应该在我的应用程序中显示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

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

问题

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

我尝试了什么

  1. 我将PNG格式转换为JPEG格式(1.5分钟结果为 使用JPEG压缩(质量= 80)实现,使用PNG需要 2-2.5分钟)成功
  2. 试图找到如何将字节[]或位图传递给ffmpeg - 没有成功。
  3. 问题

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

3 个答案:

答案 0 :(得分:2)

您可以使用renderscript快速将Bitmap(或byte [])转换为YUV格式(请参阅https://stackoverflow.com/a/39877029/192373)。您可以将这些YUV帧传递给ffmpeg库(作为suggests halfelf ),或使用内置的本地MediaCodec,它使用modt设备上的专用硬件(但压缩选项不如全软件ffmpeg灵活。

答案 1 :(得分:1)

有两个步骤让我们慢下来。将图像压缩为PNG / JPG并将其写入磁盘。如果我们直接对ffmpeg libs进行编码而不是调用ffmpeg命令,则可以跳过这两种方法。 (还有其他一些改进,比如GPU编码和多线程,但要复杂得多。)

一些代码方法:

  1. 仅使用C / C ++ NDK进行Android编程。 FFmpeg很乐意工作。但我想这不是一个选择。
  2. Java JNI从头开始构建它。这里没什么经验。我只知道这可以将java链接到c / c ++ libs。
  3. 一些java包装器。幸运的是我找到了javacpp-presets。 (还有其他一些,但这个很好并且是最新的。)
  4. 这个库包含从着名的dranger的ffmpeg教程移植的a good example,虽然它是一个解复用的。

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

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

    • 主要工作是将每个C struct成员访问权限(.->)替换为java getter / setter。
    • 还有许多C地址的操作符&,只需删除它们。
    • 将C NULL宏和C ++ nullptr指针更改为Java null对象。
    • 用于检查if, for, while中int类型的bool结果的C代码。必须将它们与java中的0进行比较。

    可能还有其他API更改,只要引用javacpp-presets docs,就可以了。

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

答案 2 :(得分:-1)

真的我不想宣传但是要使用pkzip,它的SDK可能会很好

溶液。正如他们所说,Pkzip将文件压缩到95%。

Smartcrypt SDK提供所有主要编程语言,包括C ++,Java和C#,可用于加密结构化和非结构化数据。对现有应用程序的更改通常包含两行或三行代码。