2

Requirement : I want to reverse a video file and save it as a new video file in android. ie. the final output file should play the video in reverse.

What I tried : I've used the below code (which I got from AOSP https://android.googlesource.com/platform/cts/+/kitkat-release/tests/tests/media/src/android/media/cts/MediaMuxerTest.java) with a little modification.

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

The main change I did is in this line

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

This was done in expectation that the video frames will be written to the output file in reverse order. But the result was same as the original video (not reversed).

I feel what I tried here is not making any sense. Basically I don't have much understanding of video formats, codecs, byte buffers etc.

I've also tried using JavaCV which is a good java wrapper over opencv, ffmpeg etc. and I got it working with that library. But the encoding process takes long time and also the apk size became large due to the library.

With android's built in MediaCodec APIs I expect things to be faster and lightweight. But I can accept other solutions also if they offer the same.

It's greatly appreciated if someone can offer any help on how this can be done in android. Also if you have great articles which can help me to learn the specifics/basics about video, codecs, video processing etc. that will also help.

Sreekanth
  • 2,887
  • 2
  • 16
  • 30
  • It's not generally simple to reverse a video, as the codecs are based on diffs against historical frames. – chrylis -cautiouslyoptimistic- Aug 18 '16 at 09:14
  • With javacv I was grabbing the frames in reverse order and writing to new file. So I guess there I'm getting the frame data with all the diffs calculated/applied. Is there any such way to get the final frame after all diffs applied using MediaExtracter? And preferably by specifying the frame number of interest. – Sreekanth Aug 18 '16 at 09:27
  • Theoretically, it's possible to achieved "the final frame after all the diffs", at least that is what I've been doing in my app. The operation is to seek to the previous keyframe and keep decoding forward until you reach the frame right before the current frame (output buffer in term of `MediaCodec`). – vxh.viet Oct 05 '16 at 06:48
  • The tricky part is you have to feed this output buffer as input buffer into another `MediaCodec` acting as encoder so you could mux it into a MP4 file. You can take a look at [BigFlake](http://bigflake.com/mediacodec/) for some sample but I haven't aware of a way to feed input into `MediaCodec` from another `MediaCodec` instance. Another obstacle is some device doesn't play nicely with multiple instance of `MediaCodec`, especially for high quality video (see my [question](http://stackoverflow.com/questions/39523215/mediacodec-configure-fails-with-illegalstateexception-for-1080p-videos) here). – vxh.viet Oct 05 '16 at 06:57
  • Check out my [answer](https://stackoverflow.com/questions/33230307/reverse-video-in-android/44605003#44605003) for reversing video using ffmpeg – Android Developer Jun 19 '17 at 08:38
  • @vxh.viet I came to the solution as yours, but the video between two keyframes don't play in the reverse order, even though I have reversed the presentation time. Have you met this problem? – Jian Guo Oct 10 '17 at 08:56
  • Yes, when play backward, the video will be choppy because you will need to drop all the B frames between 2 key frames. It has been quite a while since I do this so I may not recall correctly, but I think all the B frames contain the difference from the *previous* key frame. In other words, modern video is designed to play *forward* not *backward*. – vxh.viet Oct 11 '17 at 00:19
  • @vxh.viet Can you please elaborate on what you said above `you will need to drop all the B frames between 2 key frames`? How is it possible to drop the B frames? Currently, I'm seeking back to the previous key frame and I keep feeding the decoder until I get the frame I'm looking for. This works fine, but like you mentioned, it's choppy. – KRK Aug 20 '19 at 16:26
  • @KRK, it's been a long time since I work with MediaCodec, but you can check my other questions and answers in this topic for detail. For the choppy play in reverse, it is what it is here, no solution for it since most of the modern codec is design for forward playback. Unless you choose to re-encode with every frame as a key frame (which is terrible for file size), you can't achieve a smooth reverse play. There might be some special codec design specially for that purpose but that is another topic. – vxh.viet Aug 21 '19 at 02:36

0 Answers0