while (true) {
int encoderStatus = mMediaCodec.dequeueOutputBuffer(mBufferInfo, OUTPUT_TIMEOUT_US);
if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
// no output available yet
if (!drainAll) break; // out of while
<code> } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
// not expected for an encoder
mBuffers.onOutputBuffersChanged();
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// should happen before receiving buffers, and should only happen once
if (mController.isStarted()) {
throw new RuntimeException("MediaFormat changed twice.");
}
MediaFormat newFormat = mMediaCodec.getOutputFormat();
mTrackIndex = mController.notifyStarted(newFormat);
setState(STATE_STARTED);
} else if (encoderStatus < 0) {
// let's ignore it
} else {
ByteBuffer encodedData = mBuffers.getOutputBuffer(encoderStatus);
// Codec config means that config data was pulled out and fed to the muxer
// when we got the INFO_OUTPUT_FORMAT_CHANGED status. Ignore it.
boolean isCodecConfig = (mBufferInfo.flags
& MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0;
if (!isCodecConfig && mController.isStarted() && mBufferInfo.size != 0) {
// adjust the ByteBuffer values to match BufferInfo (not needed?)
encodedData.position(mBufferInfo.offset);
encodedData.limit(mBufferInfo.offset + mBufferInfo.size);
// Store mStartTimeUs and mLastTimeUs, useful to detect the max length
// reached and stop recording when needed.
if (mFirstTimeUs == Long.MIN_VALUE) {
mFirstTimeUs = mBufferInfo.presentationTimeUs;
}
mLastTimeUs = mBufferInfo.presentationTimeUs;
// Adjust the presentation times. Subclasses can pass a presentation time in any
// reference system - possibly some that has no real meaning, and frequently,
// presentation times from different encoders have a different time-base.
// To address this, encoders are required to call notifyFirstFrameMillis
// so we can adjust here - moving to 1970 reference.
// Extra benefit: we never pass a pts equal to 0, which some encoders refuse.
mBufferInfo.presentationTimeUs = (mStartTimeMillis * 1000)
+ mLastTimeUs - mFirstTimeUs;
/*IMP*/
//Here, we need to save encoded data to a file and RTMP at the same time.
}
mMediaCodec.releaseOutputBuffer(encoderStatus, false);
// Check for the maxLength constraint (with appropriate conditions)
// Not needed if drainAll because we already were asked to stop
if (!drainAll
&& !mMaxLengthReached
&& mFirstTimeUs != Long.MIN_VALUE
&& mLastTimeUs - mFirstTimeUs > mMaxLengthUs) {
onMaxLengthReached();
break;
}
// Check for the EOS flag so we can call onStopped.
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
onStopped();
break;
}
}
}
</code>
<code> } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
// not expected for an encoder
mBuffers.onOutputBuffersChanged();
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// should happen before receiving buffers, and should only happen once
if (mController.isStarted()) {
throw new RuntimeException("MediaFormat changed twice.");
}
MediaFormat newFormat = mMediaCodec.getOutputFormat();
mTrackIndex = mController.notifyStarted(newFormat);
setState(STATE_STARTED);
} else if (encoderStatus < 0) {
// let's ignore it
} else {
ByteBuffer encodedData = mBuffers.getOutputBuffer(encoderStatus);
// Codec config means that config data was pulled out and fed to the muxer
// when we got the INFO_OUTPUT_FORMAT_CHANGED status. Ignore it.
boolean isCodecConfig = (mBufferInfo.flags
& MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0;
if (!isCodecConfig && mController.isStarted() && mBufferInfo.size != 0) {
// adjust the ByteBuffer values to match BufferInfo (not needed?)
encodedData.position(mBufferInfo.offset);
encodedData.limit(mBufferInfo.offset + mBufferInfo.size);
// Store mStartTimeUs and mLastTimeUs, useful to detect the max length
// reached and stop recording when needed.
if (mFirstTimeUs == Long.MIN_VALUE) {
mFirstTimeUs = mBufferInfo.presentationTimeUs;
}
mLastTimeUs = mBufferInfo.presentationTimeUs;
// Adjust the presentation times. Subclasses can pass a presentation time in any
// reference system - possibly some that has no real meaning, and frequently,
// presentation times from different encoders have a different time-base.
// To address this, encoders are required to call notifyFirstFrameMillis
// so we can adjust here - moving to 1970 reference.
// Extra benefit: we never pass a pts equal to 0, which some encoders refuse.
mBufferInfo.presentationTimeUs = (mStartTimeMillis * 1000)
+ mLastTimeUs - mFirstTimeUs;
/*IMP*/
//Here, we need to save encoded data to a file and RTMP at the same time.
}
mMediaCodec.releaseOutputBuffer(encoderStatus, false);
// Check for the maxLength constraint (with appropriate conditions)
// Not needed if drainAll because we already were asked to stop
if (!drainAll
&& !mMaxLengthReached
&& mFirstTimeUs != Long.MIN_VALUE
&& mLastTimeUs - mFirstTimeUs > mMaxLengthUs) {
onMaxLengthReached();
break;
}
// Check for the EOS flag so we can call onStopped.
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
onStopped();
break;
}
}
}
</code>
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
// not expected for an encoder
mBuffers.onOutputBuffersChanged();
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// should happen before receiving buffers, and should only happen once
if (mController.isStarted()) {
throw new RuntimeException("MediaFormat changed twice.");
}
MediaFormat newFormat = mMediaCodec.getOutputFormat();
mTrackIndex = mController.notifyStarted(newFormat);
setState(STATE_STARTED);
} else if (encoderStatus < 0) {
// let's ignore it
} else {
ByteBuffer encodedData = mBuffers.getOutputBuffer(encoderStatus);
// Codec config means that config data was pulled out and fed to the muxer
// when we got the INFO_OUTPUT_FORMAT_CHANGED status. Ignore it.
boolean isCodecConfig = (mBufferInfo.flags
& MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0;
if (!isCodecConfig && mController.isStarted() && mBufferInfo.size != 0) {
// adjust the ByteBuffer values to match BufferInfo (not needed?)
encodedData.position(mBufferInfo.offset);
encodedData.limit(mBufferInfo.offset + mBufferInfo.size);
// Store mStartTimeUs and mLastTimeUs, useful to detect the max length
// reached and stop recording when needed.
if (mFirstTimeUs == Long.MIN_VALUE) {
mFirstTimeUs = mBufferInfo.presentationTimeUs;
}
mLastTimeUs = mBufferInfo.presentationTimeUs;
// Adjust the presentation times. Subclasses can pass a presentation time in any
// reference system - possibly some that has no real meaning, and frequently,
// presentation times from different encoders have a different time-base.
// To address this, encoders are required to call notifyFirstFrameMillis
// so we can adjust here - moving to 1970 reference.
// Extra benefit: we never pass a pts equal to 0, which some encoders refuse.
mBufferInfo.presentationTimeUs = (mStartTimeMillis * 1000)
+ mLastTimeUs - mFirstTimeUs;
/*IMP*/
//Here, we need to save encoded data to a file and RTMP at the same time.
}
mMediaCodec.releaseOutputBuffer(encoderStatus, false);
// Check for the maxLength constraint (with appropriate conditions)
// Not needed if drainAll because we already were asked to stop
if (!drainAll
&& !mMaxLengthReached
&& mFirstTimeUs != Long.MIN_VALUE
&& mLastTimeUs - mFirstTimeUs > mMaxLengthUs) {
onMaxLengthReached();
break;
}
// Check for the EOS flag so we can call onStopped.
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
onStopped();
break;
}
}
}
please search for /IMP/ in above code.
/IMP/ where I need to save buffers for later use and before releaseOutputBuffer() is called. We need to copy the mediacodec output buffer to another output buffer for asynchronous saving to a file (.mp4) and RTMP streaming.