diff options
| -rw-r--r-- | media/java/android/media/AmrInputStream.java | 173 | ||||
| -rw-r--r-- | media/jni/Android.mk | 6 | ||||
| -rw-r--r-- | media/jni/android_media_AmrInputStream.cpp | 137 | ||||
| -rw-r--r-- | media/jni/android_media_MediaPlayer.cpp | 6 |
4 files changed, 115 insertions, 207 deletions
diff --git a/media/java/android/media/AmrInputStream.java b/media/java/android/media/AmrInputStream.java index f90f1e24b1e8..fb91bbbbb321 100644 --- a/media/java/android/media/AmrInputStream.java +++ b/media/java/android/media/AmrInputStream.java @@ -18,45 +18,69 @@ package android.media; import java.io.InputStream; import java.io.IOException; +import java.nio.ByteBuffer; + +import android.media.MediaCodec.BufferInfo; +import android.util.Log; /** * AmrInputStream * @hide */ -public final class AmrInputStream extends InputStream -{ - static { - System.loadLibrary("media_jni"); - } - +public final class AmrInputStream extends InputStream { private final static String TAG = "AmrInputStream"; // frame is 20 msec at 8.000 khz private final static int SAMPLES_PER_FRAME = 8000 * 20 / 1000; - + + MediaCodec mCodec; + BufferInfo mInfo; + boolean mSawOutputEOS; + boolean mSawInputEOS; + // pcm input stream private InputStream mInputStream; - - // native handle - private long mGae; - + // result amr stream private final byte[] mBuf = new byte[SAMPLES_PER_FRAME * 2]; private int mBufIn = 0; private int mBufOut = 0; - + // helper for bytewise read() private byte[] mOneByte = new byte[1]; - + /** * Create a new AmrInputStream, which converts 16 bit PCM to AMR * @param inputStream InputStream containing 16 bit PCM. */ public AmrInputStream(InputStream inputStream) { mInputStream = inputStream; - mGae = GsmAmrEncoderNew(); - GsmAmrEncoderInitialize(mGae); + + MediaFormat format = new MediaFormat(); + format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_AUDIO_AMR_NB); + format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 8000); + format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1); + format.setInteger(MediaFormat.KEY_BIT_RATE, 12200); + + MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS); + String name = mcl.findEncoderForFormat(format); + if (name != null) { + try { + mCodec = MediaCodec.createByCodecName(name); + mCodec.configure(format, + null /* surface */, + null /* crypto */, + MediaCodec.CONFIGURE_FLAG_ENCODE); + mCodec.start(); + } catch (IOException e) { + if (mCodec != null) { + mCodec.release(); + } + mCodec = null; + } + } + mInfo = new BufferInfo(); } @Override @@ -64,7 +88,7 @@ public final class AmrInputStream extends InputStream int rtn = read(mOneByte, 0, 1); return rtn == 1 ? (0xff & mOneByte[0]) : -1; } - + @Override public int read(byte[] b) throws IOException { return read(b, 0, b.length); @@ -72,67 +96,100 @@ public final class AmrInputStream extends InputStream @Override public int read(byte[] b, int offset, int length) throws IOException { - if (mGae == 0) throw new IllegalStateException("not open"); - - // local buffer of amr encoded audio empty - if (mBufOut >= mBufIn) { - // reset the buffer + if (mCodec == null) { + throw new IllegalStateException("not open"); + } + + if (mBufOut >= mBufIn && !mSawOutputEOS) { + // no data left in buffer, refill it mBufOut = 0; mBufIn = 0; - - // fetch a 20 msec frame of pcm - for (int i = 0; i < SAMPLES_PER_FRAME * 2; ) { - int n = mInputStream.read(mBuf, i, SAMPLES_PER_FRAME * 2 - i); - if (n == -1) return -1; - i += n; + + // first push as much data into the encoder as possible + while (!mSawInputEOS) { + int index = mCodec.dequeueInputBuffer(0); + if (index < 0) { + // no input buffer currently available + break; + } else { + int numRead; + for (numRead = 0; numRead < SAMPLES_PER_FRAME * 2; ) { + int n = mInputStream.read(mBuf, numRead, SAMPLES_PER_FRAME * 2 - numRead); + if (n == -1) { + mSawInputEOS = true; + break; + } + numRead += n; + } + ByteBuffer buf = mCodec.getInputBuffer(index); + buf.put(mBuf, 0, numRead); + mCodec.queueInputBuffer(index, + 0 /* offset */, + numRead, + 0 /* presentationTimeUs */, + mSawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0 /* flags */); + } + } + + // now read encoded data from the encoder (blocking, since we just filled up the + // encoder's input with data it should be able to output at least one buffer) + while (true) { + int index = mCodec.dequeueOutputBuffer(mInfo, -1); + if (index >= 0) { + mBufIn = mInfo.size; + ByteBuffer out = mCodec.getOutputBuffer(index); + out.get(mBuf, 0 /* offset */, mBufIn /* length */); + mCodec.releaseOutputBuffer(index, false /* render */); + if ((mInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { + mSawOutputEOS = true; + } + break; + } } - - // encode it - mBufIn = GsmAmrEncoderEncode(mGae, mBuf, 0, mBuf, 0); } - - // return encoded audio to user - if (length > mBufIn - mBufOut) length = mBufIn - mBufOut; - System.arraycopy(mBuf, mBufOut, b, offset, length); - mBufOut += length; - - return length; + + if (mBufOut < mBufIn) { + // there is data in the buffer + if (length > mBufIn - mBufOut) { + length = mBufIn - mBufOut; + } + System.arraycopy(mBuf, mBufOut, b, offset, length); + mBufOut += length; + return length; + } + + if (mSawInputEOS && mSawOutputEOS) { + // no more data available in buffer, codec or input stream + return -1; + } + + // caller should try again + return 0; } @Override public void close() throws IOException { try { - if (mInputStream != null) mInputStream.close(); + if (mInputStream != null) { + mInputStream.close(); + } } finally { mInputStream = null; try { - if (mGae != 0) GsmAmrEncoderCleanup(mGae); - } finally { - try { - if (mGae != 0) GsmAmrEncoderDelete(mGae); - } finally { - mGae = 0; + if (mCodec != null) { + mCodec.release(); } + } finally { + mCodec = null; } } } @Override protected void finalize() throws Throwable { - if (mGae != 0) { - close(); - throw new IllegalStateException("someone forgot to close AmrInputStream"); + if (mCodec != null) { + Log.w(TAG, "AmrInputStream wasn't closed"); + mCodec.release(); } } - - // - // AudioRecord JNI interface - // - private static native long GsmAmrEncoderNew(); - private static native void GsmAmrEncoderInitialize(long gae); - private static native int GsmAmrEncoderEncode(long gae, - byte[] pcm, int pcmOffset, byte[] amr, int amrOffset) throws IOException; - private static native void GsmAmrEncoderCleanup(long gae); - private static native void GsmAmrEncoderDelete(long gae); - } diff --git a/media/jni/Android.mk b/media/jni/Android.mk index 3355d420aa10..8640565bd91a 100644 --- a/media/jni/Android.mk +++ b/media/jni/Android.mk @@ -2,7 +2,6 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - android_media_AmrInputStream.cpp \ android_media_ExifInterface.cpp \ android_media_ImageWriter.cpp \ android_media_ImageReader.cpp \ @@ -46,11 +45,9 @@ LOCAL_SHARED_LIBRARIES := \ libusbhost \ libexif \ libpiex \ - libstagefright_amrnb_common \ libandroidfw LOCAL_STATIC_LIBRARIES := \ - libstagefright_amrnbenc LOCAL_C_INCLUDES += \ external/libexif/ \ @@ -60,9 +57,6 @@ LOCAL_C_INCLUDES += \ frameworks/base/libs/hwui \ frameworks/av/media/libmedia \ frameworks/av/media/libstagefright \ - frameworks/av/media/libstagefright/codecs/amrnb/enc/src \ - frameworks/av/media/libstagefright/codecs/amrnb/common \ - frameworks/av/media/libstagefright/codecs/amrnb/common/include \ frameworks/av/media/mtp \ frameworks/native/include/media/openmax \ $(call include-path-for, libhardware)/hardware \ diff --git a/media/jni/android_media_AmrInputStream.cpp b/media/jni/android_media_AmrInputStream.cpp deleted file mode 100644 index b56a3641ac9f..000000000000 --- a/media/jni/android_media_AmrInputStream.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* -** -** Copyright 2007, The Android Open Source Project -** -** 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. -*/ - -#define LOG_TAG "AmrInputStream" -#include "utils/Log.h" - -#include "jni.h" -#include "JNIHelp.h" -#include "android_runtime/AndroidRuntime.h" -#include "gsmamr_enc.h" - -// ---------------------------------------------------------------------------- - -using namespace android; - -// Corresponds to max bit rate of 12.2 kbps. -static const int MAX_OUTPUT_BUFFER_SIZE = 32; -static const int FRAME_DURATION_MS = 20; -static const int SAMPLING_RATE_HZ = 8000; -static const int SAMPLES_PER_FRAME = ((SAMPLING_RATE_HZ * FRAME_DURATION_MS) / 1000); -static const int BYTES_PER_SAMPLE = 2; // Assume 16-bit PCM samples -static const int BYTES_PER_FRAME = (SAMPLES_PER_FRAME * BYTES_PER_SAMPLE); - -struct GsmAmrEncoderState { - GsmAmrEncoderState() - : mEncState(NULL), - mSidState(NULL), - mLastModeUsed(0) { - } - - ~GsmAmrEncoderState() {} - - void* mEncState; - void* mSidState; - int32_t mLastModeUsed; -}; - -static jlong android_media_AmrInputStream_GsmAmrEncoderNew - (JNIEnv *env, jclass /* clazz */) { - GsmAmrEncoderState* gae = new GsmAmrEncoderState(); - if (gae == NULL) { - jniThrowRuntimeException(env, "Out of memory"); - } - return (jlong)gae; -} - -static void android_media_AmrInputStream_GsmAmrEncoderInitialize - (JNIEnv *env, jclass /* clazz */, jlong gae) { - GsmAmrEncoderState *state = (GsmAmrEncoderState *) gae; - int32_t nResult = AMREncodeInit(&state->mEncState, &state->mSidState, false); - if (nResult != OK) { - jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", - "GsmAmrEncoder initialization failed %d", nResult); - } -} - -static jint android_media_AmrInputStream_GsmAmrEncoderEncode - (JNIEnv *env, jclass /* clazz */, - jlong gae, jbyteArray pcm, jint pcmOffset, jbyteArray amr, jint amrOffset) { - - jbyte inBuf[BYTES_PER_FRAME]; - jbyte outBuf[MAX_OUTPUT_BUFFER_SIZE]; - - env->GetByteArrayRegion(pcm, pcmOffset, sizeof(inBuf), inBuf); - GsmAmrEncoderState *state = (GsmAmrEncoderState *) gae; - int32_t length = AMREncode(state->mEncState, state->mSidState, - (Mode) MR122, - (int16_t *) inBuf, - (unsigned char *) outBuf, - (Frame_Type_3GPP*) &state->mLastModeUsed, - AMR_TX_WMF); - if (length < 0) { - jniThrowExceptionFmt(env, "java/io/IOException", - "Failed to encode a frame with error code: %d", length); - return (jint)-1; - } - - // The 1st byte of PV AMR frames are WMF (Wireless Multimedia Forum) - // bitpacked, i.e.; - // [P(4) + FT(4)]. Q=1 for good frame, P=padding bit, 0 - // Here we are converting the header to be as specified in Section 5.3 of - // RFC 3267 (AMR storage format) i.e. - // [P(1) + FT(4) + Q(1) + P(2)]. - if (length > 0) { - outBuf[0] = (outBuf[0] << 3) | 0x4; - } - - env->SetByteArrayRegion(amr, amrOffset, length, outBuf); - - return (jint)length; -} - -static void android_media_AmrInputStream_GsmAmrEncoderCleanup - (JNIEnv* /* env */, jclass /* clazz */, jlong gae) { - GsmAmrEncoderState *state = (GsmAmrEncoderState *) gae; - AMREncodeExit(&state->mEncState, &state->mSidState); - state->mEncState = NULL; - state->mSidState = NULL; -} - -static void android_media_AmrInputStream_GsmAmrEncoderDelete - (JNIEnv* /* env */, jclass /* clazz */, jlong gae) { - delete (GsmAmrEncoderState*)gae; -} - -// ---------------------------------------------------------------------------- - -static const JNINativeMethod gMethods[] = { - {"GsmAmrEncoderNew", "()J", (void*)android_media_AmrInputStream_GsmAmrEncoderNew}, - {"GsmAmrEncoderInitialize", "(J)V", (void*)android_media_AmrInputStream_GsmAmrEncoderInitialize}, - {"GsmAmrEncoderEncode", "(J[BI[BI)I", (void*)android_media_AmrInputStream_GsmAmrEncoderEncode}, - {"GsmAmrEncoderCleanup", "(J)V", (void*)android_media_AmrInputStream_GsmAmrEncoderCleanup}, - {"GsmAmrEncoderDelete", "(J)V", (void*)android_media_AmrInputStream_GsmAmrEncoderDelete}, -}; - - -int register_android_media_AmrInputStream(JNIEnv *env) -{ - const char* const kClassPathName = "android/media/AmrInputStream"; - - return AndroidRuntime::registerNativeMethods(env, - kClassPathName, gMethods, NELEM(gMethods)); -} diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index 2fb1a3b9fbcf..8f14b79660be 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -1106,7 +1106,6 @@ extern int register_android_media_MediaScanner(JNIEnv *env); extern int register_android_media_MediaSync(JNIEnv *env); extern int register_android_media_ResampleInputStream(JNIEnv *env); extern int register_android_media_MediaProfiles(JNIEnv *env); -extern int register_android_media_AmrInputStream(JNIEnv *env); extern int register_android_mtp_MtpDatabase(JNIEnv *env); extern int register_android_mtp_MtpDevice(JNIEnv *env); extern int register_android_mtp_MtpServer(JNIEnv *env); @@ -1152,11 +1151,6 @@ jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) goto bail; } - if (register_android_media_AmrInputStream(env) < 0) { - ALOGE("ERROR: AmrInputStream native registration failed\n"); - goto bail; - } - if (register_android_media_ResampleInputStream(env) < 0) { ALOGE("ERROR: ResampleInputStream native registration failed\n"); goto bail; |