// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.media;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.media.MediaCodec;
import android.media.MediaCrypto;
import android.media.MediaFormat;
import android.view.Surface;
import android.util.Log;
import java.nio.ByteBuffer;
import org.chromium.base.CalledByNative;
import org.chromium.base.JNINamespace;
/**
* A wrapper of the MediaCodec class to facilitate exception capturing and
* audio rendering.
*/
@JNINamespace("media")
class MediaCodecBridge {
private static final String TAG = "MediaCodecBridge";
private ByteBuffer[] mInputBuffers;
private ByteBuffer[] mOutputBuffers;
private MediaCodec mMediaCodec;
private AudioTrack mAudioTrack;
private static class DequeueOutputResult {
private final int mIndex;
private final int mFlags;
private final int mOffset;
private final long mPresentationTimeMicroseconds;
private final int mNumBytes;
private DequeueOutputResult(
int index, int flags, int offset, long presentationTimeMicroseconds, int numBytes) {
mIndex = index;
mFlags = flags;
mOffset = offset;
mPresentationTimeMicroseconds = presentationTimeMicroseconds;
mNumBytes = numBytes;
}
@CalledByNative("DequeueOutputResult")
private int index() { return mIndex; }
@CalledByNative("DequeueOutputResult")
private int flags() { return mFlags; }
@CalledByNative("DequeueOutputResult")
private int offset() { return mOffset; }
@CalledByNative("DequeueOutputResult")
private long presentationTimeMicroseconds() { return mPresentationTimeMicroseconds; }
@CalledByNative("DequeueOutputResult")
private int numBytes() { return mNumBytes; }
}
private MediaCodecBridge(String mime) {
mMediaCodec = MediaCodec.createDecoderByType(mime);
}
@CalledByNative
private static MediaCodecBridge create(String mime) {
return new MediaCodecBridge(mime);
}
@CalledByNative
private void release() {
mMediaCodec.release();
if (mAudioTrack != null) {
mAudioTrack.release();
}
}
@CalledByNative
private void start() {
mMediaCodec.start();
mInputBuffers = mMediaCodec.getInputBuffers();
}
@CalledByNative
private int dequeueInputBuffer(long timeoutUs) {
return mMediaCodec.dequeueInputBuffer(timeoutUs);
}
@CalledByNative
private void flush() {
mMediaCodec.flush();
if (mAudioTrack != null) {
mAudioTrack.flush();
}
}
@CalledByNative
private void stop() {
mMediaCodec.stop();
if (mAudioTrack != null) {
mAudioTrack.pause();
}
}
@CalledByNative
private MediaFormat getOutputFormat() {
return mMediaCodec.getOutputFormat();
}
@CalledByNative
private ByteBuffer getInputBuffer(int index) {
return mInputBuffers[index];
}
@CalledByNative
private ByteBuffer getOutputBuffer(int index) {
return mOutputBuffers[index];
}
@CalledByNative
private void queueInputBuffer(
int index, int offset, int size, long presentationTimeUs, int flags) {
mMediaCodec.queueInputBuffer(index, offset, size, presentationTimeUs, flags);
}
@CalledByNative
private void releaseOutputBuffer(int index, boolean render) {
mMediaCodec.releaseOutputBuffer(index, render);
}
@CalledByNative
private void getOutputBuffers() {
mOutputBuffers = mMediaCodec.getOutputBuffers();
}
@CalledByNative
private DequeueOutputResult dequeueOutputBuffer(long timeoutUs) {
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
int index = mMediaCodec.dequeueOutputBuffer(info, timeoutUs);
return new DequeueOutputResult(
index, info.flags, info.offset, info.presentationTimeUs, info.size);
}
@CalledByNative
private void configureVideo(MediaFormat format, Surface surface, MediaCrypto crypto,
int flags) {
mMediaCodec.configure(format, surface, crypto, flags);
}
@CalledByNative
private void configureAudio(MediaFormat format, MediaCrypto crypto, int flags,
boolean playAudio) {
mMediaCodec.configure(format, null, crypto, flags);
if (playAudio) {
int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
int channelConfig = (channelCount == 1) ? AudioFormat.CHANNEL_OUT_MONO :
AudioFormat.CHANNEL_OUT_STEREO;
int minBufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig,
AudioFormat.ENCODING_PCM_16BIT);
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, channelConfig,
AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrack.MODE_STREAM);
}
}
@CalledByNative
private void playOutputBuffer(byte[] buf) {
if (mAudioTrack != null) {
if (AudioTrack.PLAYSTATE_PLAYING != mAudioTrack.getPlayState()) {
mAudioTrack.play();
}
int size = mAudioTrack.write(buf, 0, buf.length);
if (buf.length != size) {
Log.i(TAG, "Failed to send all data to audio output, expected size: " +
buf.length + ", actual size: " + size);
}
}
}
}