OSDN Git Service

Make AmrInputStream use MediaCodec
authorMarco Nelissen <marcone@google.com>
Thu, 13 Oct 2016 21:38:40 +0000 (14:38 -0700)
committerMarco Nelissen <marcone@google.com>
Fri, 11 Nov 2016 18:53:09 +0000 (10:53 -0800)
Bug: 32114618
Test: ran CTS test
Change-Id: I9fe77689bf0bcd1bc8f6c5a4d62135271c7bef8b

media/java/android/media/AmrInputStream.java
media/jni/Android.mk
media/jni/android_media_AmrInputStream.cpp [deleted file]
media/jni/android_media_MediaPlayer.cpp

index f90f1e2..fb91bbb 100644 (file)
@@ -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);
-
 }
index 3355d42..8640565 100644 (file)
@@ -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 (file)
index b56a364..0000000
+++ /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));
-}
index 2fb1a3b..8f14b79 100644 (file)
@@ -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;