package com.serenegiant.media; /* * ScreenRecordingSample * Sample project to cature and save audio from internal and video from screen as MPEG4 file. * * Copyright (c) 2014-2016 saki t_saki@serenegiant.com * * File name: MediaMuxerWrapper.java * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * All files in the folder are under this Apache License, Version 2.0. */ import android.content.Context; import android.media.MediaCodec; import android.media.MediaFormat; import android.media.MediaMuxer; import android.os.Environment; import android.text.TextUtils; import android.util.Log; import com.serenegiant.utils.FileUtils; import java.io.IOException; import java.nio.ByteBuffer; public class MediaMuxerWrapper { private static final boolean DEBUG = false; // TODO set false on release private static final String TAG = MediaMuxerWrapper.class.getSimpleName(); private String mOutputPath; private final MediaMuxer mMediaMuxer; // API >= 18 private int mEncoderCount, mStatredCount; private boolean mIsStarted; private volatile boolean mIsPaused; private MediaEncoder mVideoEncoder, mAudioEncoder; /** * Constructor * @param _ext extension of output file * @throws IOException */ public MediaMuxerWrapper(final Context context, final String _ext) throws IOException { String ext = _ext; if (TextUtils.isEmpty(ext)) ext = ".mp4"; try { mOutputPath = FileUtils.getCaptureFile(context, Environment.DIRECTORY_MOVIES, ext, 0).toString(); } catch (final NullPointerException e) { throw new RuntimeException("This app has no permission of writing external storage"); } mMediaMuxer = new MediaMuxer(mOutputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); mEncoderCount = mStatredCount = 0; mIsStarted = false; } public String getOutputPath() { return mOutputPath; } public synchronized void prepare() throws IOException { if (mVideoEncoder != null) mVideoEncoder.prepare(); if (mAudioEncoder != null) mAudioEncoder.prepare(); } public synchronized void startRecording() { if (mVideoEncoder != null) mVideoEncoder.startRecording(); if (mAudioEncoder != null) mAudioEncoder.startRecording(); } public synchronized void stopRecording() { if (mVideoEncoder != null) mVideoEncoder.stopRecording(); mVideoEncoder = null; if (mAudioEncoder != null) mAudioEncoder.stopRecording(); mAudioEncoder = null; } public synchronized boolean isStarted() { return mIsStarted; } public synchronized void pauseRecording() { mIsPaused = true; if (mVideoEncoder != null) mVideoEncoder.pauseRecording(); if (mAudioEncoder != null) mAudioEncoder.pauseRecording(); } public synchronized void resumeRecording() { if (mVideoEncoder != null) mVideoEncoder.resumeRecording(); if (mAudioEncoder != null) mAudioEncoder.resumeRecording(); mIsPaused = false; } public synchronized boolean isPaused() { return mIsPaused; } //********************************************************************** //********************************************************************** /** * assign encoder to this calss. this is called from encoder. * @param encoder instance of MediaVideoEncoderBase */ /*package*/ void addEncoder(final MediaEncoder encoder) { if (encoder instanceof MediaVideoEncoderBase) { if (mVideoEncoder != null) throw new IllegalArgumentException("Video encoder already added."); mVideoEncoder = encoder; } else if (encoder instanceof MediaAudioEncoder) { if (mAudioEncoder != null) throw new IllegalArgumentException("Video encoder already added."); mAudioEncoder = encoder; } else throw new IllegalArgumentException("unsupported encoder"); mEncoderCount = (mVideoEncoder != null ? 1 : 0) + (mAudioEncoder != null ? 1 : 0); } /** * request start recording from encoder * @return true when muxer is ready to write */ /*package*/ synchronized boolean start() { if (DEBUG) Log.v(TAG, "start:"); mStatredCount++; if ((mEncoderCount > 0) && (mStatredCount == mEncoderCount)) { mMediaMuxer.start(); mIsStarted = true; notifyAll(); if (DEBUG) Log.v(TAG, "MediaMuxer started:"); } return mIsStarted; } /** * request stop recording from encoder when encoder received EOS */ /*package*/ synchronized void stop() { if (DEBUG) Log.v(TAG, "stop:mStatredCount=" + mStatredCount); mStatredCount--; if ((mEncoderCount > 0) && (mStatredCount <= 0)) { mMediaMuxer.stop(); mMediaMuxer.release(); mIsStarted = false; if (DEBUG) Log.v(TAG, "MediaMuxer stopped:"); } } /** * assign encoder to muxer * @param format * @return minus value indicate error */ /*package*/ synchronized int addTrack(final MediaFormat format) { if (mIsStarted) throw new IllegalStateException("muxer already started"); final int trackIx = mMediaMuxer.addTrack(format); if (DEBUG) Log.i(TAG, "addTrack:trackNum=" + mEncoderCount + ",trackIx=" + trackIx + ",format=" + format); return trackIx; } /** * write encoded data to muxer * @param trackIndex * @param byteBuf * @param bufferInfo */ /*package*/ synchronized void writeSampleData(final int trackIndex, final ByteBuffer byteBuf, final MediaCodec.BufferInfo bufferInfo) { if (mStatredCount > 0) mMediaMuxer.writeSampleData(trackIndex, byteBuf, bufferInfo); } //********************************************************************** //********************************************************************** }