下面列出了android.media.MediaCodec.BufferInfo#android.media.MediaCodec 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
@Test
public void addTrackWhenEncoderMediaFormatReceived() throws Exception {
audioTrackTranscoder.lastExtractFrameResult = TrackTranscoder.RESULT_EOS_REACHED;
audioTrackTranscoder.lastDecodeFrameResult = TrackTranscoder.RESULT_EOS_REACHED;
audioTrackTranscoder.lastEncodeFrameResult = TrackTranscoder.RESULT_FRAME_PROCESSED;
MediaFormat encoderMediaFormat = new MediaFormat();
doReturn(MediaCodec.INFO_OUTPUT_FORMAT_CHANGED).when(encoder).dequeueOutputFrame(anyLong());
doReturn(encoderMediaFormat).when(encoder).getOutputFormat();
doReturn(AUDIO_TRACK).when(mediaTarget).addTrack(any(MediaFormat.class), anyInt());
int result = audioTrackTranscoder.processNextFrame();
ArgumentCaptor<MediaFormat> mediaFormatArgumentCaptor = ArgumentCaptor.forClass(MediaFormat.class);
verify(mediaTarget).addTrack(mediaFormatArgumentCaptor.capture(), eq(AUDIO_TRACK));
assertThat(mediaFormatArgumentCaptor.getValue(), is(encoderMediaFormat));
assertThat(audioTrackTranscoder.targetTrack, is(AUDIO_TRACK));
assertThat(result, is(TrackTranscoder.RESULT_OUTPUT_MEDIA_FORMAT_CHANGED));
}
@Override
protected MediaCodec internal_configure(MediaCodec previous_codec,
final MediaFormat format) throws IOException {
if (DEBUG) Log.v(TAG, "internal_configure:");
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); // API >= 18
format.setInteger(MediaFormat.KEY_BIT_RATE, mBitRate > 0 ? mBitRate : calcBitRate());
format.setInteger(MediaFormat.KEY_FRAME_RATE, mFrameRate);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, mIFrameIntervals);
if (DEBUG) Log.i(TAG, "format: " + format);
if (previous_codec == null)
previous_codec = MediaCodec.createEncoderByType(MIME_TYPE);
previous_codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mSurface = previous_codec.createInputSurface(); // API >= 18
return previous_codec;
}
private void openVideoEncoder(){
AvLog.d(TAG,"openVideoEncoder startTime-->");
if(mVideoEncoder==null){
try {
MediaFormat format=convertVideoConfigToFormat(mConfig.mVideo);
mVideoEncoder= MediaCodec.createEncoderByType(mConfig.mVideo.mime);
mVideoEncoder.configure(format,null,null,MediaCodec.CONFIGURE_FLAG_ENCODE);
super.setSurface(mVideoEncoder.createInputSurface());
super.setOutputSize(mConfig.mVideo.width,mConfig.mVideo.height);
mVideoEncoder.start();
isEncodeStarted=true;
} catch (IOException e) {
e.printStackTrace();
}
}
AvLog.d(TAG,"openVideoEncoder endTime-->");
}
public MediaCodecDecoder(MediaExtractor extractor, boolean passive, int trackIndex,
OnDecoderEventListener listener)
throws IllegalStateException, IOException
{
// Apply the name of the concrete class that extends this base class to the logging tag
// THis is really not a nice solution but there's no better one: http://stackoverflow.com/a/936724
TAG = getClass().getSimpleName();
if(extractor == null || trackIndex == INDEX_NONE) {
throw new IllegalArgumentException("no track specified");
}
mExtractor = extractor;
mPassive = passive;
mTrackIndex = trackIndex;
mFormat = extractor.getTrackFormat(mTrackIndex);
mOnDecoderEventListener = listener;
mCodec = MediaCodec.createDecoderByType(mFormat.getString(MediaFormat.KEY_MIME));
mDecodingPTS = PTS_NONE;
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
protected void recorderLoop(SpeechRecord speechRecord) {
mNumBytesSubmitted = 0;
mNumBytesDequeued = 0;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
MediaFormat format = MediaFormatFactory.createMediaFormat(MediaFormatFactory.Type.FLAC, getSampleRate());
List<String> componentNames = AudioUtils.getEncoderNamesForType(format.getString(MediaFormat.KEY_MIME));
for (String componentName : componentNames) {
Log.i("component/format: " + componentName + "/" + format);
MediaCodec codec = AudioUtils.createCodec(componentName, format);
if (codec != null) {
recorderEncoderLoop(codec, speechRecord);
if (Log.DEBUG) {
AudioUtils.showMetrics(format, mNumBytesSubmitted, mNumBytesDequeued);
}
break; // TODO: we use the first one that is suitable
}
}
}
}
/**
* 硬解码获取实时帧数据并写入mp4文件
*
* @param index
*/
private void encodeToVideoTrack(int index) {
// 获取到的实时帧视频数据
ByteBuffer encodedData = mEncoder.getOutputBuffer(index);
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
// The codec config data was pulled out and fed to the muxer
// when we got
// the INFO_OUTPUT_FORMAT_CHANGED status.
// Ignore it.
Log.d(TAG, "ignoring BUFFER_FLAG_CODEC_CONFIG");
mBufferInfo.size = 0;
}
if (mBufferInfo.size == 0) {
Log.d(TAG, "info.size == 0, drop it.");
encodedData = null;
} else {
Log.d(TAG, "got buffer, info: size=" + mBufferInfo.size + ", presentationTimeUs="
+ mBufferInfo.presentationTimeUs + ", offset=" + mBufferInfo.offset);
}
if (encodedData != null) {
mMuxer.writeSampleData(mVideoTrackIndex, encodedData, mBufferInfo);
}
}
private void init() {
mBufferInfo = new MediaCodec.BufferInfo();
try {
mEncoder = MediaCodec.createEncoderByType(MIME_TYPE);
MediaFormat mediaFormat = MediaFormat.createAudioFormat(MIME_TYPE,
sampleRate, channelCount);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
mediaFormat.setInteger(MediaFormat.KEY_AAC_PROFILE,
KEY_AAC_PROFILE);
mEncoder.configure(mediaFormat, null, null,
MediaCodec.CONFIGURE_FLAG_ENCODE);
mEncoder.start();
} catch (IOException e) {
e.printStackTrace();
}
}
public void write() {
MediaCodecInputStream mediaCodecInputStream = aacEncoder.getMediaCodecInputStream();
MediaCodecInputStream.readAll(mediaCodecInputStream, writeBuffer, new MediaCodecInputStream.OnReadAllCallback() {
boolean shouldAddPacketHeader = true;
byte[] header = new byte[7];
@Override
public void onReadOnce(byte[] buffer, int readSize, MediaCodec.BufferInfo bufferInfo) {
if (readSize <= 0) {
return;
}
try {
Loggers.d("TestAudioEncoder", String.format("onReadOnce: readSize:%d, bufferInfo:%d", readSize, bufferInfo.size));
if (shouldAddPacketHeader) {
Loggers.d("TestAudioEncoder", String.format("onReadOnce: add packet header"));
AACEncoder.addADTStoPacket(header, 7 + bufferInfo.size);
os.write(header);
}
os.write(buffer, 0, readSize);
os.flush();
} catch (IOException e) {
e.printStackTrace();
}
shouldAddPacketHeader = readSize >= bufferInfo.size;
}
});
}
public UdpReceiverDecoderThread(Surface surface1, int port, Context context) {
surface=surface1;
mContext = context;
this.port = port;
nalu_data = new byte[NALU_MAXLEN];
nalu_data_position = 0;
settings= PreferenceManager.getDefaultSharedPreferences(mContext);
DecoderMultiThread=settings.getBoolean("decoderMultiThread", true);
userDebug=settings.getBoolean("userDebug", false);
groundRecord=settings.getBoolean("groundRecording", false);
if(userDebug){
SharedPreferences.Editor editor=settings.edit();
if(settings.getString("debugFile","").length()>=5000){
editor.putString("debugFile","new Session !\n");
}else{
editor.putString("debugFile",settings.getString("debugFile","")+"\n\nnew Session !\n");
}
editor.commit();
}
info = new MediaCodec.BufferInfo();
}
@Override
public void init(int sampleRateHz, CodecAndBitrate codecAndBitrate, boolean allowVbr)
throws EncoderException, IOException {
codecType = lookupCodecType(codecAndBitrate);
if (codecType == CodecType.UNSPECIFIED || codecType == CodecType.OGG_OPUS) {
throw new EncoderException("Codec not set properly.");
}
if (codecType == CodecType.AMRWB && sampleRateHz != 16000) {
throw new EncoderException("AMR-WB encoder requires a sample rate of 16kHz.");
}
MediaCodecInfo codecInfo = searchAmongAndroidSupportedCodecs(getMime(codecType));
if (codecInfo == null) {
throw new EncoderException("Encoder not found.");
}
this.codec = MediaCodec.createByCodecName(codecInfo.getName());
MediaFormat format = getMediaFormat(codecAndBitrate, sampleRateHz);
codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
codec.start();
initBuffers();
addedHeader = false;
successfullyFlushed = false;
formatChangeReportedOnce = false;
}
private void recordVirtualDisplay() {
while (!mQuit.get()) {
int index = mEncoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_US);
Log.i(TAG, "dequeue output buffer index=" + index);
if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// 后续输出格式变化
resetOutputFormat();
} else if (index == MediaCodec.INFO_TRY_AGAIN_LATER) {
// 请求超时
Log.d(TAG, "retrieving buffers time out!");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
} else if (index >= 0) {
// 有效输出
if (!mMuxerStarted) {
Log.d(TAG, "mMuxerStarted not started");
throw new IllegalStateException("MediaMuxer dose not call addTrack(format) ");
}
encodeToVideoTrack(index);
mEncoder.releaseOutputBuffer(index, false);
}
}
}
public MediaEncoder(final MediaMuxerWrapper muxer, final MediaEncoderListener listener) {
if (listener == null) throw new NullPointerException("MediaDecoderListener is null");
if (muxer == null) throw new NullPointerException("MediaExtractorWrapper is null");
mWeakMuxer = new WeakReference<MediaMuxerWrapper>(muxer);
muxer.addEncoder(this);
mListener = listener;
synchronized (mLock) {
// create BufferInfo here for effectiveness(to reduce GC)
mBufferInfo = new MediaCodec.BufferInfo();
// wait for starting thread
new Thread(this, getClass().getSimpleName()).start();
try {
mLock.wait();
} catch (final InterruptedException e) {
}
}
}
@Override
protected void configureCodec(
MediaCodecInfo codecInfo,
MediaCodec codec,
Format format,
MediaCrypto crypto,
float codecOperatingRate) {
String codecMimeType = codecInfo.codecMimeType;
codecMaxValues = getCodecMaxValues(codecInfo, format, getStreamFormats());
MediaFormat mediaFormat =
getMediaFormat(
format,
codecMimeType,
codecMaxValues,
codecOperatingRate,
deviceNeedsNoPostProcessWorkaround,
tunnelingAudioSessionId);
if (surface == null) {
Assertions.checkState(shouldUseDummySurface(codecInfo));
if (dummySurface == null) {
dummySurface = DummySurface.newInstanceV17(context, codecInfo.secure);
}
surface = dummySurface;
}
codec.configure(mediaFormat, surface, crypto, 0);
if (Util.SDK_INT >= 23 && tunneling) {
tunnelingOnFrameRenderedListener = new OnFrameRenderedListenerV23(codec);
}
}
/**
* @param trackType The track type that the renderer handles. One of the {@code C.TRACK_TYPE_*}
* constants defined in {@link C}.
* @param mediaCodecSelector A decoder selector.
* @param drmSessionManager For use with encrypted media. May be null if support for encrypted
* media is not required.
* @param playClearSamplesWithoutKeys Encrypted media may contain clear (un-encrypted) regions.
* For example a media file may start with a short clear region so as to allow playback to
* begin in parallel with key acquisition. This parameter specifies whether the renderer is
* permitted to play clear regions of encrypted media files before {@code drmSessionManager}
* has obtained the keys necessary to decrypt encrypted regions of the media.
* @param assumedMinimumCodecOperatingRate A codec operating rate that all codecs instantiated by
* this renderer are assumed to meet implicitly (i.e. without the operating rate being set
* explicitly using {@link MediaFormat#KEY_OPERATING_RATE}).
*/
public MediaCodecRenderer(
int trackType,
MediaCodecSelector mediaCodecSelector,
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
boolean playClearSamplesWithoutKeys,
float assumedMinimumCodecOperatingRate) {
super(trackType);
Assertions.checkState(Util.SDK_INT >= 16);
this.mediaCodecSelector = Assertions.checkNotNull(mediaCodecSelector);
this.drmSessionManager = drmSessionManager;
this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys;
this.assumedMinimumCodecOperatingRate = assumedMinimumCodecOperatingRate;
buffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED);
flagsOnlyBuffer = DecoderInputBuffer.newFlagsOnlyInstance();
formatHolder = new FormatHolder();
decodeOnlyPresentationTimestamps = new ArrayList<>();
outputBufferInfo = new MediaCodec.BufferInfo();
codecReconfigurationState = RECONFIGURATION_STATE_NONE;
codecReinitializationState = REINITIALIZATION_STATE_NONE;
codecOperatingRate = CODEC_OPERATING_RATE_UNSET;
rendererOperatingRate = 1f;
}
/**
* Write a media sample to the decoder.
*
* A "sample" here refers to a single atomic access unit in the media stream. The definition
* of "access unit" is dependent on the type of encoding used, but it typically refers to
* a single frame of video or a few seconds of audio. {@link MediaExtractor}
* extracts data from a stream one sample at a time.
*
* @param extractor Instance of {@link MediaExtractor} wrapping the media.
*
* @param presentationTimeUs The time, relative to the beginning of the media stream,
* at which this buffer should be rendered.
*
* @param flags Flags to pass to the decoder. See {@link MediaCodec#queueInputBuffer(int,
* int, int, long, int)}
*
* @throws MediaCodec.CryptoException
*/
public boolean writeSample(final MediaExtractor extractor,
final boolean isSecure,
final long presentationTimeUs,
int flags) {
boolean result = false;
boolean isEos = false;
if (!mAvailableInputBuffers.isEmpty()) {
int index = mAvailableInputBuffers.remove();
ByteBuffer buffer = mInputBuffers[index];
// reads the sample from the file using extractor into the buffer
int size = extractor.readSampleData(buffer, 0);
if (size <= 0) {
flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
}
// Submit the buffer to the codec for decoding. The presentationTimeUs
// indicates the position (play time) for the current sample.
if (!isSecure) {
mDecoder.queueInputBuffer(index, 0, size, presentationTimeUs, flags);
} else {
extractor.getSampleCryptoInfo(cryptoInfo);
mDecoder.queueSecureInputBuffer(index, 0, cryptoInfo, presentationTimeUs, flags);
}
result = true;
}
return result;
}
@Override
public void setup() throws IOException {
mExtractor.selectTrack(mTrackIndex);
mEncoder = MediaCodec.createEncoderByType(mOutputFormat.getString(MediaFormat.KEY_MIME));
mEncoder.configure(mOutputFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mEncoder.start();
mEncoderStarted = true;
final MediaFormat inputFormat = mExtractor.getTrackFormat(mTrackIndex);
mDecoder = MediaCodec.createDecoderByType(inputFormat.getString(MediaFormat.KEY_MIME));
mDecoder.configure(inputFormat, null, null, 0);
mDecoder.start();
mDecoderStarted = true;
mAudioChannel = new AudioChannel(mDecoder, mEncoder, mOutputFormat);
}
@Override
protected @KeepCodecResult int canKeepCodec(
MediaCodec codec, MediaCodecInfo codecInfo, Format oldFormat, Format newFormat) {
if (areAdaptationCompatible(codecInfo.adaptive, oldFormat, newFormat)
&& newFormat.width <= codecMaxValues.width
&& newFormat.height <= codecMaxValues.height
&& getMaxInputSize(codecInfo, newFormat) <= codecMaxValues.inputSize) {
return oldFormat.initializationDataEquals(newFormat)
? KEEP_CODEC_RESULT_YES_WITHOUT_RECONFIGURATION
: KEEP_CODEC_RESULT_YES_WITH_RECONFIGURATION;
}
return KEEP_CODEC_RESULT_NO;
}
/**
* Method to set byte array to the MediaCodec encoder
* @param buffer
* @param length length of byte array, zero means EOS.
* @param presentationTimeUs
*/
protected void encode(final ByteBuffer buffer, final int length, final long presentationTimeUs) {
if (!mIsCapturing) return;
final ByteBuffer[] inputBuffers = mMediaCodec.getInputBuffers();
while (mIsCapturing) {
final int inputBufferIndex = mMediaCodec.dequeueInputBuffer(TIMEOUT_USEC);
if (inputBufferIndex >= 0) {
final ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
if (buffer != null) {
inputBuffer.put(buffer);
}
// if (DEBUG) Log.v(TAG, "encode:queueInputBuffer");
if (length <= 0) {
// send EOS
mIsEOS = true;
if (DEBUG) Log.i(TAG, "send BUFFER_FLAG_END_OF_STREAM");
mMediaCodec.queueInputBuffer(inputBufferIndex, 0, 0,
presentationTimeUs, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
break;
} else {
mMediaCodec.queueInputBuffer(inputBufferIndex, 0, length,
presentationTimeUs, 0);
}
break;
} else if (inputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
// wait for MediaCodec encoder is ready to encode
// nothing to do here because MediaCodec#dequeueInputBuffer(TIMEOUT_USEC)
// will wait for maximum TIMEOUT_USEC(10msec) on each call
}
}
}
@TargetApi(21)
private void renderOutputBufferTimedV21(MediaCodec codec, int bufferIndex, long releaseTimeNs) {
maybeNotifyVideoSizeChanged();
TraceUtil.beginSection("releaseOutputBufferTimed");
codec.releaseOutputBuffer(bufferIndex,false);// releaseTimeNs);
TraceUtil.endSection();
codecCounters.renderedOutputBufferCount++;
maybeNotifyDrawnToSurface();
}
@Override
protected @KeepCodecResult int canKeepCodec(
MediaCodec codec, MediaCodecInfo codecInfo, Format oldFormat, Format newFormat) {
if (codecInfo.isSeamlessAdaptationSupported(
oldFormat, newFormat, /* isNewFormatComplete= */ true)
&& newFormat.width <= codecMaxValues.width
&& newFormat.height <= codecMaxValues.height
&& getMaxInputSize(codecInfo, newFormat) <= codecMaxValues.inputSize) {
return oldFormat.initializationDataEquals(newFormat)
? KEEP_CODEC_RESULT_YES_WITHOUT_RECONFIGURATION
: KEEP_CODEC_RESULT_YES_WITH_RECONFIGURATION;
}
return KEEP_CODEC_RESULT_NO;
}
@Override
public void buildRenderers(DemoPlayer player) {
Allocator allocator = new DefaultAllocator(BUFFER_SEGMENT_SIZE);
Handler mainHandler = player.getMainHandler();
// Build the video and audio renderers.
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(mainHandler, null);
DataSource dataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);
ExtractorSampleSource sampleSource = new ExtractorSampleSource(uri, dataSource, allocator,
BUFFER_SEGMENT_COUNT * BUFFER_SEGMENT_SIZE, mainHandler, player, 0);
MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context,
sampleSource, MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000,
mainHandler, player, 50);
MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource,
MediaCodecSelector.DEFAULT, null, true, mainHandler, player,
AudioCapabilities.getCapabilities(context), AudioManager.STREAM_MUSIC);
TrackRenderer textRenderer = new TextTrackRenderer(sampleSource, player,
mainHandler.getLooper());
// Invoke the callback.
TrackRenderer[] renderers = new TrackRenderer[DemoPlayer.RENDERER_COUNT];
renderers[DemoPlayer.TYPE_VIDEO] = videoRenderer;
renderers[DemoPlayer.TYPE_AUDIO] = audioRenderer;
renderers[DemoPlayer.TYPE_TEXT] = textRenderer;
player.onRenderers(renderers, bandwidthMeter);
}
private void writePacketToFile(ByteBuffer buffer, MediaCodec.BufferInfo bufferInfo) {
byte[] samples = new byte[bufferInfo.size];
buffer.get(samples, bufferInfo.offset, bufferInfo.size);
buffer.position(bufferInfo.offset);
try {
Files.write(samples, new File("/sdcard/Kickflip/packet_" + mPacketCount));
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void writeSampleData(MediaCodec encoder, int trackIndex, int bufferIndex, ByteBuffer encodedData, MediaCodec.BufferInfo bufferInfo) {
synchronized (mReadyFence) {
if (mReady) {
ByteBuffer muxerInput;
if (formatRequiresBuffering()) {
// Copy encodedData into another ByteBuffer, recycling if possible
synchronized (mMuxerInputQueue) {
muxerInput = mMuxerInputQueue.get(trackIndex).isEmpty() ?
ByteBuffer.allocateDirect(encodedData.capacity()) : mMuxerInputQueue.get(trackIndex).remove();
}
muxerInput.put(encodedData);
muxerInput.position(0);
encoder.releaseOutputBuffer(bufferIndex, false);
mHandler.sendMessage(mHandler.obtainMessage(MSG_WRITE_FRAME,
new WritePacketData(encoder, trackIndex, bufferIndex, muxerInput, bufferInfo)));
} else {
handleWriteSampleData(encoder, trackIndex, bufferIndex, encodedData, bufferInfo);
}
} else {
Log.w(TAG, "Dropping frame because Muxer not ready!");
releaseOutputBufer(encoder, encodedData, bufferIndex, trackIndex);
if (formatRequiresBuffering())
encoder.releaseOutputBuffer(bufferIndex, false);
}
}
}
private void clearRenderedFirstFrame() {
renderedFirstFrame = false;
// The first frame notification is triggered by renderOutputBuffer or renderOutputBufferV21 for
// non-tunneled playback, onQueueInputBuffer for tunneled playback prior to API level 23, and
// OnFrameRenderedListenerV23.onFrameRenderedListener for tunneled playback on API level 23 and
// above.
if (Util.SDK_INT >= 23 && tunneling) {
MediaCodec codec = getCodec();
// If codec is null then the listener will be instantiated in configureCodec.
if (codec != null) {
tunnelingOnFrameRenderedListener = new OnFrameRenderedListenerV23(codec);
}
}
}
/**
* Handle providing raw audio to MediaCodec InputBuffer
* @param codec
* @param inputBufferId
* @return number bytes provided
* @throws IOException
*/
private int queueCodecInputBuffer(MediaCodec codec, int inputBufferId) throws IOException {
ByteBuffer inputBuffer = codec.getInputBuffer(inputBufferId);
inputBuffer.clear();
int bytesAvailable = inputAudioStream.available();
int bytesToWrite = bytesAvailable < inputBuffer.limit() ? bytesAvailable : inputBuffer.limit();
inputBuffer.put(IOUtils.toByteArray(inputAudioStream, bytesToWrite));
codec.queueInputBuffer(inputBufferId, 0, bytesToWrite, 0, 0);
return bytesToWrite;
}
private void createMediaCodec() throws IOException {
if (mMediaCodec != null) {
if (DEBUG) {
Log.w(TAG, "MediaCodec is already running.");
}
return;
}
if (mWorkThread != null) {
if (DEBUG) {
Log.w(TAG, "WorkThread is already running.");
}
return;
}
MediaFormat format = MediaFormat.createAudioFormat("audio/mp4a-latm",
getSamplingRate(), getChannelCount());
format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
mMediaCodec = MediaCodec.createDecoderByType("audio/mp4a-latm");
mMediaCodec.configure(format, null, null, 0);
mMediaCodec.start();
mWorkThread = new WorkThread();
mWorkThread.setName("AACLATM-DECODE");
mWorkThread.start();
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public void forceSyncFrame() {
if (isRunning()) {
Bundle bundle = new Bundle();
bundle.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
try {
codec.setParameters(bundle);
} catch (IllegalStateException e) {
Log.e(TAG, "encoder need be running", e);
}
}
}
/**
* Renders the output buffer with the specified index. This method is only called if the platform
* API version of the device is less than 21.
*
* @param codec The codec that owns the output buffer.
* @param index The index of the output buffer to drop.
* @param presentationTimeUs The presentation time of the output buffer, in microseconds.
*/
protected void renderOutputBuffer(MediaCodec codec, int index, long presentationTimeUs) {
maybeNotifyVideoSizeChanged();
TraceUtil.beginSection("releaseOutputBuffer");
codec.releaseOutputBuffer(index, true);
TraceUtil.endSection();
lastRenderTimeUs = SystemClock.elapsedRealtime() * 1000;
decoderCounters.renderedOutputBufferCount++;
consecutiveDroppedFrameCount = 0;
maybeNotifyRenderedFirstFrame();
}
/**
* This encoder was attached to the engine. Keep the controller
* and run the internal thread.
*/
final void prepare(@NonNull final MediaEncoderEngine.Controller controller, final long maxLengthMillis) {
mController = controller;
mBufferInfo = new MediaCodec.BufferInfo();
mMaxLengthMillis = maxLengthMillis;
mWorker = WorkerHandler.get(getName());
LOG.i(getName(), "Prepare was called. Posting.");
mWorker.post(new Runnable() {
@Override
public void run() {
LOG.i(getName(), "Prepare was called. Executing.");
onPrepare(controller, maxLengthMillis);
}
});
}
@Override
public final void setUp(@NonNull MediaFormat desiredOutputFormat) {
try {
mEncoder = MediaCodec.createEncoderByType(desiredOutputFormat.getString(MediaFormat.KEY_MIME));
} catch (IOException e) {
throw new IllegalStateException(e);
}
onConfigureEncoder(desiredOutputFormat, mEncoder);
onStartEncoder(desiredOutputFormat, mEncoder);
onStarted(mDataSource.getTrackFormat(), desiredOutputFormat, mEncoder);
}