Using MediaMuxer instead of FileOutputStream to save frames into mp4 file

1.7k Views Asked by At

I use next sample to record video from buffer (from onPreviewFrame(byte[] data,...). But it saves video using Output Stream. I would like to change to MediaMuxer. Also when using this sample the final video is being played with very high speed in video player. I'm just not sure what time to set for encoder.queueInputBuffer(inputBufIndex, 0, 0, ptsUsec, MediaCodec.BUFFER_FLAG_END_OF_STREAM); I use long ptsUsec = (System.currentTimeMillis() * 1000) / FRAME_RATE;

private void encodeVideoFrameFromBuffer(byte[] frameData) {
    if (encoder == null) return;
    final int TIMEOUT_USEC = 10000;
    ByteBuffer[] encoderInputBuffers = encoder.getInputBuffers();
    ByteBuffer[] encoderOutputBuffers = encoder.getOutputBuffers();
    MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();

    if (!outputDone && outputStream == null) {
        String fileName = Environment.getExternalStorageDirectory() + File.separator + "test" + 1280 + "x" + 720 + ".mp4";
        File file = new File(fileName);
        if (file.exists()) {
            file.delete();
        }
        try {
            outputStream = new FileOutputStream(fileName);
            Log.d(TAG, "encoded output will be saved as " + fileName);
        } catch (IOException ioe) {
            Log.w(TAG, "Unable to create debug output file " + fileName);
            throw new RuntimeException(ioe);

        }
    }
    if (outputStream != null) {
        int inputBufIndex = encoder.dequeueInputBuffer(TIMEOUT_USEC);

        if (inputBufIndex >= 0) {
            long ptsUsec = (System.currentTimeMillis() * 1000) / FRAME_RATE;
            if (outputDone) {
                encoder.queueInputBuffer(inputBufIndex, 0, 0, ptsUsec,
                        MediaCodec.BUFFER_FLAG_END_OF_STREAM);
            } else {
                ByteBuffer inputBuf = encoderInputBuffers[inputBufIndex];

                inputBuf.clear();
                inputBuf.put(frameData);
                encoder.queueInputBuffer(inputBufIndex, 0, frameData.length, ptsUsec, 0);

            }
            generateIndex++;
        }


        int encoderStatus = encoder.dequeueOutputBuffer(info, TIMEOUT_USEC);
        if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
            // no output available yet

        } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
            // not expected for an encoder
            encoderOutputBuffers = encoder.getOutputBuffers();
            Log.d(TAG, "encoder output buffers changed");
        } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
            MediaFormat newFormat = encoder.getOutputFormat();
            Log.d(TAG, "encoder output format changed: " + newFormat);
        } else if (encoderStatus < 0) {
            Log.e(TAG, "unexpected result from encoder.dequeueOutputBuffer: " + encoderStatus);
        } else { // encoderStatus >= 0
            ByteBuffer encodedData = encoderOutputBuffers[encoderStatus];
            encodedData.position(info.offset);
            encodedData.limit(info.offset + info.size);

            byte[] data = new byte[info.size];
            encodedData.get(data);
            encodedData.position(info.offset);
            try {
                outputStream.write(data);
            } catch (IOException ioe) {
                Log.w(TAG, "failed writing debug data to file");
                throw new RuntimeException(ioe);
            }

            encoder.releaseOutputBuffer(encoderStatus, false);
        }
    }

    if (outputDone) {
        if (outputStream != null) {


            try {
                outputStream.close();
            } catch (IOException ioe) {
                Log.w(TAG, "failed closing debug file");
                throw new RuntimeException(ioe);
            }
            outputStream = null;
            stopEncoder();
        }
    }
}

@Override
public void onPreviewFrame(byte[] data, Camera camera) {
    doEncodeDecodeVideoFromBuffer(data);

}
1

There are 1 best solutions below

0
On

About fast playing - try to use System.nanoTime() / 1000L instead of (System.currentTimeMillis() * 1000) / FRAME_RATE

to use muxer you have to initialize it outside of your encode/decode process and feed it with sample data in decode part. Change your

try {
    outputStream.write(data);
} catch (IOException ioe) {
    Log.w(TAG, "failed writing debug data to file");
    throw new RuntimeException(ioe);
}

to

//somewhere outside encode/decode part
MediaCodec.BufferInfo videoInfo = new MediaCodec.BufferInfo();
muxer = new MediaMuxer(/*your file*/,
                MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
int videoTrack = muxer.addTrack(encoder.getOutputFormat());
muxer.start();

//try-catch block replacement
muxer.writeSampleData(videoTrack, data, videoInfo);

don't forget to stop() and release() your muxer finally. That should work