Intereting Posts
Не удается получить push-уведомление от Urban Airship Сохранение данных SQLite после обновления Просмотр не может быть привязан к родительскому координаторуLayout INSTALL_FAILED_CPU_ABI_INCOMPATIBLE при установке приложений на целевую страницу, чтобы получить это сообщение об ошибке, как решить проблему? Установите FAB (плавающая кнопка действия) над клавиатурой Получить минимум из столбца базы данных SQLite? Android переместить приложение в sd Android: NullPointerException: попытка вызвать виртуальный метод «int android.graphics.Bitmap.getWidth ()» в ссылке на нулевой объект Поддержка ориентации естественного датчика Android Закрытие окна предупреждения, когда пользователь коснулся пустого экрана в Javascript Блокировать / отключить последнюю кнопку приложений Пользовательские шрифты в android Android: диалоговое окно без тени вокруг него ANTI_ALIAS_FLAG для ImageView в макете? Должен ли onHandleIntent вызываться, когда IntentService запущен с bindService?

Сжатие видео на Android с использованием новой библиотеки MediaCodec

В моем приложении я пытаюсь загрузить несколько видео, которые пользователь выбрал из галереи. Проблема в том, что обычно файлы видеороликов для Android очень велики для загрузки, и поэтому мы хотим сжать их сначала за счет более низкого битрейта / разрешения.

Я только что слышал о новом MediaCodec api, который представлен с API 16 (я пробовал это делать с помощью ffmpeg).

Сейчас я делаю следующее: сначала декодируйте входное видео с помощью видеодекодера и настройте его в формате, который был прочитан из входного файла. Затем я создаю стандартный видеокодер с некоторыми предопределенными параметрами и использую его для кодирования выходного буфера декодера. Затем я сохраняю буфер вывода кодера в файл.

Все выглядит хорошо – одинаковое количество пакетов записывается и считывается с каждого входного и выходного буфера, но окончательный файл не похож на видеофайл и не может быть открыт каким-либо видеопроигрывателем.

Похоже, декодирование в порядке, потому что я проверяю его, отображая его на поверхности. Сначала я сконфигурировал декодер для работы с Surface, и когда мы вызываем releaseOutputBuffer, мы используем флаг render, и мы можем видеть видео на экране.

Вот код, который я использую:

//init decoder MediaCodec decoder = MediaCodec.createDecoderByType(mime); decoder.configure(format, null , null , 0); decoder.start(); ByteBuffer[] codecInputBuffers = decoder.getInputBuffers(); ByteBuffer[] codecOutputBuffers = decoder.getOutputBuffers(); //init encoder MediaCodec encoder = MediaCodec.createEncoderByType(mime); int width = format.getInteger(MediaFormat.KEY_WIDTH); int height = format.getInteger(MediaFormat.KEY_HEIGHT); MediaFormat mediaFormat = MediaFormat.createVideoFormat(mime, width, height); mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 400000); mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 25); mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar); mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5); encoder.configure(mediaFormat, null , null , MediaCodec.CONFIGURE_FLAG_ENCODE); encoder.start(); ByteBuffer[] encoderInputBuffers = encoder.getInputBuffers(); ByteBuffer[] encoderOutputBuffers = encoder.getOutputBuffers(); extractor.selectTrack(0); boolean sawInputEOS = false; boolean sawOutputEOS = false; boolean sawOutputEOS2 = false; MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); BufferInfo encoderInfo = new MediaCodec.BufferInfo(); while (!sawInputEOS || !sawOutputEOS || !sawOutputEOS2) { if (!sawInputEOS) { sawInputEOS = decodeInput(extractor, decoder, codecInputBuffers); } if (!sawOutputEOS) { int outputBufIndex = decoder.dequeueOutputBuffer(info, 0); if (outputBufIndex >= 0) { sawOutputEOS = decodeEncode(extractor, decoder, encoder, codecOutputBuffers, encoderInputBuffers, info, outputBufIndex); } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { Log.d(LOG_TAG, "decoding INFO_OUTPUT_BUFFERS_CHANGED"); codecOutputBuffers = decoder.getOutputBuffers(); } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { final MediaFormat oformat = decoder.getOutputFormat(); Log.d(LOG_TAG, "decoding Output format has changed to " + oformat); } else if (outputBufIndex == MediaCodec.INFO_TRY_AGAIN_LATER) { Log.d(LOG_TAG, "decoding dequeueOutputBuffer timed out!"); } } if (!sawOutputEOS2) { int encodingOutputBufferIndex = encoder.dequeueOutputBuffer(encoderInfo, 0); if (encodingOutputBufferIndex >= 0) { sawOutputEOS2 = encodeOuput(outputStream, encoder, encoderOutputBuffers, encoderInfo, encodingOutputBufferIndex); } else if (encodingOutputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { Log.d(LOG_TAG, "encoding INFO_OUTPUT_BUFFERS_CHANGED"); encoderOutputBuffers = encoder.getOutputBuffers(); } else if (encodingOutputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { final MediaFormat oformat = encoder.getOutputFormat(); Log.d(LOG_TAG, "encoding Output format has changed to " + oformat); } else if (encodingOutputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) { Log.d(LOG_TAG, "encoding dequeueOutputBuffer timed out!"); } } } //clear some stuff here... 

И это метод, который я использую для декодирования / кодирования:

  private boolean decodeInput(MediaExtractor extractor, MediaCodec decoder, ByteBuffer[] codecInputBuffers) { boolean sawInputEOS = false; int inputBufIndex = decoder.dequeueInputBuffer(0); if (inputBufIndex >= 0) { ByteBuffer dstBuf = codecInputBuffers[inputBufIndex]; input1count++; int sampleSize = extractor.readSampleData(dstBuf, 0); long presentationTimeUs = 0; if (sampleSize < 0) { sawInputEOS = true; sampleSize = 0; Log.d(LOG_TAG, "done decoding input: #" + input1count); } else { presentationTimeUs = extractor.getSampleTime(); } decoder.queueInputBuffer(inputBufIndex, 0, sampleSize, presentationTimeUs, sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); if (!sawInputEOS) { extractor.advance(); } } return sawInputEOS; } private boolean decodeOutputToFile(MediaExtractor extractor, MediaCodec decoder, ByteBuffer[] codecOutputBuffers, MediaCodec.BufferInfo info, int outputBufIndex, OutputStream output) throws IOException { boolean sawOutputEOS = false; ByteBuffer buf = codecOutputBuffers[outputBufIndex]; if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { sawOutputEOS = true; Log.d(LOG_TAG, "done decoding output: #" + output1count); } if (info.size > 0) { output1count++; byte[] outData = new byte[info.size]; buf.get(outData); output.write(outData, 0, outData.length); } else { Log.d(LOG_TAG, "no data available " + info.size); } buf.clear(); decoder.releaseOutputBuffer(outputBufIndex, false); return sawOutputEOS; } private boolean encodeInputFromFile(MediaCodec encoder, ByteBuffer[] encoderInputBuffers, MediaCodec.BufferInfo info, FileChannel channel) throws IOException { boolean sawInputEOS = false; int inputBufIndex = encoder.dequeueInputBuffer(0); if (inputBufIndex >= 0) { ByteBuffer dstBuf = encoderInputBuffers[inputBufIndex]; input1count++; int sampleSize = channel.read(dstBuf); if (sampleSize < 0) { sawInputEOS = true; sampleSize = 0; Log.d(LOG_TAG, "done encoding input: #" + input1count); } encoder.queueInputBuffer(inputBufIndex, 0, sampleSize, channel.position(), sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); } return sawInputEOS; } 

Любое предложение о том, что я делаю неправильно?

Я не нашел слишком много примеров для кодирования с MediaCodec всего несколько примеров кода для декодирования … Большое спасибо за помощь

Вывод MediaCodec является сырым элементарным потоком. Вам нужно упаковать его в формат видеофайла (возможно, перелистывать аудио), прежде чем многие игроки узнают его. FWIW, я обнаружил, что основанный на GStreamer проигрыватель Totem Movie для Linux будет воспроизводить «сырые» видео / avc-файлы.

Обновление: способ конвертировать H.264 в .mp4 на Android – это класс MediaMuxer , представленный в Android 4.3 (API 18). Есть несколько примеров (EncodeAndMuxTest, CameraToMpegTest), которые демонстрируют его использование.

Я просто наткнулся на эту библиотеку, которая может помочь вам создать надлежащий контейнер, чтобы видеоплееры узнали его: http://code.google.com/p/mp4parser/

Используйте библиотеку SiliCompressor для решения этой проблемы. Compresse Video и изображения при сохранении качества носителя.