Android Multimediaフレームワークまとめ(二十七)MediaCodecレビュー

23990 ワード

Android AppがMediaCodec Java APIを介して入手したコーデックは、実際にはStageFrightメディアフレームワークによって提供されている.android.media.MediaCodec呼び出しlibmedia_jni.so中のJNI native関数、これらのJNI関数はlibstagefrightを呼び出す.soライブラリはStageFrightフレームワーク内のコーデックを取得する.StageFrightはOMXコンポーネントを再呼び出して復号する.これは前に整理した流れです.今回の再読は主に彼らの呼び出し過程を理解することだ.
まずJava層コードを見てみましょう.
mediaCodec = MediaCodec.createByCodecName(codecName);//  Codec
MediaFormat mediaFormat = MediaFormat.createVideoFormat(MINE_TYPE, width, height);

mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bit);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, fps);
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); //        //   s
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,                                    
                MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);
                // mediaFormat.setInteger(MediaFormat.KEY_PROFILE,
                // CodecProfileLevel.AVCProfileBaseline);
                // mediaFormat.setInteger(MediaFormat.KEY_LEVEL,
                // CodecProfileLevel.AVCLevel52);
                mediaCodec.configure(mediaFormat, null, null,
                        MediaCodec.CONFIGURE_FLAG_ENCODE);
                mediaCodec.start();

Javaレイヤ:createByName->New MediaCode()->setup(native宣言メソッド)JNIレイヤ:1、マッピング(android_media_MediaCodec.cpp)
    { "native_setup", "(Ljava/lang/String;ZZ)V",
      (void *)android_media_MediaCodec_native_setup },

2、setup->android_media_MediaCodec_native_setup
static void android_media_MediaCodec_native_setup(
        JNIEnv *env, jobject thiz,
        jstring name, jboolean nameIsType, jboolean encoder) {
    if (name == NULL) {
        //      ,jni        。  jniThrowException
        jniThrowException(env, "java/lang/NullPointerException", NULL);
        return;
    }

    const char *tmp = env->GetStringUTFChars(name, NULL);

    if (tmp == NULL) {
        return;
    }

    sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder);

    const status_t err = codec->initCheck();
    if (err == NAME_NOT_FOUND) {
        // fail and do not try again.
        jniThrowException(env, "java/lang/IllegalArgumentException",
                String8::format("Failed to initialize %s, error %#x", tmp, err));
        env->ReleaseStringUTFChars(name, tmp);
        return;
    } if (err == NO_MEMORY) {
        throwCodecException(env, err, ACTION_CODE_TRANSIENT,
                String8::format("Failed to initialize %s, error %#x", tmp, err));
        env->ReleaseStringUTFChars(name, tmp);
        return;
    } else if (err != OK) {
        // believed possible to try again
        jniThrowException(env, "java/io/IOException",
                String8::format("Failed to find matching codec %s, error %#x", tmp, err));
        env->ReleaseStringUTFChars(name, tmp);
        return;
    }

    env->ReleaseStringUTFChars(name, tmp);

    codec->registerSelf();

    setMediaCodec(env,thiz, codec);
}

3、ポイントはsp codec=new JMediaCodec(env,thiz,tmp,nameIsType,encoder);spはスマートポインタの強いポインタ(Strong Pointer)であり、スマートポインタは主にメモリ関連を柔軟に管理している.またwp(Weak Pointer)は、弱い参照ポインタオブジェクトで、systemcoreincludeutilsStrongPointer.hの下で、このgoogleのものを直接コピーして、プロジェクトに運用してもいいです.
4、構造関数を見て、
JMediaCodec::JMediaCodec(
        JNIEnv *env, jobject thiz,
        const char *name, bool nameIsType, bool encoder)
    : mClass(NULL),
      mObject(NULL) {
    jclass clazz = env->GetObjectClass(thiz);//  class
    CHECK(clazz != NULL);

    mClass = (jclass)env->NewGlobalRef(clazz);//    
    mObject = env->NewWeakGlobalRef(thiz);//     

    cacheJavaObjects(env);

    mLooper = new ALooper;
    mLooper->setName("MediaCodec_looper");

    mLooper->start(
            false,      // runOnCallingThread
            true,       // canCallJava
            PRIORITY_FOREGROUND);

    if (nameIsType) {
        mCodec = MediaCodec::CreateByType(mLooper, name, encoder, &mInitStatus);
    } else {
        mCodec = MediaCodec::CreateByComponentName(mLooper, name, &mInitStatus);
    }
    CHECK((mCodec != NULL) != (mInitStatus != OK));
}

mCodec=MediaCodec::CreateByType(mLooper,name,encoder,&mInitStatus)//ここのMedCodecはstagefrightのMediaCodecで、frameworksavmedialibstagefrightMediaCodecにあります.cppはlibstagefrightに属する.so
5、MediaCodecに入る.cpp中
// static
sp<MediaCodec> MediaCodec::CreateByType(
        const sp<ALooper> &looper, const char *mime, bool encoder, status_t *err, pid_t pid) {
    sp<MediaCodec> codec = new MediaCodec(looper, pid);

    const status_t ret = codec->init(mime, true /* nameIsType */, encoder);
    if (err != NULL) {
        *err = ret;
    }
    return ret == OK ? codec : NULL; // NULL deallocates codec.
}

6、構造
MediaCodec::MediaCodec(const sp<ALooper> &looper, pid_t pid)
    : mState(UNINITIALIZED),
      mReleasedByResourceManager(false),
      mLooper(looper),
      mCodec(NULL),
      mReplyID(0),
      mFlags(0),
      mStickyError(OK),
      mSoftRenderer(NULL),
      mResourceManagerClient(new ResourceManagerClient(this)),
      mResourceManagerService(new ResourceManagerServiceProxy(pid)),
      mBatteryStatNotified(false),
      mIsVideo(false),
      mVideoWidth(0),
      mVideoHeight(0),
      mRotationDegrees(0),
      mDequeueInputTimeoutGeneration(0),
      mDequeueInputReplyID(0),
      mDequeueOutputTimeoutGeneration(0),
      mDequeueOutputReplyID(0),
      mHaveInputSurface(false),
      mHavePendingInputBuffers(false) {
}

ここはちょっと面白いです.MediaCodecを直接.hヘッダーファイルの変数を初期化します.注意:(コロン)、MediaCodecを見てください.hでは、最初の文はstruct MediaCodec:public AHandler、つまりMediaCodecは構造体であり、この構造体はAHandler(構造体でもある)を継承し、AHandlerはutils/RefBaseを継承する.h,classではなく構造体を大量に使用するのはなぜですか.
  • 1、構造体はスタックの中で作成して、値のタイプで、速度は比較的に速い
  • に戻ります
  • 2、Cとの互換性も考えられ、Cにも構造体がある.
  • 3、通常はベースタイプとして扱う小オブジェクト
  • を処理するために用いられる.
  • 4、c++のうちの構造体は継承可能(ネット上では構造体が多く、継承できないという説が訂正されているが、ここではソースコードも証明され、構造体は他のものだけでなく、他のものも継承できる)
  • structとclassは本質的に同じであるべきで、ただデフォルトのアクセス権限が異なる(structデフォルトはpublic、classデフォルトはprivate)、ネット上では構造体が虚関数を定義できないという説もあるが、この説も間違っており、以下のコードもMediaCodecである.hでは,虚関数だけでなく,友元も定義できる.
    protected:
        virtual ~MediaCodec();
        virtual void onMessageReceived(const sp<AMessage> &msg);
    
    private:
        // used by ResourceManagerClient
        status_t reclaim(bool force = false);
        friend struct ResourceManagerClient;

    最後の疑問点
    読みながらNdkMediaCodecおよびNdkMediaCodecも発見する.cppこれらのclassと、上のいくつかのclassの違いは何ですか?何の関係があるの?どうしてこんなデザインするの?frameworks\av\includedk\NdkMediaCodec.h
    /* * This file defines an NDK API. * Do not remove methods. * Do not change method signatures. * Do not change the value of constants. * Do not change the size of any of the classes defined in here. * Do not reference types that are not part of the NDK. * Do not #include files that aren't part of the NDK. */
    
    #ifndef _NDK_MEDIA_CODEC_H
    #define _NDK_MEDIA_CODEC_H
    
    #include <android/native_window.h>
    
    #include "NdkMediaCrypto.h"
    #include "NdkMediaError.h"
    #include "NdkMediaFormat.h"
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    
    struct AMediaCodec;
    typedef struct AMediaCodec AMediaCodec;
    
    struct AMediaCodecBufferInfo {
        int32_t offset;
        int32_t size;
        int64_t presentationTimeUs;
        uint32_t flags;
    };
    typedef struct AMediaCodecBufferInfo AMediaCodecBufferInfo;
    typedef struct AMediaCodecCryptoInfo AMediaCodecCryptoInfo;
    
    enum {
        AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM = 4,
        AMEDIACODEC_CONFIGURE_FLAG_ENCODE = 1,
        AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED = -3,
        AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED = -2,
        AMEDIACODEC_INFO_TRY_AGAIN_LATER = -1
    };
    
    /** * Create codec by name. Use this if you know the exact codec you want to use. * When configuring, you will need to specify whether to use the codec as an * encoder or decoder. */
    AMediaCodec* AMediaCodec_createCodecByName(const char *name);
    
    /** * Create codec by mime type. Most applications will use this, specifying a * mime type obtained from media extractor. */
    AMediaCodec* AMediaCodec_createDecoderByType(const char *mime_type);
    
    /** * Create encoder by name. */
    AMediaCodec* AMediaCodec_createEncoderByType(const char *mime_type);
    
    /** * delete the codec and free its resources */
    media_status_t AMediaCodec_delete(AMediaCodec*);
    
    /** * Configure the codec. For decoding you would typically get the format from an extractor. */
    media_status_t AMediaCodec_configure(
            AMediaCodec*,
            const AMediaFormat* format,
            ANativeWindow* surface,
            AMediaCrypto *crypto,
            uint32_t flags);
    
    /** * Start the codec. A codec must be configured before it can be started, and must be started * before buffers can be sent to it. */
    media_status_t AMediaCodec_start(AMediaCodec*);
    
    /** * Stop the codec. */
    media_status_t AMediaCodec_stop(AMediaCodec*);
    
    /* * Flush the codec's input and output. All indices previously returned from calls to * AMediaCodec_dequeueInputBuffer and AMediaCodec_dequeueOutputBuffer become invalid. */
    media_status_t AMediaCodec_flush(AMediaCodec*);
    
    /** * Get an input buffer. The specified buffer index must have been previously obtained from * dequeueInputBuffer, and not yet queued. */
    uint8_t* AMediaCodec_getInputBuffer(AMediaCodec*, size_t idx, size_t *out_size);
    
    /** * Get an output buffer. The specified buffer index must have been previously obtained from * dequeueOutputBuffer, and not yet queued. */
    uint8_t* AMediaCodec_getOutputBuffer(AMediaCodec*, size_t idx, size_t *out_size);
    
    /** * Get the index of the next available input buffer. An app will typically use this with * getInputBuffer() to get a pointer to the buffer, then copy the data to be encoded or decoded * into the buffer before passing it to the codec. */
    ssize_t AMediaCodec_dequeueInputBuffer(AMediaCodec*, int64_t timeoutUs);
    
    /** * Send the specified buffer to the codec for processing. */
    media_status_t AMediaCodec_queueInputBuffer(AMediaCodec*,
            size_t idx, off_t offset, size_t size, uint64_t time, uint32_t flags);
    
    /** * Send the specified buffer to the codec for processing. */
    media_status_t AMediaCodec_queueSecureInputBuffer(AMediaCodec*,
            size_t idx, off_t offset, AMediaCodecCryptoInfo*, uint64_t time, uint32_t flags);
    
    /** * Get the index of the next available buffer of processed data. */
    ssize_t AMediaCodec_dequeueOutputBuffer(AMediaCodec*, AMediaCodecBufferInfo *info,
            int64_t timeoutUs);
    AMediaFormat* AMediaCodec_getOutputFormat(AMediaCodec*);
    
    /** * If you are done with a buffer, use this call to return the buffer to * the codec. If you previously specified a surface when configuring this * video decoder you can optionally render the buffer. */
    media_status_t AMediaCodec_releaseOutputBuffer(AMediaCodec*, size_t idx, bool render);
    
    /** * If you are done with a buffer, use this call to update its surface timestamp * and return it to the codec to render it on the output surface. If you * have not specified an output surface when configuring this video codec, * this call will simply return the buffer to the codec. * * For more details, see the Java documentation for MediaCodec.releaseOutputBuffer. */
    media_status_t AMediaCodec_releaseOutputBufferAtTime(
            AMediaCodec *mData, size_t idx, int64_t timestampNs);
    
    
    typedef enum {
        AMEDIACODECRYPTOINFO_MODE_CLEAR = 0,
        AMEDIACODECRYPTOINFO_MODE_AES_CTR = 1
    } cryptoinfo_mode_t;
    
    /** * Create an AMediaCodecCryptoInfo from scratch. Use this if you need to use custom * crypto info, rather than one obtained from AMediaExtractor. * * AMediaCodecCryptoInfo describes the structure of an (at least * partially) encrypted input sample. * A buffer's data is considered to be partitioned into "subsamples", * each subsample starts with a (potentially empty) run of plain, * unencrypted bytes followed by a (also potentially empty) run of * encrypted bytes. * numBytesOfClearData can be null to indicate that all data is encrypted. * This information encapsulates per-sample metadata as outlined in * ISO/IEC FDIS 23001-7:2011 "Common encryption in ISO base media file format files". */
    AMediaCodecCryptoInfo *AMediaCodecCryptoInfo_new(
            int numsubsamples,
            uint8_t key[16],
            uint8_t iv[16],
            cryptoinfo_mode_t mode,
            size_t *clearbytes,
            size_t *encryptedbytes);
    
    /** * delete an AMediaCodecCryptoInfo created previously with AMediaCodecCryptoInfo_new, or * obtained from AMediaExtractor */
    media_status_t AMediaCodecCryptoInfo_delete(AMediaCodecCryptoInfo*);
    
    /** * The number of subsamples that make up the buffer's contents. */
    size_t AMediaCodecCryptoInfo_getNumSubSamples(AMediaCodecCryptoInfo*);
    
    /** * A 16-byte opaque key */
    media_status_t AMediaCodecCryptoInfo_getKey(AMediaCodecCryptoInfo*, uint8_t *dst);
    
    /** * A 16-byte initialization vector */
    media_status_t AMediaCodecCryptoInfo_getIV(AMediaCodecCryptoInfo*, uint8_t *dst);
    
    /** * The type of encryption that has been applied, * one of AMEDIACODECRYPTOINFO_MODE_CLEAR or AMEDIACODECRYPTOINFO_MODE_AES_CTR. */
    cryptoinfo_mode_t AMediaCodecCryptoInfo_getMode(AMediaCodecCryptoInfo*);
    
    /** * The number of leading unencrypted bytes in each subsample. */
    media_status_t AMediaCodecCryptoInfo_getClearBytes(AMediaCodecCryptoInfo*, size_t *dst);
    
    /** * The number of trailing encrypted bytes in each subsample. */
    media_status_t AMediaCodecCryptoInfo_getEncryptedBytes(AMediaCodecCryptoInfo*, size_t *dst);
    
    #ifdef __cplusplus
    } // extern "C"
    #endif
    
    #endif //_NDK_MEDIA_CODEC_H
    

    AMediaCodecには、以下のようにcreateAMediaCodecがあります.
    static AMediaCodec * createAMediaCodec(const char *name, bool name_is_type, bool encoder) {
        AMediaCodec *mData = new AMediaCodec();
        mData->mLooper = new ALooper;
        mData->mLooper->setName("NDK MediaCodec_looper");
        status_t ret = mData->mLooper->start(
                false,      // runOnCallingThread
                true,       // canCallJava XXX
                PRIORITY_FOREGROUND);
        if (name_is_type) {
            mData->mCodec = android::MediaCodec::CreateByType(mData->mLooper, name, encoder);
        } else {
            mData->mCodec = android::MediaCodec::CreateByComponentName(mData->mLooper, name);
        }
        if (mData->mCodec == NULL) {  // failed to create codec
            AMediaCodec_delete(mData);
            return NULL;
        }
        mData->mHandler = new CodecHandler(mData);
        mData->mLooper->registerHandler(mData->mHandler);
        mData->mGeneration = 1;
        mData->mRequestedActivityNotification = false;
        mData->mCallback = NULL;
    
        return mData;
    }

    中にはandroid::MediaCodec::CreateByType(mData->mLooper,name,encoder);MediaCodecのCreateByType関数が呼び出されます.このような役割はしばらく明確ではありません.Googleの標識を見るのは動かないで、変えないで、むやみにしないで、これはNDK APIです.