2015-04-21 5 views
0

Моя цель - объединить видеофрагменты из нескольких видеофайлов. Фрагменты определяются произвольным временем начала и окончания. Сначала я хотел сделать это, используя библиотеку, например mp4parser, но она может обрезать потоки только на синхронизацию (IFRAME), в то время как мне нужна более высокая точность.Возможно ли/как правильно передать декодированные кадры MediaCodec в кодировщик MediaCodec?

Мой сценарий - Извлечение закодированного потока из файла -> Декодирование -> Кодирование -> Mux результат в файл mp4. Прямо сейчас, как правило, работает код, но видео получило белый шум. Протестировано на Nexus-S и Galaxy-S3. Мой код представляет собой комбинацию из нескольких примеров:

  • Читать ранее записанные файлы на основе MoviePlayer.java
  • Decode-Encode: DecodeEditEncodeTest.java
  • Mux видеопоток в mp4 - еще один пример, не имеет значения здесь

Я хочу упростить примеры, потому что мне не нужно обрабатывать кадры посередине. Я пытался подавать буферы с выхода декодера на вход энкодера без поверхности в середине. Общий процесс работал в том смысле, что код запускался до завершения и приводил к воспроизведению видеофайла. Однако содержимое файла имеет белый шум.

Это фрагмент кода, который передает кадры из декодера в кодировщик. Что не так и как заставить его работать?

... 
} else { // decoderStatus >= 0 
    if (VERBOSE) Log.d(TAG, "surface decoder given buffer " 
          + decoderStatus + " (size=" + info.size + ")"); 
    // The ByteBuffers are null references, but we still get a nonzero 
    // size for the decoded data. 
    boolean doRender = (info.size != 0); 
    // As soon as we call releaseOutputBuffer, the buffer will be forwarded 
    // to SurfaceTexture to convert to a texture. The API doesn't 
    // guarantee that the texture will be available before the call 
    // returns, so we need to wait for the onFrameAvailable callback to 
    // fire. If we don't wait, we risk rendering from the previous frame. 
    // decoder.releaseOutputBuffer(decoderStatus, doRender); 
    if (doRender) { 
    // This waits for the image and renders it after it arrives. 
//     if (VERBOSE) Log.d(TAG, "awaiting frame"); 
//       outputSurface.awaitNewImage(); 
//       outputSurface.drawImage(); 
//       // Send it to the encoder. 
//        inputSurface.setPresentationTime(info.presentationTimeUs * 1000); 
//       if (VERBOSE) Log.d(TAG, "swapBuffers"); 
//       inputSurface.swapBuffers(); 

      encoderStatus = encoder.dequeueInputBuffer(-1); 

      if (encoderStatus >= 0) { 
           encoderInputBuffers[encoderStatus].clear(); 

           decoderOutputBuffers[decoderStatus].position(info.offset); 
           decoderOutputBuffers[decoderStatus].limit(info.offset + info.size); 

           encoderInputBuffers[encoderStatus].put(decoderOutputBuffers[decoderStatus]); 
           encoder.queueInputBuffer(encoderStatus, 0, info.size, info.presentationTimeUs*1000, 0); 
       } 
      } 

         decoder.releaseOutputBuffer(decoderStatus, false); 
... 
+0

'grafika' недействительный тег (русизм от графика), он должен быть« графикой »вместо – Stan

+2

@Stan, grafika - правильный термин. Он не относится к польскому или русскому слову, а относится к этому: [https://github.com/google/grafika](https://github.com/google/grafika) –

+0

О, его lib. Спасибо, что указал мне. – Stan

ответ

4

Лучше использовать поверхность, чем ByteBuffer. Это быстрее, так же как и более портативный. Поверхности представляют собой очереди буферов, а не только фреймбуферы для пиксельных данных; декодированные видеокадры передаются с помощью дескриптора. Если вы используете ByteBuffers, видеоданные должны быть скопированы пару раз, что замедлит вас.

Создайте кодировщик MediaCodec, получите входную поверхность и передайте это декодеру в качестве выходной поверхности.

Если вам нужно работать с API 16/17, вы застряли с ByteBuffers. Если вы будете искать вокруг, вы можете найти конвертеры с обратным преобразованием для дурацких форматов qualcomm, но имейте в виду, что тестов CTS до API 18 не было, поэтому никаких гарантий нет.

+0

Спасибо! Я останусь с Поверхностями. Правильно ли я понял, что, говоря «Создайте кодировщик MediaCodec, получите входную поверхность и передайте это декодеру в качестве выходной поверхности». Вы имеете в виду использование классов-помощников «InputSurface и OutputSurface» из примеров? (Я пытался использовать «голый» 'Surface', и это не сработало). В противном случае, как установить 'presentationTimeUs' для следующего кадра кодировщика, дождаться завершения декодирования и т. Д.? Я прошу прощения, если мои вопросы слишком наивны: я пытаюсь реализовать явно прямые функции с меньшим количеством строк/более простым кодом. –

+1

Вам не нужны вспомогательные классы. Вызовите http://developer.android.com/reference/android/media/MediaCodec.html#createInputSurface() в кодере и передайте Поверхность, которая возвращается в декодер 'configure()'. Вы можете иметь только один декодер, подающий кодировщик за раз, но если вы 'stop()' и 'release()' декодер, он должен освободить Surface и позволить вам присоединить другой экземпляр декодера MediaCodec. – fadden

+0

Еще раз спасибо, поразительно, что мировой эксперт No1 по этому вопросу отвечает в реальном времени. –

 Смежные вопросы

  • Нет связанных вопросов^_^