Reducción de tamaño de archivo de vídeo MediaMuxer (compresión, disminución de resolución)
Estoy buscando una forma eficiente de reducir el peso de algunos videos (como un File
, para cargar) y la respuesta obvia para eso es: permite reducir la resolución! (FullHD o 4K no es necesario, simple HD es suficiente para mí) He intentado muchas maneras que debería funcionar a través de lotes de APIs (necesario 10) y la mejor manera estaba usando android-ffmpeg-java , pero … en mi bonita Rápido dispositivo casi insignificante casi todo el proceso dura alrededor de length_of_video * 4 segundos y también este peso lib es de 9 Mb, esta cantidad aumenta mi tamaño de aplicación … No wai! (12 Mb a 1 Mb es buen resultado, pero todavía demasiados defectos)
Así que he decidido utilizar formas nativas de Android para hacer esto, MediaMuxer
y MediaCodec
– están disponibles desde API18 y API16 respectivelly (los usuarios de dispositivos más antiguos: lo siento, pero también a menudo tienen cámara de "baja resolución"). Abajo el método casi funciona – MediaMuxer
NO respetan MediaFormat.KEY_WIDTH
y MediaFormat.KEY_HEIGHT
– extraído El File
es "re-comprimido", el peso es un poco más pequeño, pero la resolución es la misma que en el File
vídeo original …
- Interrumpir la grabación de medios mediante programación. Camera.apk de samsung galaxy tiene `this.mMediaRecorder.pause ();` no funciona en mi código
- Decodificación de flujo de vídeo en Android de DJI drone
- ¿Establecer el tiempo de espera de MediaPlayer?
- Transmisión de lista de vídeos mediante Exoplayer
- Crear video de series de imágenes android
Por lo tanto, pregunta: ¿Cómo comprimir y volver a escala / cambiar la resolución de vídeo con MediaMuxer
y otras clases y métodos que lo acompañan?
public File getCompressedFile(String videoPath) throws IOException{ MediaExtractor extractor = new MediaExtractor(); extractor.setDataSource(videoPath); int trackCount = extractor.getTrackCount(); String filePath = videoPath.substring(0, videoPath.lastIndexOf(File.separator)); String[] splitByDot = videoPath.split("\\."); String ext=""; if(splitByDot!=null && splitByDot.length>1) ext = splitByDot[splitByDot.length-1]; String fileName = videoPath.substring(videoPath.lastIndexOf(File.separator)+1, videoPath.length()); if(ext.length()>0) fileName=fileName.replace("."+ext, "_out."+ext); else fileName=fileName.concat("_out"); final File outFile = new File(filePath, fileName); if(!outFile.exists()) outFile.createNewFile(); MediaMuxer muxer = new MediaMuxer(outFile.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); HashMap<Integer, Integer> indexMap = new HashMap<Integer, Integer>(trackCount); for (int i = 0; i < trackCount; i++) { extractor.selectTrack(i); MediaFormat format = extractor.getTrackFormat(i); String mime = format.getString(MediaFormat.KEY_MIME); if(mime!=null && mime.startsWith("video")){ int currWidth = format.getInteger(MediaFormat.KEY_WIDTH); int currHeight = format.getInteger(MediaFormat.KEY_HEIGHT); format.setInteger(MediaFormat.KEY_WIDTH, currWidth>currHeight ? 960 : 540); format.setInteger(MediaFormat.KEY_HEIGHT, currWidth>currHeight ? 540 : 960); //API19 MediaFormat.KEY_MAX_WIDTH and KEY_MAX_HEIGHT format.setInteger("max-width", format.getInteger(MediaFormat.KEY_WIDTH)); format.setInteger("max-height", format.getInteger(MediaFormat.KEY_HEIGHT)); } int dstIndex = muxer.addTrack(format); indexMap.put(i, dstIndex); } boolean sawEOS = false; int bufferSize = 256 * 1024; int offset = 100; ByteBuffer dstBuf = ByteBuffer.allocate(bufferSize); MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); muxer.start(); while (!sawEOS) { bufferInfo.offset = offset; bufferInfo.size = extractor.readSampleData(dstBuf, offset); if (bufferInfo.size < 0) { sawEOS = true; bufferInfo.size = 0; } else { bufferInfo.presentationTimeUs = extractor.getSampleTime(); bufferInfo.flags = extractor.getSampleFlags(); int trackIndex = extractor.getSampleTrackIndex(); muxer.writeSampleData(indexMap.get(trackIndex), dstBuf, bufferInfo); extractor.advance(); } } muxer.stop(); muxer.release(); return outFile; }
PD. Mucho material útil sobre muxer aquí , encima de las bases de código en MediaMuxerTest.java
, el método cloneMediaUsingMuxer
- El comando de cambio de velocidad falla cuando el flujo de audio no está presente en el video - ffmpeg
- Vídeo HTML5 Muestra una pantalla negra en carga
- Reproducción de un video local de aplicación (.mp4) en una vista web
- Vídeo remoto Android con Titanio
- Cómo obtener el ID de video de YouTube, incluidos los parámetros
- reproductor de vídeo android se bloquea reproduciendo archivos locales
- Problema de compatibilidad de vídeo: el video grabado en Android no se reproduce en el iPhone
- ¿Cómo acceder a EGL Image directamente desde Android Surface para su uso en el decodificador de video MediaCodec?
Basándome en bigflake.com/mediacodec/ (fuente impresionante de conocimiento sobre las clases de Media) He probado algunas maneras y finalmente ExtractDecodeEditEncodeMuxTest resultó muy útil. Esta prueba no fue descrita en el artículo sobre el sitio bigflake, pero se puede encontrar AQUÍ junto a otras clases mencionadas en el texto.
Por lo tanto, he copiado la mayor parte del código de arriba mencionado ExtractDecodeEditEncodeMuxTest
clase, reescribió algunos métodos y allí es: VideoResolutionChanger
. Por supuesto, además de cambiar la resolución, también vuelve a codificar los medios de salida. Esto me da 2Mb de vídeo HD de 16 Mb fullHD. ¡Bonito! ¡Y rápido! Todo el proceso es un poco más largo que la duración del archivo de vídeo de entrada en mi 4×2,5 GHz, 3 Gb de RAM, dispositivo Adreno 330 – por ejemplo, 10 segundos de entrada de vídeo -> 11-12 segundos de procesamiento. Con mencionado en cuestión ffmpeg-java
sería smth unos 40 segundos o más (y 9 Mb más para la aplicación). Para las pruebas he usado videos mp4 grabados con mi teléfono.
Aquí vamos:
VideoResolutionChanger:
@TargetApi(18) public class VideoResolutionChanger { private static final int TIMEOUT_USEC = 10000; private static final String OUTPUT_VIDEO_MIME_TYPE = "video/avc"; private static final int OUTPUT_VIDEO_BIT_RATE = 2048 * 1024; private static final int OUTPUT_VIDEO_FRAME_RATE = 30; private static final int OUTPUT_VIDEO_IFRAME_INTERVAL = 10; private static final int OUTPUT_VIDEO_COLOR_FORMAT = MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface; private static final String OUTPUT_AUDIO_MIME_TYPE = "audio/mp4a-latm"; private static final int OUTPUT_AUDIO_CHANNEL_COUNT = 2; private static final int OUTPUT_AUDIO_BIT_RATE = 128 * 1024; private static final int OUTPUT_AUDIO_AAC_PROFILE = MediaCodecInfo.CodecProfileLevel.AACObjectHE; private static final int OUTPUT_AUDIO_SAMPLE_RATE_HZ = 44100; private int mWidth = 1280; private int mHeight = 720; private String mOutputFile, mInputFile; public String changeResolution(File f) throws Throwable { mInputFile=f.getAbsolutePath(); String filePath = mInputFile.substring(0, mInputFile.lastIndexOf(File.separator)); String[] splitByDot = mInputFile.split("\\."); String ext=""; if(splitByDot!=null && splitByDot.length>1) ext = splitByDot[splitByDot.length-1]; String fileName = mInputFile.substring(mInputFile.lastIndexOf(File.separator)+1, mInputFile.length()); if(ext.length()>0) fileName=fileName.replace("."+ext, "_out.mp4"); else fileName=fileName.concat("_out.mp4"); final File outFile = new File(Environment.getExternalStorageDirectory(), fileName); if(!outFile.exists()) outFile.createNewFile(); mOutputFile=outFile.getAbsolutePath(); ChangerWrapper.changeResolutionInSeparatedThread(this); return mOutputFile; } private static class ChangerWrapper implements Runnable { private Throwable mThrowable; private VideoResolutionChanger mChanger; private ChangerWrapper(VideoResolutionChanger changer) { mChanger = changer; } @Override public void run() { try { mChanger.prepareAndChangeResolution(); } catch (Throwable th) { mThrowable = th; } } public static void changeResolutionInSeparatedThread(VideoResolutionChanger changer) throws Throwable { ChangerWrapper wrapper = new ChangerWrapper(changer); Thread th = new Thread(wrapper, ChangerWrapper.class.getSimpleName()); th.start(); th.join(); if (wrapper.mThrowable != null) throw wrapper.mThrowable; } } private void prepareAndChangeResolution() throws Exception { Exception exception = null; MediaCodecInfo videoCodecInfo = selectCodec(OUTPUT_VIDEO_MIME_TYPE); if (videoCodecInfo == null) return; MediaCodecInfo audioCodecInfo = selectCodec(OUTPUT_AUDIO_MIME_TYPE); if (audioCodecInfo == null) return; MediaExtractor videoExtractor = null; MediaExtractor audioExtractor = null; OutputSurface outputSurface = null; MediaCodec videoDecoder = null; MediaCodec audioDecoder = null; MediaCodec videoEncoder = null; MediaCodec audioEncoder = null; MediaMuxer muxer = null; InputSurface inputSurface = null; try { videoExtractor = createExtractor(); int videoInputTrack = getAndSelectVideoTrackIndex(videoExtractor); MediaFormat inputFormat = videoExtractor.getTrackFormat(videoInputTrack); MediaMetadataRetriever m = new MediaMetadataRetriever(); m.setDataSource(mInputFile); Bitmap thumbnail = m.getFrameAtTime(); int inputWidth = thumbnail.getWidth(), inputHeight = thumbnail.getHeight(); if(inputWidth>inputHeight){ if(mWidth<mHeight){ int w = mWidth; mWidth=mHeight; mHeight=w; } } else{ if(mWidth>mHeight){ int w = mWidth; mWidth=mHeight; mHeight=w; } } MediaFormat outputVideoFormat = MediaFormat.createVideoFormat(OUTPUT_VIDEO_MIME_TYPE, mWidth, mHeight); outputVideoFormat.setInteger( MediaFormat.KEY_COLOR_FORMAT, OUTPUT_VIDEO_COLOR_FORMAT); outputVideoFormat.setInteger(MediaFormat.KEY_BIT_RATE, OUTPUT_VIDEO_BIT_RATE); outputVideoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, OUTPUT_VIDEO_FRAME_RATE); outputVideoFormat.setInteger( MediaFormat.KEY_I_FRAME_INTERVAL, OUTPUT_VIDEO_IFRAME_INTERVAL); AtomicReference<Surface> inputSurfaceReference = new AtomicReference<Surface>(); videoEncoder = createVideoEncoder( videoCodecInfo, outputVideoFormat, inputSurfaceReference); inputSurface = new InputSurface(inputSurfaceReference.get()); inputSurface.makeCurrent(); outputSurface = new OutputSurface(); videoDecoder = createVideoDecoder(inputFormat, outputSurface.getSurface()); audioExtractor = createExtractor(); int audioInputTrack = getAndSelectAudioTrackIndex(audioExtractor); MediaFormat inputAudioFormat = audioExtractor.getTrackFormat(audioInputTrack); MediaFormat outputAudioFormat = MediaFormat.createAudioFormat(inputAudioFormat.getString(MediaFormat.KEY_MIME), inputAudioFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE), inputAudioFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT)); outputAudioFormat.setInteger(MediaFormat.KEY_BIT_RATE, OUTPUT_AUDIO_BIT_RATE); outputAudioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, OUTPUT_AUDIO_AAC_PROFILE); audioEncoder = createAudioEncoder(audioCodecInfo, outputAudioFormat); audioDecoder = createAudioDecoder(inputAudioFormat); muxer = new MediaMuxer(mOutputFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); changeResolution(videoExtractor, audioExtractor, videoDecoder, videoEncoder, audioDecoder, audioEncoder, muxer, inputSurface, outputSurface); } finally { try { if (videoExtractor != null) videoExtractor.release(); } catch(Exception e) { if (exception == null) exception = e; } try { if (audioExtractor != null) audioExtractor.release(); } catch(Exception e) { if (exception == null) exception = e; } try { if (videoDecoder != null) { videoDecoder.stop(); videoDecoder.release(); } } catch(Exception e) { if (exception == null) exception = e; } try { if (outputSurface != null) { outputSurface.release(); } } catch(Exception e) { if (exception == null) exception = e; } try { if (videoEncoder != null) { videoEncoder.stop(); videoEncoder.release(); } } catch(Exception e) { if (exception == null) exception = e; } try { if (audioDecoder != null) { audioDecoder.stop(); audioDecoder.release(); } } catch(Exception e) { if (exception == null) exception = e; } try { if (audioEncoder != null) { audioEncoder.stop(); audioEncoder.release(); } } catch(Exception e) { if (exception == null) exception = e; } try { if (muxer != null) { muxer.stop(); muxer.release(); } } catch(Exception e) { if (exception == null) exception = e; } try { if (inputSurface != null) inputSurface.release(); } catch(Exception e) { if (exception == null) exception = e; } } if (exception != null) throw exception; } private MediaExtractor createExtractor() throws IOException { MediaExtractor extractor; extractor = new MediaExtractor(); extractor.setDataSource(mInputFile); return extractor; } private MediaCodec createVideoDecoder(MediaFormat inputFormat, Surface surface) throws IOException { MediaCodec decoder = MediaCodec.createDecoderByType(getMimeTypeFor(inputFormat)); decoder.configure(inputFormat, surface, null, 0); decoder.start(); return decoder; } private MediaCodec createVideoEncoder(MediaCodecInfo codecInfo, MediaFormat format, AtomicReference<Surface> surfaceReference) throws IOException { MediaCodec encoder = MediaCodec.createByCodecName(codecInfo.getName()); encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); surfaceReference.set(encoder.createInputSurface()); encoder.start(); return encoder; } private MediaCodec createAudioDecoder(MediaFormat inputFormat) throws IOException { MediaCodec decoder = MediaCodec.createDecoderByType(getMimeTypeFor(inputFormat)); decoder.configure(inputFormat, null, null, 0); decoder.start(); return decoder; } private MediaCodec createAudioEncoder(MediaCodecInfo codecInfo, MediaFormat format) throws IOException { MediaCodec encoder = MediaCodec.createByCodecName(codecInfo.getName()); encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); encoder.start(); return encoder; } private int getAndSelectVideoTrackIndex(MediaExtractor extractor) { for (int index = 0; index < extractor.getTrackCount(); ++index) { if (isVideoFormat(extractor.getTrackFormat(index))) { extractor.selectTrack(index); return index; } } return -1; } private int getAndSelectAudioTrackIndex(MediaExtractor extractor) { for (int index = 0; index < extractor.getTrackCount(); ++index) { if (isAudioFormat(extractor.getTrackFormat(index))) { extractor.selectTrack(index); return index; } } return -1; } private void changeResolution(MediaExtractor videoExtractor, MediaExtractor audioExtractor, MediaCodec videoDecoder, MediaCodec videoEncoder, MediaCodec audioDecoder, MediaCodec audioEncoder, MediaMuxer muxer, InputSurface inputSurface, OutputSurface outputSurface) { ByteBuffer[] videoDecoderInputBuffers = null; ByteBuffer[] videoDecoderOutputBuffers = null; ByteBuffer[] videoEncoderOutputBuffers = null; MediaCodec.BufferInfo videoDecoderOutputBufferInfo = null; MediaCodec.BufferInfo videoEncoderOutputBufferInfo = null; videoDecoderInputBuffers = videoDecoder.getInputBuffers(); videoDecoderOutputBuffers = videoDecoder.getOutputBuffers(); videoEncoderOutputBuffers = videoEncoder.getOutputBuffers(); videoDecoderOutputBufferInfo = new MediaCodec.BufferInfo(); videoEncoderOutputBufferInfo = new MediaCodec.BufferInfo(); ByteBuffer[] audioDecoderInputBuffers = null; ByteBuffer[] audioDecoderOutputBuffers = null; ByteBuffer[] audioEncoderInputBuffers = null; ByteBuffer[] audioEncoderOutputBuffers = null; MediaCodec.BufferInfo audioDecoderOutputBufferInfo = null; MediaCodec.BufferInfo audioEncoderOutputBufferInfo = null; audioDecoderInputBuffers = audioDecoder.getInputBuffers(); audioDecoderOutputBuffers = audioDecoder.getOutputBuffers(); audioEncoderInputBuffers = audioEncoder.getInputBuffers(); audioEncoderOutputBuffers = audioEncoder.getOutputBuffers(); audioDecoderOutputBufferInfo = new MediaCodec.BufferInfo(); audioEncoderOutputBufferInfo = new MediaCodec.BufferInfo(); MediaFormat decoderOutputVideoFormat = null; MediaFormat decoderOutputAudioFormat = null; MediaFormat encoderOutputVideoFormat = null; MediaFormat encoderOutputAudioFormat = null; int outputVideoTrack = -1; int outputAudioTrack = -1; boolean videoExtractorDone = false; boolean videoDecoderDone = false; boolean videoEncoderDone = false; boolean audioExtractorDone = false; boolean audioDecoderDone = false; boolean audioEncoderDone = false; int pendingAudioDecoderOutputBufferIndex = -1; boolean muxing = false; while ((!videoEncoderDone) || (!audioEncoderDone)) { while (!videoExtractorDone && (encoderOutputVideoFormat == null || muxing)) { int decoderInputBufferIndex = videoDecoder.dequeueInputBuffer(TIMEOUT_USEC); if (decoderInputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) break; ByteBuffer decoderInputBuffer = videoDecoderInputBuffers[decoderInputBufferIndex]; int size = videoExtractor.readSampleData(decoderInputBuffer, 0); long presentationTime = videoExtractor.getSampleTime(); if (size >= 0) { videoDecoder.queueInputBuffer( decoderInputBufferIndex, 0, size, presentationTime, videoExtractor.getSampleFlags()); } videoExtractorDone = !videoExtractor.advance(); if (videoExtractorDone) videoDecoder.queueInputBuffer(decoderInputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); break; } while (!audioExtractorDone && (encoderOutputAudioFormat == null || muxing)) { int decoderInputBufferIndex = audioDecoder.dequeueInputBuffer(TIMEOUT_USEC); if (decoderInputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) break; ByteBuffer decoderInputBuffer = audioDecoderInputBuffers[decoderInputBufferIndex]; int size = audioExtractor.readSampleData(decoderInputBuffer, 0); long presentationTime = audioExtractor.getSampleTime(); if (size >= 0) audioDecoder.queueInputBuffer(decoderInputBufferIndex, 0, size, presentationTime, audioExtractor.getSampleFlags()); audioExtractorDone = !audioExtractor.advance(); if (audioExtractorDone) audioDecoder.queueInputBuffer(decoderInputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); break; } while (!videoDecoderDone && (encoderOutputVideoFormat == null || muxing)) { int decoderOutputBufferIndex = videoDecoder.dequeueOutputBuffer( videoDecoderOutputBufferInfo, TIMEOUT_USEC); if (decoderOutputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) break; if (decoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { videoDecoderOutputBuffers = videoDecoder.getOutputBuffers(); break; } if (decoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { decoderOutputVideoFormat = videoDecoder.getOutputFormat(); break; } ByteBuffer decoderOutputBuffer = videoDecoderOutputBuffers[decoderOutputBufferIndex]; if ((videoDecoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { videoDecoder.releaseOutputBuffer(decoderOutputBufferIndex, false); break; } boolean render = videoDecoderOutputBufferInfo.size != 0; videoDecoder.releaseOutputBuffer(decoderOutputBufferIndex, render); if (render) { outputSurface.awaitNewImage(); outputSurface.drawImage(); inputSurface.setPresentationTime( videoDecoderOutputBufferInfo.presentationTimeUs * 1000); inputSurface.swapBuffers(); } if ((videoDecoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { videoDecoderDone = true; videoEncoder.signalEndOfInputStream(); } break; } while (!audioDecoderDone && pendingAudioDecoderOutputBufferIndex == -1 && (encoderOutputAudioFormat == null || muxing)) { int decoderOutputBufferIndex = audioDecoder.dequeueOutputBuffer( audioDecoderOutputBufferInfo, TIMEOUT_USEC); if (decoderOutputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) break; if (decoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { audioDecoderOutputBuffers = audioDecoder.getOutputBuffers(); break; } if (decoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { decoderOutputAudioFormat = audioDecoder.getOutputFormat(); break; } ByteBuffer decoderOutputBuffer = audioDecoderOutputBuffers[decoderOutputBufferIndex]; if ((audioDecoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { audioDecoder.releaseOutputBuffer(decoderOutputBufferIndex, false); break; } pendingAudioDecoderOutputBufferIndex = decoderOutputBufferIndex; break; } while (pendingAudioDecoderOutputBufferIndex != -1) { int encoderInputBufferIndex = audioEncoder.dequeueInputBuffer(TIMEOUT_USEC); ByteBuffer encoderInputBuffer = audioEncoderInputBuffers[encoderInputBufferIndex]; int size = audioDecoderOutputBufferInfo.size; long presentationTime = audioDecoderOutputBufferInfo.presentationTimeUs; if (size >= 0) { ByteBuffer decoderOutputBuffer = audioDecoderOutputBuffers[pendingAudioDecoderOutputBufferIndex] .duplicate(); decoderOutputBuffer.position(audioDecoderOutputBufferInfo.offset); decoderOutputBuffer.limit(audioDecoderOutputBufferInfo.offset + size); encoderInputBuffer.position(0); encoderInputBuffer.put(decoderOutputBuffer); audioEncoder.queueInputBuffer( encoderInputBufferIndex, 0, size, presentationTime, audioDecoderOutputBufferInfo.flags); } audioDecoder.releaseOutputBuffer(pendingAudioDecoderOutputBufferIndex, false); pendingAudioDecoderOutputBufferIndex = -1; if ((audioDecoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) audioDecoderDone = true; break; } while (!videoEncoderDone && (encoderOutputVideoFormat == null || muxing)) { int encoderOutputBufferIndex = videoEncoder.dequeueOutputBuffer( videoEncoderOutputBufferInfo, TIMEOUT_USEC); if (encoderOutputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) break; if (encoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { videoEncoderOutputBuffers = videoEncoder.getOutputBuffers(); break; } if (encoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { encoderOutputVideoFormat = videoEncoder.getOutputFormat(); break; } ByteBuffer encoderOutputBuffer = videoEncoderOutputBuffers[encoderOutputBufferIndex]; if ((videoEncoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { videoEncoder.releaseOutputBuffer(encoderOutputBufferIndex, false); break; } if (videoEncoderOutputBufferInfo.size != 0) { muxer.writeSampleData( outputVideoTrack, encoderOutputBuffer, videoEncoderOutputBufferInfo); } if ((videoEncoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { videoEncoderDone = true; } videoEncoder.releaseOutputBuffer(encoderOutputBufferIndex, false); break; } while (!audioEncoderDone && (encoderOutputAudioFormat == null || muxing)) { int encoderOutputBufferIndex = audioEncoder.dequeueOutputBuffer( audioEncoderOutputBufferInfo, TIMEOUT_USEC); if (encoderOutputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) { break; } if (encoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { audioEncoderOutputBuffers = audioEncoder.getOutputBuffers(); break; } if (encoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { encoderOutputAudioFormat = audioEncoder.getOutputFormat(); break; } ByteBuffer encoderOutputBuffer = audioEncoderOutputBuffers[encoderOutputBufferIndex]; if ((audioEncoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { audioEncoder.releaseOutputBuffer(encoderOutputBufferIndex, false); break; } if (audioEncoderOutputBufferInfo.size != 0) muxer.writeSampleData( outputAudioTrack, encoderOutputBuffer, audioEncoderOutputBufferInfo); if ((audioEncoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) audioEncoderDone = true; audioEncoder.releaseOutputBuffer(encoderOutputBufferIndex, false); break; } if (!muxing && (encoderOutputAudioFormat != null) && (encoderOutputVideoFormat != null)) { outputVideoTrack = muxer.addTrack(encoderOutputVideoFormat); outputAudioTrack = muxer.addTrack(encoderOutputAudioFormat); muxer.start(); muxing = true; } } } private static boolean isVideoFormat(MediaFormat format) { return getMimeTypeFor(format).startsWith("video/"); } private static boolean isAudioFormat(MediaFormat format) { return getMimeTypeFor(format).startsWith("audio/"); } private static String getMimeTypeFor(MediaFormat format) { return format.getString(MediaFormat.KEY_MIME); } private static MediaCodecInfo selectCodec(String mimeType) { int numCodecs = MediaCodecList.getCodecCount(); for (int i = 0; i < numCodecs; i++) { MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i); if (!codecInfo.isEncoder()) { continue; } String[] types = codecInfo.getSupportedTypes(); for (int j = 0; j < types.length; j++) { if (types[j].equalsIgnoreCase(mimeType)) { return codecInfo; } } } return null; } }
Necesita también InputSurface
, OutputSurface
y TextureRender
, que se colocan al lado de ExtractDecodeEditEncodeMuxTest
(enlace HERE ). Ponga estos tres en el mismo paquete con VideoResolutionChanger
y VideoResolutionChanger
así:
try{ String pathToReEncodedFile = new VideoResolutionChanger().changeResolution(videoFilePath); }catch(Throwable t){/* smth wrong :( */}
Donde videoFilePath
se puede obtener de File
utilizando file.getAbsolutePath()
.
Sé que no es la manera más limpia y probablemente no más eficaz / eficiente, pero he estado buscando código similar para los últimos dos días y encontró mucho de los temas, que más me redireccionaron a INDE, ffmpeg o jcodec, otros quedaron sin Respuesta adecuada. Así que lo estoy dejando aquí, usar esto sabiamente!
LIMITACIONES:
- Por encima del uso-como-este fragmento no se puede iniciar en el hilo con looper, por ejemplo, dentro de la actividad. La mejor manera es crear IntentService y pasar el camino del archivo de entrada
String
inIntent
s extraBundle
. Entonces puede ejecutarchangeResolution
stright dentroonHandleIntent
; - API18 y superiores (se
MediaMuxer
introducidoMediaMuxer
); - API18 necesita, por supuesto,
WRITE_EXTERNAL_STORAGE
, API19 y superior tiene este "incorporado";
@fadden ¡ GRACIAS por su trabajo y apoyo! 🙂
El MediaMuxer no está involucrado en la compresión o escalado de vídeo. Todo lo que hace es tomar la salida H.264 de MediaCodec y envolverlo en un contenedor de archivos .mp4.
Mirando su código, está extrayendo unidades NAL con MediaExtractor y volviendo a envolverlas de inmediato con MediaMuxer. Esto debe ser extremadamente rápido y no tienen ningún impacto en el vídeo en sí, ya que sólo está re-envoltura de la H.264.
Para escalar el vídeo es necesario decodificar el vídeo con un decodificador de MediaCodec, alimentar las unidades NAL de MediaExtractor al mismo y volver a codificarlo con un codificador de MediaCodec, pasando los marcos a un MediaMuxer.
Has encontrado bigflake.com ; Ver también Grafika . Ninguno de estos tiene exactamente lo que usted está buscando, pero las diversas piezas están allí.
Lo mejor es decodificar a una superficie, no a ByteBuffer. Esto requiere API 18, pero para la cordura es mejor olvidar que MediaCodec existía antes de entonces. Y necesitará API 18 para MediaMuxer de todos modos.
Puede probar Intel INDE Media for Mobile, tutoriales en https://software.intel.com/en-us/articles/intel-inde-media-pack-for-android-tutorials . Tiene un ejemplo que muestra cómo usarlo para transcodificar = recomprimir archivos de vídeo.
Puede establecer una resolución menor y \ o bitrate a la salida para obtener un archivo más pequeño https://github.com/INDExOS/media-for-mobile/blob/master/Android/samples/apps/src/com/intel/inde/mp /samples/ComposerTranscodeCoreActivity.java
- AndEngine error al cargar la biblioteca
- ¿Cómo puedo importar com.google.android.gms. * En Android Studio usando una compilación Gradle?