¿Hay alguna manera de hacer compresión de imágenes y ahorrar más rápido en Android?

La situación

Debo mostrar 200-350 marcos de animación en mi aplicación. Las imágenes tienen una resolución de 500×300. Si el usuario quiere compartir animación, tengo que convertirlo en vídeo. Para la conversión estoy usando el comando 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 

Para convertir imágenes a vídeo ffmpeg quiere archivos reales no Bitmap o byte [].

Problema

La compresión de mapas de bits a archivos de imagen toma mucho tiempo. 210 la conversión de la imagen tarda cerca de 1 minuto terminar en el dispositivo medio (HTC UNO m7). Convertir archivos de imagen a mp4 toma unos 15 segundos en el mismo dispositivo. Todos juntos el usuario tiene que esperar unos 1,5 minutos.

Lo que he intentado

  1. Cambié el formato del formato de la comrpession PNG a JPEG (el resultado de 1.5 minuto se alcanza con la compresión del JPEG (calidad = 80), con PNG toma cerca de 2-2.5 minutos) éxito
  2. Intentado encontrar cómo pase el byte [] o el mapa de bits a ffmpeg – no succes.

PREGUNTA

  1. ¿Hay alguna manera (biblioteca (incluso nativa)) para hacer el proceso de ahorro más rápido.
  2. ¿Hay alguna forma de pasar byte [] o objetos de mapa de bits (i significa png archivo descomprimido a Android Bitmap Class Object) a ffmpeg biblioteca de creación de vídeo método
  3. ¿Hay alguna otra biblioteca de trabajo que creará mp4 (o cualquier formato soportado (apoyado por redes sociales principales)) de byte [] o objetos de mapa de bits en unos 30 segundos (para 200 marcos).

Hay dos pasos que nos frenan. Comprimir imagen a PNG / JPG y escribir en disco. Ambos se pueden omitir si codificamos directamente contra ffmpeg libs, en lugar de llamar al comando ffmpeg. (Hay otras mejoras también, como la codificación de GPU y multithreading, pero mucho más complicado.)

Algunas aproximaciones al código:

  1. Utilice sólo C / C ++ NDK para la programación de Android. FFmpeg trabajará felizmente. Pero supongo que no es una opción aquí.
  2. Construirlo desde cero por Java JNI. No hay mucha experiencia aquí. Sólo sé que esto podría enlazar java a c / c ++ libs.
  3. Un contenedor java. Por suerte encontré javacpp-presets . (Hay otros también, pero éste es bastante bueno y hasta la fecha.)

Esta biblioteca incluye un buen ejemplo del famoso tutorial ffmpeg de dranger, aunque es una demuxing.

Podemos intentar escribir un muxing uno, siguiendo el ejemplo de muxing.c de ffmpeg.

 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); } } 

Para portar el código C, normalmente se deben realizar varios cambios:

  • En su mayor parte, el trabajo consiste en reemplazar cada miembro de estructura C que accede ( . Y -> ) a java getter / setter.
  • También hay muchos C dirección-de los operadores & , simplemente eliminarlos.
  • Cambiar la macro C NULL y el puntero nullptr C ++ al objeto nulo de Java.
  • C códigos utilizados para comprobar bool resultado de un int tipo en if, for, while . Hay que compararlos con 0 en java.

Y puede haber otros cambios en la API, siempre y cuando se haga referencia a los documentos predefinidos de javacpp , estará bien.

Tenga en cuenta que he omitido todos los códigos de manejo de errores aquí. Puede ser necesario en el desarrollo real / producción.

Puede convertir Bitmap (o byte []) al formato YUV rápidamente, utilizando renderscript (consulte https://stackoverflow.com/a/39877029/192373 ). Puede pasar estos marcos YUV a la biblioteca ffmpeg (como sugiere halfelf ), o usar el built-in nativo MediaCodec que utiliza hardware dedicado en los dispositivos modt (pero las opciones de compresión son menos flexibles que todos los software ffmpeg).

Realmente no quiero hacer publicidad, pero para utilizar pkzip y su SDK puede ser una buena

solución. Pkzip comprime el archivo al 95% como dicen.

El SDK de Smartcrypt está disponible en todos los principales lenguajes de programación, incluyendo C ++, Java y C #, y puede usarse para cifrar datos estructurados y no estructurados. Los cambios en las aplicaciones existentes normalmente constan de dos o tres líneas de código.

  • Buscar enum etiqueta por valor
  • MojoExecutionException: Maven con Android
  • Lista de palabras reservadas en Android
  • R.id.container no resuelto
  • Dibujar complejo dibujable en api inferior a 23
  • Deshacer y rehacer en lienzo para Android
  • Determinar un nivel de zoom razonable para Google Maps dada la precisión de la ubicación
  • Almacene, convierta a vídeo una pantalla de Android de la corriente
  • Aplicación de Android que utiliza Socket para enviar y recibir mensajes:
  • Ejecución de scripts de Groovy incrustados en Java en tiempo de ejecución para Android
  • Android.os.NetworkOnMainThreadException para webservice (ksoap)
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.