[Code source Android 10] Compréhension approfondie de l'initialisation du décodage dur MediaCodec

Série d'articles MediaCodec :

  1. [Code source Android 10] Compréhension approfondie de l'initialisation du décodage dur MediaCodec
  2. [Code source Android 10] Compréhension approfondie de l'initialisation d'Omx
  3. [Code source Android 10] Compréhension approfondie du démarrage du service de codec
  4. [Code source Android 10] Compréhension approfondie du démarrage du service logiciel Codec2
  5. [Code source Android 10] Compréhension approfondie de la création de la liste MediaCodec : buildMediaCodecList
  6. [Code source Android 10] Compréhension approfondie de l'allocation des composants MediaCodec
  7. [Code source Android 10] Compréhension approfondie de la configuration de MediaCodec

L'appel de l'API de décodage matériel dans Android est implémenté en utilisant MediaCodec pour appeler le matériel étape par étape. Habituellement, il doit appeler le VPU pour effectuer le travail de décodage. Maintenant, analysons d'abord son processus d'initialisation.
insérez la description de l'image ici

Ce qui suit est un code d'initialisation de décodage dur typique. Bien sûr, la gestion des exceptions est également effectuée pour une meilleure tolérance aux pannes.

  1. Créez un décodeur basé sur MIME_TYPE (vidéo/avc), appelez createDecoderByType pour y parvenir ;
  2. Créer une configuration MediaFormat en fonction de la longueur et de la largeur de la vidéo et de MIME_TYPE (définir l'espace colorimétrique de décodage, la ligne de base du profil et le niveau du profil, etc.) ;
  3. Passez la configuration MediaFormat, Surface (l'image de dessin décodée peut être nulle après décodage), etc. à la fonction de configuration du décodeur configure pour la configuration.

public class H264Decoder {
    private static final String TAG = "H264Decoder";
    private final static String MIME_TYPE = "video/avc"; // H.264 Advanced Video

    private MediaCodec mDecoderMediaCodec;
   
    private int mFps;
    private Surface mSurface;
    private int mWidth;
    private int mHeight;
    private int mInitMediaCodecTryTimes = 0;

    public H264Decoder(int width, int height, int fps, Surface surface) {
        mWidth = width;
        mHeight = height;
        mFps = fps;
        mSurface = surface;

        initMediaCodec(width, height, surface);
    }

    private void initMediaCodec(int width, int height, Surface surface) {
        try {
            mDecoderMediaCodec = MediaCodec.createDecoderByType(MIME_TYPE);
            //创建配置
            MediaFormat mediaFormat = MediaFormat.createVideoFormat(MIME_TYPE, width, height);
            mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
            //mediaFormat.setInteger(MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline);
            //mediaFormat.setInteger("level", MediaCodecInfo.CodecProfileLevel.AVCLevel4); // Level 4

            mDecoderMediaCodec.configure(mediaFormat, surface, null, 0);
        } catch (Exception e) {
            e.printStackTrace();
            //创建解码失败
            Log.e(TAG, "init MediaCodec fail.");
            // 重新尝试六次
            if (mInitMediaCodecTryTimes < 6) {
                mInitMediaCodecTryTimes++;
                try {
                    Thread.sleep(20);
                } catch (InterruptedException ie) {
                    ie.printStackTrace();
                }
                initMediaCodec(mWidth, mHeight, mSurface);
            }
        }
    }
    ......
}

Maintenant, analysez la première étape, comment createDecoderByType trouve-t-il le MediaCodec instancié ?

Instancie le décodeur préféré qui prend en charge le type mime donné.

Voici une liste partielle des types mime définis et de leur sémantique :

"video/x-vnd.on2.vp8" - vidéo VP8 (par exemple vidéo dans webm)

"video/x-vnd.on2.vp9" - vidéo VP9 (par exemple vidéo dans webm)

"vidéo/avc" - vidéo H.264/AVC

"vidéo/hevc" - vidéo H.265/HEVC

"video/mp4v-es" - Vidéo MPEG4

"vidéo/3gpp" - vidéo H.263

« audio/3gpp » : audio à bande étroite AMR

« audio/amr-wb » - audio large bande AMR

"audio/mpeg" - Couche audio III MPEG1/2

"audio/mp4a-latm" - audio AAC (notez qu'il s'agit de paquets AAC bruts, non encapsulés dans LATM)

"audio/vorbis" - audio vorbis

"audio/g711-alaw" - G.711 alaw audio

"audio/g711-mlaw" - G.711 audio ulaw

Le constructeur MediaCodec est en fait appelé en interne, mais les deux derniers paramètres sont codés en dur, nameIsType (true), comme son nom l'indique, si le nom est un type, et encoder (false) s'il s'agit d'un encodeur.

frameworks/base/media/java/android/media/MediaCodec.java

final public class MediaCodec {
    ......
    @NonNull
    public static MediaCodec createDecoderByType(@NonNull String type)
            throws IOException {
        return new MediaCodec(type, true /* nameIsType */, false /* encoder */);
    }
    ......
}

  1. Obtenez l'objet Looper. S'il est appelé dans un thread normal, alors Looper.myLooper() ne doit pas être nul, puis utilisez cet objet Looper pour construire EventHandler. Sinon, utilisez Looper.getMainLooper() pour obtenir l'objet Looper du thread principal et construire EventHandler ;
  2. Affectez ensuite à la fois mCallbackHandler (callback "handle") et mOnFrameRenderedHandler (frame rendering "handle") à l'objet EventHandler que vous venez de créer. Cet EventHandler est utilisé pour gérer les événements, et il sera utilisé plus tard pour analyser les événements spécifiques qu'il gère ;
  3. Créer un Buffer Lock pour la corrélation de synchronisation ;
  4. mNameAtCreation indique s'il faut enregistrer le nom utilisé au moment de la création, ici c'est vrai, donc ce n'est pas nécessaire, car on connaît déjà le nom ;
  5. Enfin, appelez native_setup pour effectuer des appels jni pour une initialisation supplémentaire.

frameworks/base/media/java/android/media/MediaCodec.java

final public class MediaCodec {
    ......
    private EventHandler mEventHandler;
    private EventHandler mOnFrameRenderedHandler;
    private EventHandler mCallbackHandler;
    ......
    final private Object mBufferLock;
    ......
    private MediaCodec(
            @NonNull String name, boolean nameIsType, boolean encoder) {
        Looper looper;
        if ((looper = Looper.myLooper()) != null) {
            mEventHandler = new EventHandler(this, looper);
        } else if ((looper = Looper.getMainLooper()) != null) {
            mEventHandler = new EventHandler(this, looper);
        } else {
            mEventHandler = null;
        }
        mCallbackHandler = mEventHandler;
        mOnFrameRenderedHandler = mEventHandler;

        mBufferLock = new Object();

        // save name used at creation
        mNameAtCreation = nameIsType ? null : name;

        native_setup(name, nameIsType, encoder);
    }
    
    private String mNameAtCreation;
    ......
}

  1. Créez un objet JMediaCodec ;
  2. Vérifiez s'il y a une erreur lors de la création de l'objet JMediaCodec, et si une erreur se produit, branchez-vous à l'instruction correspondante et lancez une exception à la couche java ;
  3. Appelez JMediaCodec registerSelf() pour vous enregistrer ;
  4. Appelez la fonction setMediaCodec(…) pour affecter le pointeur vers l'objet JMediaCodec au champ de couche Java (MediaCodec.java mNativeContext).

frameworks/base/media/jni/android_media_MediaCodec.cpp

static void android_media_MediaCodec_native_setup(
        JNIEnv *env, jobject thiz,
        jstring name, jboolean nameIsType, jboolean encoder) {
    if (name == NULL) {
        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);
}
......
static const JNINativeMethod gMethods[] = {
    ......
    { "native_setup", "(Ljava/lang/String;ZZ)V",
      (void *)android_media_MediaCodec_native_setup },
    ......
};

  1. Mettez en cache certains objets jni pour une utilisation ultérieure ;
  2. Créez un objet ALooper, démarrez-le et utilisez le LooperThread nouvellement créé (runOnCallingThread est faux pour indiquer que le traitement sur le thread n'est plus appelé) pour traiter les événements ;
  3. nameIsType est égal à true, appelez la méthode Native MediaCodec CreateByType pour créer un objet Native MediaCodec.

frameworks/base/media/jni/android_media_MediaCodec.cpp

JMediaCodec::JMediaCodec(
        JNIEnv *env, jobject thiz,
        const char *name, bool nameIsType, bool encoder)
    : mClass(NULL),
      mObject(NULL) {
    jclass clazz = env->GetObjectClass(thiz);
    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
            ANDROID_PRIORITY_VIDEO);

    if (nameIsType) {
        mCodec = MediaCodec::CreateByType(mLooper, name, encoder, &mInitStatus);
        if (mCodec == nullptr || mCodec->getName(&mNameAtCreation) != OK) {
            mNameAtCreation = "(null)";
        }
    } else {
        mCodec = MediaCodec::CreateByComponentName(mLooper, name, &mInitStatus);
        mNameAtCreation = name;
    }
    CHECK((mCodec != NULL) != (mInitStatus != OK));
}

Les deux derniers paramètres d'entrée sont -1.

  1. Appelez MediaCodecList::findMatchingCodecs(…) pour trouver tous les codecs correspondants ;
  2. Parcourez les noms de composants dans le conteneur Vector renvoyé à l'étape précédente, créez un objet MediaCodec cpp et appelez sa fonction init(…) en fonction du nom du composant ;
  3. S'il n'y a pas d'erreur dans l'initialisation, il renverra directement l'objet MediaCodec cpp.

frameworks/av/media/libstagefright/MediaCodec.cpp

sp<MediaCodec> MediaCodec::CreateByType(
        const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err, pid_t pid,
        uid_t uid) {
    Vector<AString> matchingCodecs;

    MediaCodecList::findMatchingCodecs(
            mime.c_str(),
            encoder,
            0,
            &matchingCodecs);

    if (err != NULL) {
        *err = NAME_NOT_FOUND;
    }
    for (size_t i = 0; i < matchingCodecs.size(); ++i) {
        sp<MediaCodec> codec = new MediaCodec(looper, pid, uid);
        AString componentName = matchingCodecs[i];
        status_t ret = codec->init(componentName);
        if (err != NULL) {
            *err = ret;
        }
        if (ret == OK) {
            return codec;
        }
        ALOGD("Allocating component '%s' failed (%d), try next one.",
                componentName.c_str(), ret);
    }
    return NULL;
}

  1. Appelez getInstance() pour obtenir l'objet BpMediaCodecList ;
  2. Appelez la méthode findCodecByType(…) de BpMediaCodecList, qui est en fait traitée par MediaCodecList::findCodecByType(…) pour renvoyer l'index correspondant. Lorsque matchIndex est inférieur à 0, quittez la boucle ;
  3. Appelez la méthode getCodecInfo(…) de BpMediaCodecList pour obtenir MediaCodecInfo ;
  4. Obtenez le nom du décodeur via l'objet MediaCodecInfo getCodecName();
  5. Le paramètre d'entrée flags est égal à 0, donc le nom du composant du décodeur est directement poussé dans les correspondances du conteneur ;
  6. Ensuite, déterminez si vous souhaitez trier les éléments du conteneur en fonction de la définition ou non de la propriété debug.stagefright.swcodec.

frameworks/av/media/libstagefright/MediaCodecList.cpp

void MediaCodecList::findMatchingCodecs(
        const char *mime, bool encoder, uint32_t flags,
        Vector<AString> *matches) {
    matches->clear();

    const sp<IMediaCodecList> list = getInstance();
    if (list == nullptr) {
        return;
    }

    size_t index = 0;
    for (;;) {
        ssize_t matchIndex =
            list->findCodecByType(mime, encoder, index);

        if (matchIndex < 0) {
            break;
        }

        index = matchIndex + 1;

        const sp<MediaCodecInfo> info = list->getCodecInfo(matchIndex);
        CHECK(info != nullptr);
        AString componentName = info->getCodecName();

        if ((flags & kHardwareCodecsOnly) && isSoftwareCodec(componentName)) {
            ALOGV("skipping SW codec '%s'", componentName.c_str());
        } else {
            matches->push(componentName);
            ALOGV("matching '%s'", componentName.c_str());
        }
    }

    if (flags & kPreferSoftwareCodecs ||
            property_get_bool("debug.stagefright.swcodec", false)) {
        matches->sort(compareSoftwareCodecsFirst);
    }
}

Recherchez le service nommé media.player, qui est en fait MediaPlayerService, puis obtenez IMediaCodecList (BpMediaCodecList) via MediaPlayerService getCodecList(). Ici, la communication inter-processus est en fait appelée vers le MediaPlayerService distant via BpMediaPlayerService.

frameworks/av/media/libstagefright/MediaCodecList.cpp

sp<IMediaCodecList> MediaCodecList::getInstance() {
    Mutex::Autolock _l(sRemoteInitMutex);
    if (sRemoteList == nullptr) {
        sp<IBinder> binder =
            defaultServiceManager()->getService(String16("media.player"));
        sp<IMediaPlayerService> service =
            interface_cast<IMediaPlayerService>(binder);
        if (service.get() != nullptr) {
            sRemoteList = service->getCodecList();
            if (sRemoteList != nullptr) {
                sBinderDeathObserver = new BinderDeathObserver();
                binder->linkToDeath(sBinderDeathObserver.get());
            }
        }
        if (sRemoteList == nullptr) {
            // if failed to get remote list, create local list
            sRemoteList = getLocalInstance();
        }
    }
    return sRemoteList;
}

La méthode BpMediaPlayerService getCodecList() utilise le mécanisme de classeur pour écrire des données et attendre la réponse de l'extrémité distante.

frameworks/av/media/libmedia/IMediaPlayerService.cpp

class BpMediaPlayerService: public BpInterface<IMediaPlayerService>
{
public:
    ......
    virtual sp<IMediaCodecList> getCodecList() const {
        Parcel data, reply;
        data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
        remote()->transact(GET_CODEC_LIST, data, &reply);
        return interface_cast<IMediaCodecList>(reply.readStrongBinder());
    }
};

Le répondeur réel est MediaPlayerService::getCodecList(). Cette fonction appelle en interne MediaCodecList::getLocalInstance(), puis revient en arrière (car le processus appelant et le processus répondant sont différents, bien que les deux utilisent les fonctions de la même classe).

frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp

sp<IMediaCodecList> MediaPlayerService::getCodecList() const {
    return MediaCodecList::getLocalInstance();
}

MediaCodecList hérite de BnMediaCodecList, et le commentaire dit : getLocalInstance() n'est utilisé que par MediaPlayerService.

  1. Créez un nouvel objet MediaCodecList, mais le paramètre d'entrée est la valeur de retour d'une autre fonction GetBuilders();
  2. Vérifiez si l'étape précédente est anormale et renvoyez enfin l'objet singleton BpMediaCodecList s'il n'y a pas d'anomalie ;
  3. isProfilingNeeded() voit que le nom est lié à l'analyse du codec, et il n'est pas analysé en détail s'il n'est pas dans le processus principal.

frameworks/av/media/libstagefright/MediaCodecList.cpp

sp<IMediaCodecList> MediaCodecList::getLocalInstance() {
    Mutex::Autolock autoLock(sInitMutex);

    if (sCodecList == nullptr) {
        MediaCodecList *codecList = new MediaCodecList(GetBuilders());
        if (codecList->initCheck() == OK) {
            sCodecList = codecList;

            if (isProfilingNeeded()) {
                ALOGV("Codec profiling needed, will be run in separated thread.");
                pthread_t profiler;
                if (pthread_create(&profiler, nullptr, profilerThreadWrapper, nullptr) != 0) {
                    ALOGW("Failed to create thread for codec profiling.");
                }
            }
        } else {
            // failure to initialize may be temporary. retry on next call.
            delete codecList;
        }
    }

    return sCodecList;
}

  1. Parcourez tous les MediaCodecListBuilderBase* dans le conteneur renvoyé par GetBuilders();
  2. Appelez la méthode buildMediaCodecList(…) de chaque générateur ;
  3. MediaCodecListWriter écrit les paramètres globaux dans AMessage et écrit les informations de codec dans le conteneur pointé par mCodecInfos ;
  4. mCodecInfos est une variable pointant vers le conteneur std::vector<sp>, triant les éléments dans le conteneur (le tri est basé sur le rang) ;
  5. Supprimez les éléments en double dans le conteneur, à condition que le commutateur de propriété debug.stagefright.dedupe-codecs soit activé.

frameworks/av/media/libstagefright/MediaCodecList.cpp

MediaCodecList::MediaCodecList(std::vector<MediaCodecListBuilderBase*> builders) {
    mGlobalSettings = new AMessage();
    mCodecInfos.clear();
    MediaCodecListWriter writer;
    for (MediaCodecListBuilderBase *builder : builders) {
        if (builder == nullptr) {
            ALOGD("ignored a null builder");
            continue;
        }
        mInitCheck = builder->buildMediaCodecList(&writer);
        if (mInitCheck != OK) {
            break;
        }
    }
    writer.writeGlobalSettings(mGlobalSettings);
    writer.writeCodecInfos(&mCodecInfos);
    std::stable_sort(
            mCodecInfos.begin(),
            mCodecInfos.end(),
            [](const sp<MediaCodecInfo> &info1, const sp<MediaCodecInfo> &info2) {
                // null is lowest
                return info1 == nullptr
                        || (info2 != nullptr && info1->getRank() < info2->getRank());
            });

    // remove duplicate entries
    bool dedupe = property_get_bool("debug.stagefright.dedupe-codecs", true);
    if (dedupe) {
        std::set<std::string> codecsSeen;
        for (auto it = mCodecInfos.begin(); it != mCodecInfos.end(); ) {
            std::string codecName = (*it)->getCodecName();
            if (codecsSeen.count(codecName) == 0) {
                codecsSeen.emplace(codecName);
                it++;
            } else {
                it = mCodecInfos.erase(it);
            }
        }
    }
}

Dépend du plugin pour fournir une liste des codecs OMX disponibles. Si le plugin fournit une surface d'entrée, l'encodeur vidéo OMX ne peut pas être utilisé.

  1. Appelez StagefrightPluginLoader::GetCCodecInstance() pour obtenir l'objet StagefrightPluginLoader, puis appelez sa méthode createInputSurface() ;
  2. Branchez le builder à ajouter au conteneur en fonction de la valeur de retour de l'étape précédente ;
  3. Ajoutez le générateur renvoyé par GetCodec2InfoBuilder() au conteneur.

La fonction GetCodec2InfoBuilder() appelle en interne la méthode createBuilder() de l'objet StagefrightPluginLoader pour renvoyer le générateur.

frameworks/av/media/libstagefright/MediaCodecList.cpp

OmxInfoBuilder sOmxInfoBuilder{true /* allowSurfaceEncoders */};
OmxInfoBuilder sOmxNoSurfaceEncoderInfoBuilder{false /* allowSurfaceEncoders */};

Mutex sCodec2InfoBuilderMutex;
std::unique_ptr<MediaCodecListBuilderBase> sCodec2InfoBuilder;

MediaCodecListBuilderBase *GetCodec2InfoBuilder() {
    Mutex::Autolock _l(sCodec2InfoBuilderMutex);
    if (!sCodec2InfoBuilder) {
        sCodec2InfoBuilder.reset(
                StagefrightPluginLoader::GetCCodecInstance()->createBuilder());
    }
    return sCodec2InfoBuilder.get();
}

std::vector<MediaCodecListBuilderBase *> GetBuilders() {
    std::vector<MediaCodecListBuilderBase *> builders;
    // if plugin provides the input surface, we cannot use OMX video encoders.
    // In this case, rely on plugin to provide list of OMX codecs that are usable.
    sp<PersistentSurface> surfaceTest =
        StagefrightPluginLoader::GetCCodecInstance()->createInputSurface();
    if (surfaceTest == nullptr) {
        ALOGD("Allowing all OMX codecs");
        builders.push_back(&sOmxInfoBuilder);
    } else {
        ALOGD("Allowing only non-surface-encoder OMX codecs");
        builders.push_back(&sOmxNoSurfaceEncoderInfoBuilder);
    }
    builders.push_back(GetCodec2InfoBuilder());
    return builders;
}

Ici, le pointeur vers l'objet StagefrightPluginLoader est déclaré avec unique_ptr, de sorte que la variable de pointeur sInstance ne peut être « liée » qu'une seule fois, ce qui produit l'effet du mode singleton.

conseils

unique_ptr est un pointeur intelligent avec la propriété exclusive des ressources, c'est-à-dire qu'une ressource d'objet ne peut être pointée que par un unique_ptr à la fois.

frameworks/av/media/libstagefright/StagefrightPluginLoader.cpp

const std::unique_ptr<StagefrightPluginLoader> &StagefrightPluginLoader::GetCCodecInstance() {
    Mutex::Autolock _l(sMutex);
    if (!sInstance) {
        ALOGV("Loading library");
        sInstance.reset(new StagefrightPluginLoader(kCCodecPluginPath));
    }
    return sInstance;
}

  1. Appelez dlopen(…) pour ouvrir cette bibliothèque so selon le chemin libsfplugin_ccodec.so ;
  2. Appelez dlsym pour résoudre les symboles afin d'obtenir les adresses des trois méthodes CreateCodec, CreateBuilder et CreateInputSurface.

conseils

RTLD_NOW : avant le retour de dlopen, tous les symboles indéfinis doivent être analysés, sinon, dlopen renverra NULL, l'erreur est : symbole indéfini : xxxx...

RTLD_NODELETE : la bibliothèque n'est pas déchargée pendant dlclose() et les variables statiques de la bibliothèque ne sont pas initialisées lorsque la bibliothèque est rechargée ultérieurement à l'aide de dlopen(). Ce drapeau n'est pas conforme à POSIX-2001.

frameworks/av/media/libstagefright/StagefrightPluginLoader.cpp

namespace /* unnamed */ {

constexpr const char kCCodecPluginPath[] = "libsfplugin_ccodec.so";

}  // unnamed namespace

StagefrightPluginLoader::StagefrightPluginLoader(const char *libPath) {
    if (android::base::GetIntProperty("debug.stagefright.ccodec", 1) == 0) {
        ALOGD("CCodec is disabled.");
        return;
    }
    mLibHandle = dlopen(libPath, RTLD_NOW | RTLD_NODELETE);
    if (mLibHandle == nullptr) {
        ALOGD("Failed to load library: %s (%s)", libPath, dlerror());
        return;
    }
    mCreateCodec = (CodecBase::CreateCodecFunc)dlsym(mLibHandle, "CreateCodec");
    if (mCreateCodec == nullptr) {
        ALOGD("Failed to find symbol: CreateCodec (%s)", dlerror());
    }
    mCreateBuilder = (MediaCodecListBuilderBase::CreateBuilderFunc)dlsym(
            mLibHandle, "CreateBuilder");
    if (mCreateBuilder == nullptr) {
        ALOGD("Failed to find symbol: CreateBuilder (%s)", dlerror());
    }
    mCreateInputSurface = (CodecBase::CreateInputSurfaceFunc)dlsym(
            mLibHandle, "CreateInputSurface");
    if (mCreateInputSurface == nullptr) {
        ALOGD("Failed to find symbol: CreateInputSurface (%s)", dlerror());
    }
}

Il n'est pas difficile de découvrir à partir de l'analyse des symboles ci-dessus que StagefrightPluginLoader::createInputSurface() appellera éventuellement la méthode android::PersistentSurface *CreateInputSurface() sous codec2/sfplugin/CCodec.cpp.

frameworks/av/media/libstagefright/StagefrightPluginLoader.cpp

PersistentSurface *StagefrightPluginLoader::createInputSurface() {
    if (mLibHandle == nullptr || mCreateInputSurface == nullptr) {
        ALOGD("Handle or CreateInputSurface symbol is null");
        return nullptr;
    }
    return mCreateInputSurface();
}

Il n'est pas difficile de voir à partir du fichier bp que le fichier so libsfplugin_ccodec se trouve dans le répertoire frameworks/av/media/codec2/sfplugin/ Il est clair en un coup d'œil quels fichiers cpp ont été compilés, y compris les bibliothèques dépendantes.

frameworks/av/media/codec2/sfplugin/Android.bp

cc_library_shared {
    name: "libsfplugin_ccodec",

    srcs: [
        "C2OMXNode.cpp",
        "CCodec.cpp",
        "CCodecBufferChannel.cpp",
        "CCodecBuffers.cpp",
        "CCodecConfig.cpp",
        "Codec2Buffer.cpp",
        "Codec2InfoBuilder.cpp",
        "Omx2IGraphicBufferSource.cpp",
        "PipelineWatcher.cpp",
        "ReflectedParamUpdater.cpp",
        "SkipCutBuffer.cpp",
    ],

    cflags: [
        "-Werror",
        "-Wall",
    ],

    header_libs: [
        "libcodec2_internal",
    ],

    shared_libs: [
        "[email protected]",
        "[email protected]",
        "[email protected]",
        "[email protected]",
        "libbase",
        "libbinder",
        "libcodec2",
        "libcodec2_client",
        "libcodec2_vndk",
        "libcutils",
        "libgui",
        "libhidlallocatorutils",
        "libhidlbase",
        "liblog",
        "libmedia",
        "libmedia_omx",
        "libsfplugin_ccodec_utils",
        "libstagefright_bufferqueue_helper",
        "libstagefright_codecbase",
        "libstagefright_foundation",
        "libstagefright_omx",
        "libstagefright_omx_utils",
        "libstagefright_xmlparser",
        "libui",
        "libutils",
    ],

    sanitize: {
        cfi: true,
        misc_undefined: [
            "unsigned-integer-overflow",
            "signed-integer-overflow",
        ],
    },
}

Analysons maintenant l'implémentation spécifique de la méthode CreateInputSurface() dans sfplugin.

  1. Appelez Codec2Client::CreateInputSurface() pour obtenir un pointeur partagé vers Codec2Client::InputSurface ;
  2. Étant donné que la valeur de l'attribut debug.stagefright.c2inputsurface n'est pas définie sur la plate-forme Rockchip rk, la valeur par défaut est 0 et la fonction Codec2Client::CreateInputSurface() renvoie directement un pointeur nul ;
  3. Enfin, Android :: PersistentSurface *CreateInputSurface() renvoie également un pointeur nul.

frameworks/av/media/codec2/sfplugin/CCodec.cpp

extern "C" android::PersistentSurface *CreateInputSurface() {
    using namespace android;
    // Attempt to create a Codec2's input surface.
    std::shared_ptr<Codec2Client::InputSurface> inputSurface =
            Codec2Client::CreateInputSurface();
    if (!inputSurface) {
        if (property_get_int32("debug.stagefright.c2inputsurface", 0) == -1) {
            sp<IGraphicBufferProducer> gbp;
            sp<OmxGraphicBufferSource> gbs = new OmxGraphicBufferSource();
            status_t err = gbs->initCheck();
            if (err != OK) {
                ALOGE("Failed to create persistent input surface: error %d", err);
                return nullptr;
            }
            return new PersistentSurface(
                    gbs->getIGraphicBufferProducer(),
                    sp<IGraphicBufferSource>(
                        new Omx2IGraphicBufferSource(gbs)));
        } else {
            return nullptr;
        }
    }
    return new PersistentSurface(
            inputSurface->getGraphicBufferProducer(),
            static_cast<sp<android::hidl::base::V1_0::IBase>>(
            inputSurface->getHalInterface()));
}

La structure Codec2Client est définie dans codec2/hidl/client.h.

frameworks/av/media/codec2/hidl/client/include/codec2/hidl/client.h

struct Codec2Client : public Codec2ConfigurableClient {
    ......
    // Create an input surface.
    static std::shared_ptr<InputSurface> CreateInputSurface(
            char const* serviceName = nullptr);
    ......
}

La valeur de propriété de debug.stagefright.c2inputsurface sur la plate-forme Rockchip rk n'est pas définie, la valeur par défaut est donc 0. Ici renvoie nullptr directement.

frameworks/av/media/codec2/hidl/client/client.cpp

std::shared_ptr<Codec2Client::InputSurface> Codec2Client::CreateInputSurface(
        char const* serviceName) {
    int32_t inputSurfaceSetting = ::android::base::GetIntProperty(
            "debug.stagefright.c2inputsurface", int32_t(0));
    if (inputSurfaceSetting <= 0) {
        return nullptr;
    }
    ......
}

Revenez maintenant à la fonction MediaCodecList.cpp GetBuilders(), qui est déjà connue pour ajouter sOmxInfoBuilder au conteneur. sOmxInfoBuilder est une instance de la classe OmxInfoBuilder et l'encodeur de surface est autorisé par défaut. En plus d'ajouter sOmxInfoBuilder, il continuera à ajouter le générateur renvoyé par la fonction GetCodec2InfoBuilder(). Cette fonction appelle en interne la fonction CreateBuilder() dans sfplugin.

Vous pouvez voir que l'objet Codec2InfoBuilder est directement renvoyé ici.

frameworks/av/media/codec2/sfplugin/Codec2InfoBuilder.cpp

extern "C" android::MediaCodecListBuilderBase *CreateBuilder() {
    return new android::Codec2InfoBuilder;
}

Le constructeur de la classe Codec2InfoBuilder ne fait rien.

frameworks/av/media/codec2/sfplugin/include/media/stagefright/Codec2InfoBuilder.h

namespace android {

class Codec2InfoBuilder : public MediaCodecListBuilderBase {
public:
    Codec2InfoBuilder() = default;
    ~Codec2InfoBuilder() override = default;
    status_t buildMediaCodecList(MediaCodecListWriter* writer) override;
};

}  // namespace android

Dans le constructeur MediaCodecList, selon l'analyse ci-dessus, il n'est pas difficile de conclure que le constructeur appelle son buildMediaCodecList(…), l'un est OmxInfoBuilder, et l'autre est Codec2InfoBuilder. Quant à la façon de construire la liste MediaCodec, nous l'analyserons dans la section suivante. De retour à la ligne principale, la fonction MediaCodecList::findMatchingCodecs(…) dans MediaCodecList.cpp appelle en interne la méthode findCodecByType(…) de BpMediaCodecList, qui est en fait traitée par MediaCodecList::findCodecByType(…) pour renvoyer l'index correspondant.

  1. Parcourir tous les MediaCodecInfo dans le conteneur ;
  2. S'il s'avère que le retour de MediaCodecInfo isEncoder() n'est pas faux, ignorez cet élément, indiquant qu'il ne s'agit pas d'un décodeur ;
  3. S'il s'avère que MediaCodecInfo getCapabilitiesFor(…) renvoie nullptr, cet élément est également ignoré, indiquant que cet élément n'a pas de capacité de décodage correspondante ;
  4. Vérifiez si les fonctionnalités avancées prennent en charge les décodeurs chiffrés et les décodeurs de lecture tunnel, et renvoyez l'index correspondant si ces deux fonctionnalités ne sont pas prises en charge.

frameworks/av/media/libstagefright/MediaCodecList.cpp

ssize_t MediaCodecList::findCodecByType(
        const char *type, bool encoder, size_t startIndex) const {
    static const char *advancedFeatures[] = {
        "feature-secure-playback",
        "feature-tunneled-playback",
    };

    size_t numCodecInfos = mCodecInfos.size();
    for (; startIndex < numCodecInfos; ++startIndex) {
        const MediaCodecInfo &info = *mCodecInfos[startIndex];

        if (info.isEncoder() != encoder) {
            continue;
        }
        sp<MediaCodecInfo::Capabilities> capabilities = info.getCapabilitiesFor(type);
        if (capabilities == nullptr) {
            continue;
        }
        const sp<AMessage> &details = capabilities->getDetails();

        int32_t required;
        bool isAdvanced = false;
        for (size_t ix = 0; ix < ARRAY_SIZE(advancedFeatures); ix++) {
            if (details->findInt32(advancedFeatures[ix], &required) &&
                    required != 0) {
                isAdvanced = true;
                break;
            }
        }

        if (!isAdvanced) {
            return startIndex;
        }
    }

    return -ENOENT;
}

Regardons la fonction MediaCodecList::findMatchingCodecs(…) dans MediaCodecList.cpp appelle en interne la méthode getCodecInfo(…) de BpMediaCodecList. En fait, MediaCodecList::getCodecInfo(…) renvoie le MediaCodecInfo, qui consiste à renvoyer directement l'élément correspondant dans le conteneur (std::vector<sp>) pointé par mCodecInfos en fonction de l'index.

frameworks/av/media/libstagefright/include/media/stagefright/MediaCodecList.h

struct MediaCodecList : public BnMediaCodecList {
    ......
    virtual sp<MediaCodecInfo> getCodecInfo(size_t index) const {
        if (index >= mCodecInfos.size()) {
            ALOGE("b/24445127");
            return NULL;
        }
        return mCodecInfos[index];
    }
    ......
}

Nous pouvons enfin analyser le constructeur MediaCodec cpp et son processus d'initialisation. Divers champs sont initialisés dans le constructeur.

frameworks/av/media/libstagefright/MediaCodec.cpp

MediaCodec::MediaCodec(const sp<ALooper> &looper, pid_t pid, uid_t uid)
    : mState(UNINITIALIZED),
      mReleasedByResourceManager(false),
      mLooper(looper),
      mCodec(NULL),
      mReplyID(0),
      mFlags(0),
      mStickyError(OK),
      mSoftRenderer(NULL),
      mAnalyticsItem(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),
      mCpuBoostRequested(false),
      mLatencyUnknown(0) {
    if (uid == kNoUid) {
        mUid = IPCThreadState::self()->getCallingUid();
    } else {
        mUid = uid;
    }

    initAnalyticsItem();
}

  1. Si le nom contient la chaîne .secure, définissez secureCodec sur true ;
  2. Obtenez le MediaCodecInfo correspondant en fonction du nom et vérifiez si le décodage vidéo est pris en charge ;
  3. Appelez GetCodecBase(…) pour créer une sous-classe héritée de CodecBase afin d'obtenir le décodeur ;
  4. Si le codec vidéo crée une boucle dédiée, le gestionnaire enregistré correspondant est la sous-classe CodecBase créée à l'étape précédente ;
  5. Le gestionnaire correspondant à l'enregistrement de mLooper est l'objet MediaCodec lui-même ;
  6. Définir le rappel de la sous-classe CodecBase, définir le rappel de la sous-classe BufferChannelBase ;
  7. Appelez ResourceManagerService reclaimResource(…) pour récupérer des ressources.

frameworks/av/media/libstagefright/MediaCodec.cpp

status_t MediaCodec::init(const AString &name) {
    mResourceManagerService->init();

    // save init parameters for reset
    mInitName = name;

    // Current video decoders do not return from OMX_FillThisBuffer
    // quickly, violating the OpenMAX specs, until that is remedied
    // we need to invest in an extra looper to free the main event
    // queue.

    mCodecInfo.clear();

    bool secureCodec = false;
    AString tmp = name;
    if (tmp.endsWith(".secure")) {
        secureCodec = true;
        tmp.erase(tmp.size() - 7, 7);
    }
    const sp<IMediaCodecList> mcl = MediaCodecList::getInstance();
    if (mcl == NULL) {
        mCodec = NULL;  // remove the codec.
        return NO_INIT; // if called from Java should raise IOException
    }
    for (const AString &codecName : { name, tmp }) {
        ssize_t codecIdx = mcl->findCodecByName(codecName.c_str());
        if (codecIdx < 0) {
            continue;
        }
        mCodecInfo = mcl->getCodecInfo(codecIdx);
        Vector<AString> mediaTypes;
        mCodecInfo->getSupportedMediaTypes(&mediaTypes);
        for (size_t i = 0; i < mediaTypes.size(); i++) {
            if (mediaTypes[i].startsWith("video/")) {
                mIsVideo = true;
                break;
            }
        }
        break;
    }
    if (mCodecInfo == nullptr) {
        return NAME_NOT_FOUND;
    }

    mCodec = GetCodecBase(name, mCodecInfo->getOwnerName());
    if (mCodec == NULL) {
        return NAME_NOT_FOUND;
    }

    if (mIsVideo) {
        // video codec needs dedicated looper
        if (mCodecLooper == NULL) {
            mCodecLooper = new ALooper;
            mCodecLooper->setName("CodecLooper");
            mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
        }

        mCodecLooper->registerHandler(mCodec);
    } else {
        mLooper->registerHandler(mCodec);
    }

    mLooper->registerHandler(this);

    mCodec->setCallback(
            std::unique_ptr<CodecBase::CodecCallback>(
                    new CodecCallback(new AMessage(kWhatCodecNotify, this))));
    mBufferChannel = mCodec->getBufferChannel();
    mBufferChannel->setCallback(
            std::unique_ptr<CodecBase::BufferCallback>(
                    new BufferCallback(new AMessage(kWhatCodecNotify, this))));

    sp<AMessage> msg = new AMessage(kWhatInit, this);
    msg->setObject("codecInfo", mCodecInfo);
    // name may be different from mCodecInfo->getCodecName() if we stripped
    // ".secure"
    msg->setString("name", name);

    if (mAnalyticsItem != NULL) {
        mAnalyticsItem->setCString(kCodecCodec, name.c_str());
        mAnalyticsItem->setCString(kCodecMode, mIsVideo ? kCodecModeVideo : kCodecModeAudio);
    }

    status_t err;
    Vector<MediaResource> resources;
    MediaResource::Type type =
            secureCodec ? MediaResource::kSecureCodec : MediaResource::kNonSecureCodec;
    MediaResource::SubType subtype =
            mIsVideo ? MediaResource::kVideoCodec : MediaResource::kAudioCodec;
    resources.push_back(MediaResource(type, subtype, 1));
    for (int i = 0; i <= kMaxRetry; ++i) {
        if (i > 0) {
            // Don't try to reclaim resource for the first time.
            if (!mResourceManagerService->reclaimResource(resources)) {
                break;
            }
        }

        sp<AMessage> response;
        err = PostAndAwaitResponse(msg, &response);
        if (!isResourceError(err)) {
            break;
        }
    }
    return err;
}

Dans la méthode MediaCodec::GetCodecBase(…), différents objets ou structures ACodec, android::CCodec ou MediaFilter sont créés selon différents propriétaires et noms.

Après avoir ajouté le journal, l'impression du journal sur la plate-forme rk3399 est la suivante :

MediaCodec : GetCodecBase name=OMX.rk.video_decoder.avc owner=default

Ainsi, la structure ACodec est réellement créée.

frameworks/av/media/libstagefright/MediaCodec.cpp

static CodecBase *CreateCCodec() {
    return StagefrightPluginLoader::GetCCodecInstance()->createCodec();
}

//static
sp<CodecBase> MediaCodec::GetCodecBase(const AString &name, const char *owner) {
    if (owner) {
        if (strcmp(owner, "default") == 0) {
            return new ACodec;
        } else if (strncmp(owner, "codec2", 6) == 0) {
            return CreateCCodec();
        }
    }

    if (name.startsWithIgnoreCase("c2.")) {
        return CreateCCodec();
    } else if (name.startsWithIgnoreCase("omx.")) {
        // at this time only ACodec specifies a mime type.
        return new ACodec;
    } else if (name.startsWithIgnoreCase("android.filter.")) {
        return new MediaFilter;
    } else {
        return NULL;
    }
}

Cette initialisation attribue des valeurs initiales à une série de champs, initialise neuf états et change l'état en non initialisé dans le constructeur.

frameworks/av/media/libstagefright/ACodec.cpp

ACodec::ACodec()
    : mSampleRate(0),
      mNodeGeneration(0),
      mUsingNativeWindow(false),
      mNativeWindowUsageBits(0),
      mLastNativeWindowDataSpace(HAL_DATASPACE_UNKNOWN),
      mIsVideo(false),
      mIsImage(false),
      mIsEncoder(false),
      mFatalError(false),
      mShutdownInProgress(false),
      mExplicitShutdown(false),
      mIsLegacyVP9Decoder(false),
      mEncoderDelay(0),
      mEncoderPadding(0),
      mRotationDegrees(0),
      mChannelMaskPresent(false),
      mChannelMask(0),
      mDequeueCounter(0),
      mMetadataBuffersToSubmit(0),
      mNumUndequeuedBuffers(0),
      mRepeatFrameDelayUs(-1LL),
      mMaxPtsGapUs(0LL),
      mMaxFps(-1),
      mFps(-1.0),
      mCaptureFps(-1.0),
      mCreateInputBuffersSuspended(false),
      mTunneled(false),
      mDescribeColorAspectsIndex((OMX_INDEXTYPE)0),
      mDescribeHDRStaticInfoIndex((OMX_INDEXTYPE)0),
      mDescribeHDR10PlusInfoIndex((OMX_INDEXTYPE)0),
      mStateGeneration(0),
      mVendorExtensionsStatus(kExtensionsUnchecked) {
    memset(&mLastHDRStaticInfo, 0, sizeof(mLastHDRStaticInfo));

    mUninitializedState = new UninitializedState(this);
    mLoadedState = new LoadedState(this);
    mLoadedToIdleState = new LoadedToIdleState(this);
    mIdleToExecutingState = new IdleToExecutingState(this);
    mExecutingState = new ExecutingState(this);

    mOutputPortSettingsChangedState =
        new OutputPortSettingsChangedState(this);

    mExecutingToIdleState = new ExecutingToIdleState(this);
    mIdleToLoadedState = new IdleToLoadedState(this);
    mFlushingState = new FlushingState(this);

    mPortEOS[kPortIndexInput] = mPortEOS[kPortIndexOutput] = false;
    mInputEOSResult = OK;

    mPortMode[kPortIndexInput] = IOMX::kPortModePresetByteBuffer;
    mPortMode[kPortIndexOutput] = IOMX::kPortModePresetByteBuffer;

    memset(&mLastNativeWindowCrop, 0, sizeof(mLastNativeWindowCrop));

    changeState(mUninitializedState);
}


---------------------
Auteur : TYYJ-Hong Wei
Source : CSDN
Original : https://blog.csdn.net/tyyj90/article/details/120893513
Avis de droit d'auteur : Cet article est l'article original de l'auteur, veuillez joindre le lien du blog pour réimpression !
Analyse de contenu Par : CSDN, blog CNBLOG, plug-in de réimpression en un clic

Je suppose que tu aimes

Origine blog.csdn.net/xiaowang_lj/article/details/131592296
conseillé
Classement