// inspired by: https://github.com/Kickflip/kickflip-android-sdk/blob/e35e0a5bb7161ccffebd564ec1a76a0e2c053fc8/sdk/src/main/java/io/kickflip/sdk/av/Muxer.java package io.cine.android.streaming; import android.media.MediaCodec; import android.media.MediaFormat; import android.util.Log; import com.google.common.eventbus.EventBus; import java.nio.ByteBuffer; /** * Base Muxer class for interaction with MediaCodec based * encoders * * @hide */ public abstract class Muxer { private static final String TAG = "Muxer"; private final int mExpectedNumTracks = 2; // TODO: Make this configurable? protected int mNumTracks; protected int mNumTracksFinished; protected long mFirstPts; protected long mLastPts[]; private EncodingConfig mConfig; private EventBus mEventBus; protected Muxer() { } public EncodingConfig getConfig() { return mConfig; } public void prepare(EncodingConfig config) { mConfig = config; mNumTracks = 0; mNumTracksFinished = 0; mFirstPts = 0; mLastPts = new long[mExpectedNumTracks]; for (int i = 0; i < mLastPts.length; i++) { mLastPts[i] = 0; } Log.i(TAG, "Created muxer for output: " + mConfig.getOutputPath()); } public void setEventBus(EventBus eventBus) { mEventBus = eventBus; } /** * Returns the absolute output path. * <p/> * e.g /sdcard/app/uuid/index.m3u8 * * @return */ public String getOutputPath() { return mConfig.getOutputPath(); } /** * Adds the specified track and returns the track index * * @param trackFormat MediaFormat of the track to add. Gotten from MediaCodec#dequeueOutputBuffer * when returned status is INFO_OUTPUT_FORMAT_CHANGED * @return index of track in output file */ public int addTrack(MediaFormat trackFormat) { mNumTracks++; return mNumTracks - 1; } /** * Called by the hosting Encoder * to notify the Muxer that it should no * longer assume the Encoder resources are available. */ public void onEncoderReleased(int trackIndex) { } public void release() { } /** * Write the MediaCodec output buffer. This method <b>must</b> * be overridden by subclasses to release encodedData, transferring * ownership back to encoder, by calling encoder.releaseOutputBuffer(bufferIndex, false); * * @param trackIndex * @param encodedData * @param bufferInfo */ public void writeSampleData(MediaCodec encoder, int trackIndex, int bufferIndex, ByteBuffer encodedData, MediaCodec.BufferInfo bufferInfo) { if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { Log.d(TAG, "SIGNAL END OF TRACK"); signalEndOfTrack(); } } protected boolean allTracksFinished() { return (mNumTracks == mNumTracksFinished); } protected boolean allTracksAdded() { return (mNumTracks == mExpectedNumTracks); } /** * Muxer will call this itself if it detects BUFFER_FLAG_END_OF_STREAM * in writeSampleData. */ public void signalEndOfTrack() { mNumTracksFinished++; } /** * Does this Muxer's format require AAC ADTS headers? * see http://wiki.multimedia.cx/index.php?title=ADTS * * @return */ protected boolean formatRequiresADTS() { switch (mConfig.getFormat()) { case HLS: return true; case MPEG4: return true; case RTMP: return true; default: return false; } } /** * Does this Muxer's format require * copying and buffering encoder output buffers. * Generally speaking, is the output a Socket or File? * * @return */ protected boolean formatRequiresBuffering() { switch (mConfig.getFormat()) { case RTMP: return true; default: return false; } } /** * Return a relative pts given an absolute pts and trackIndex. * <p/> * This method advances the state of the Muxer, and must only * be called once per call to {@link #writeSampleData(android.media.MediaCodec, int, int, java.nio.ByteBuffer, android.media.MediaCodec.BufferInfo)}. */ protected long getNextRelativePts(long absPts, int trackIndex) { if (mFirstPts == 0) { mFirstPts = absPts; return 0; } return getSafePts(absPts - mFirstPts, trackIndex); } /** * Sometimes packets with non-increasing pts are dequeued from the MediaCodec output buffer. * This method ensures that a crash won't occur due to non monotonically increasing packet timestamp. */ private long getSafePts(long pts, int trackIndex) { if (mLastPts[trackIndex] >= pts) { // Enforce a non-zero minimum spacing // between pts mLastPts[trackIndex] += 9643; return mLastPts[trackIndex]; } mLastPts[trackIndex] = pts; return pts; } public static enum FORMAT {MPEG4, HLS, RTMP} }