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層コードを見てみましょう.
Javaレイヤ:createByName->New MediaCode()->setup(native宣言メソッド)JNIレイヤ:1、マッピング(android_media_MediaCodec.cpp)
2、setup->android_media_MediaCodec_native_setup
3、ポイントはsp codec=new JMediaCodec(env,thiz,tmp,nameIsType,encoder);spはスマートポインタの強いポインタ(Strong Pointer)であり、スマートポインタは主にメモリ関連を柔軟に管理している.またwp(Weak Pointer)は、弱い参照ポインタオブジェクトで、systemcoreincludeutilsStrongPointer.hの下で、このgoogleのものを直接コピーして、プロジェクトに運用してもいいです.
4、構造関数を見て、
mCodec=MediaCodec::CreateByType(mLooper,name,encoder,&mInitStatus)//ここのMedCodecはstagefrightのMediaCodecで、frameworksavmedialibstagefrightMediaCodecにあります.cppはlibstagefrightに属する.so
5、MediaCodecに入る.cpp中
6、構造
ここはちょっと面白いです.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では,虚関数だけでなく,友元も定義できる.
最後の疑問点
読みながらNdkMediaCodecおよびNdkMediaCodecも発見する.cppこれらのclassと、上のいくつかのclassの違いは何ですか?何の関係があるの?どうしてこんなデザインするの?frameworks\av\includedk\NdkMediaCodec.h
AMediaCodecには、以下のようにcreateAMediaCodecがあります.
中にはandroid::MediaCodec::CreateByType(mData->mLooper,name,encoder);MediaCodecのCreateByType関数が呼び出されます.このような役割はしばらく明確ではありません.Googleの標識を見るのは動かないで、変えないで、むやみにしないで、これはNDK APIです.
まず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ではなく構造体を大量に使用するのはなぜですか.
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です.