Обратное видео в Android с помощью MediaCodec, MediaExtractor, MediaMuxer и т. д.

Требование: я хочу перевернуть видеофайл и сохранить его как новый видеофайл в Android. т.е. окончательный выходной файл должен воспроизводить видео в обратном порядке.

Что я пробовал: я использовал приведенный ниже код (который я получил от AOSP https://android.googlesource.com/platform/cts/+/kitkat-release/tests)./tests/media/src/android/media/cts/MediaMuxerTest.java) с небольшой модификацией.

 File file = new File(srcMedia.getPath());
    MediaExtractor extractor = new MediaExtractor();

    extractor.setDataSource(file.getPath());
    int trackCount = extractor.getTrackCount();

    // Set up MediaMuxer for the destination.
    MediaMuxer muxer;
    muxer = new MediaMuxer(dstMediaPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
    // Set up the tracks.
    HashMap<Integer, Integer> indexMap = new HashMap<Integer, Integer>(trackCount);
    for (int i = 0; i < trackCount; i++) {
        extractor.selectTrack(i);
        MediaFormat format = extractor.getTrackFormat(i);
        int dstIndex = muxer.addTrack(format);
        indexMap.put(i, dstIndex);
    }
    // Copy the samples from MediaExtractor to MediaMuxer.
    boolean sawEOS = false;
    int bufferSize = MAX_SAMPLE_SIZE;
    int frameCount = 0;
    int offset = 100;
    long totalTime = mTotalVideoDurationInMicroSeconds;
    ByteBuffer dstBuf = ByteBuffer.allocate(bufferSize);
    MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
    if (degrees >= 0) {
        muxer.setOrientationHint(degrees);
    }
    muxer.start();
    while (!sawEOS) {
        bufferInfo.offset = offset;
        bufferInfo.size = extractor.readSampleData(dstBuf, offset);
        if (bufferInfo.size < 0) {
            if (VERBOSE) {
                Log.d(TAG, "saw input EOS.");
            }
            sawEOS = true;
            bufferInfo.size = 0;
        } else {
            bufferInfo.presentationTimeUs = totalTime - extractor.getSampleTime();
            //noinspection WrongConstant
            bufferInfo.flags = extractor.getSampleFlags();
            int trackIndex = extractor.getSampleTrackIndex();
            muxer.writeSampleData(indexMap.get(trackIndex), dstBuf,
                    bufferInfo);
            extractor.advance();
            frameCount++;
            if (VERBOSE) {
                Log.d(TAG, "Frame (" + frameCount + ") " +
                        "PresentationTimeUs:" + bufferInfo.presentationTimeUs +
                        " Flags:" + bufferInfo.flags +
                        " TrackIndex:" + trackIndex +
                        " Size(KB) " + bufferInfo.size / 1024);
            }
        }
    }
    muxer.stop();
    muxer.release();

Основное изменение, которое я сделал, находится в этой строке

bufferInfo.presentationTimeUs = totalTime - extractor.getSampleTime();

Это было сделано в расчете на то, что видеокадры будут записаны в выходной файл в обратном порядке. Но результат был таким же, как и исходное видео (не перевернутое).

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

Я также пробовал использовать JavaCV, который является хорошей оболочкой java поверх opencv, ffmpeg и т. д., и у меня получилось работать с этой библиотекой. Но процесс кодирования занимает много времени, а также размер apk стал большим из-за библиотеки.

Я ожидаю, что со встроенными в Android API MediaCodec все будет быстрее и легче. Но я могу принять и другие решения, если они предлагают то же самое.

Мы очень признательны, если кто-то может предложить любую помощь в том, как это можно сделать в Android. Кроме того, если у вас есть отличные статьи, которые могут помочь мне узнать специфику/основы о видео, кодеках, обработке видео и т. д., это тоже поможет.


person Sreekanth    schedule 18.08.2016    source источник
comment
Как правило, инвертировать видео непросто, поскольку кодеки основаны на различиях с историческими кадрами.   -  person chrylis -cautiouslyoptimistic-    schedule 18.08.2016
comment
С javacv я брал кадры в обратном порядке и писал в новый файл. Итак, я думаю, что я получаю данные кадра со всеми рассчитанными/примененными различиями. Есть ли такой способ получить окончательный кадр после применения всех различий с помощью MediaExtracter? И желательно, указав номер интересующего кадра.   -  person Sreekanth    schedule 18.08.2016
comment
Теоретически возможно получить окончательный кадр после всех различий, по крайней мере, это то, что я делал в своем приложении. Операция заключается в поиске предыдущего ключевого кадра и продолжении декодирования до тех пор, пока вы не достигнете кадра прямо перед текущим кадром (выходной буфер в терминах MediaCodec).   -  person vxh.viet    schedule 05.10.2016
comment
Сложность заключается в том, что вы должны передать этот выходной буфер в качестве входного буфера другому MediaCodec, действующему как кодировщик, чтобы вы могли мультиплексировать его в файл MP4. Вы можете взглянуть на BigFlake для примера, но я не знаю, как передать входные данные в MediaCodec из другого экземпляра MediaCodec. Еще одним препятствием является то, что некоторые устройства плохо работают с несколькими экземплярами MediaCodec, особенно для видео высокого качества (см. -for-1080p-videos">вопрос здесь).   -  person vxh.viet    schedule 05.10.2016
comment
Посмотрите мой ответ для реверсирования видео с помощью ffmpeg.   -  person Android Developer    schedule 19.06.2017
comment
@ vxh.viet Я пришел к решению как к вашему, но видео между двумя ключевыми кадрами не воспроизводится в обратном порядке, хотя я изменил время презентации на обратное. Вы сталкивались с этой проблемой?   -  person Jian Guo    schedule 10.10.2017
comment
Да, при воспроизведении в обратном направлении видео будет прерывистым, потому что вам нужно будет отбросить все кадры B между двумя ключевыми кадрами. Прошло довольно много времени с тех пор, как я это сделал, поэтому я могу неправильно вспомнить, но я думаю, что все кадры B содержат отличие от предыдущего ключевого кадра. Другими словами, современное видео предназначено для воспроизведения вперед, а не назад.   -  person vxh.viet    schedule 11.10.2017
comment
@vxh.viet Не могли бы вы уточнить, что вы сказали выше you will need to drop all the B frames between 2 key frames? Как можно отбросить B-кадры? В настоящее время я возвращаюсь к предыдущему ключевому кадру и продолжаю кормить декодер, пока не получу кадр, который ищу. Это отлично работает, но, как вы упомянули, это прерывисто.   -  person KRK    schedule 20.08.2019
comment
@KRK, я давно не работаю с MediaCodec, но вы можете подробно ознакомиться с другими моими вопросами и ответами в этой теме. Для прерывистого воспроизведения в обратном направлении это то, что здесь есть, для этого нет решения, поскольку большинство современных кодеков предназначены для воспроизведения в прямом направлении. Если вы не решите перекодировать каждый кадр в качестве ключевого кадра (что ужасно для размера файла), вы не сможете добиться плавного обратного воспроизведения. Может быть какой-то специальный дизайн кодека специально для этой цели, но это уже другая тема.   -  person vxh.viet    schedule 21.08.2019