Le principe de base du système et l'utilisation du multimédia MediaCodec

œuvres MediaCodec

classe Android MediaCodec offre un accès à faible niveau de l'interface encodeur / décodeur multimédia, qui fait partie de l'architecture multimédia Android à faible niveau, il est souvent utilisé avec MediaExtractor, MediaMuxer, AudioTrack, comme un codec peut être H.264, H.265, AAC, 3gp et d'autres formats audio et vidéo courants. D'une manière générale, le principe de fonctionnement MediaCodec traite les données d'entrée pour produire des données de sortie. Plus précisément, MediaCodec utilise un ensemble de tampon d'entrée / sortie à un traitement synchrone ou asynchrone de données pendant le codage et le décodage: Tout d'abord, les données du client sont écrites dans la mémoire tampon d'entrée et le codec codec acquis soumis au codec, le codec sera traitée déplacer vers la sortie de la mémoire tampon de codeur, tandis que la propriété du client de récupération de la mémoire tampon d'entrée, puis, le client acquis à partir de la sortie codée à la mémoire tampon de lecture de bonnes données sont traitées sur un code, à traiter pour récupérer la propriété du client codec de la mémoire tampon de sortie. Le processus est répété jusqu'à ce que le codeur cesse de fonctionner ou arrêter de façon inattendue.

On peut le voir sur la figure, l'effet mediacodec générer des données de sortie est le traitement d'entrée de données. Génération d'un premier tampon de données d'entrée, les données dans le tampon fourni au codec, codec seront utilisées pour les données de processus de façon asynchrone ces entrées, puis le tampon de sortie est rempli au consommateur, après que le tampon de dépenses de consommation retour au codec.

Procédé de codage MediaCodec

Tout au long du processus de codage et de décodage, MediaCodec utiliser l'expérience configurer, démarrer, le traitement des données est arrêté, libérant plusieurs processus, l'état correspondant peut être résumée comme arrêté (l'arrêt), l'exécution (exécution) et la libération (libéré) trois états, état d'arrêt et peut être subdivisé en initialisés (non initialisé), la configuration (configuration), anormal (erreur), l'état d'exécution peut être divisé en des données d'écriture (Flushed), en cours d'exécution (exécution) et la fin du courant (hors d'usage flux). toute MediaCodec état de la structure est la suivante:

 

 

Après vu de la figure, est créé lorsque MediaCodec entrera dans l'état non initialisée, et des informations de configuration à mettre en place des appels de départ () démarre, MediaCodec entrera dans l'état en cours d'exécution, et peut lire et écrire des données. Si une erreur se produit dans ce processus, MediaCodec va entrer dans l'état Stopped, nous devons utiliser la méthode reset pour réinitialiser le codec ou MediaCodec tenu des ressources finiront par être libérés. Bien sûr, si MediaCodec utilisation normale, nous pouvons également envoyer des instructions au codec EOS, alors que la méthode d'arrêt et de libération appel aboutissant à l'aide de codecs.

(1) créer encodeur / décodeur
MediaCodec fournit principalement createEncoderByType (type String), deux méthodes createDecoderByType (type String) pour créer un codec, ils ont tous besoin de passer un type MIME de formats multimédia. Type MIME commun format multimédia est la suivante:
● "Vidéo / X - vnd.on2.vp8" - VP8 Video (Vidéo dans IE .webm)
● "Vidéo / X - vnd.on2.vp9" - VP9 vidéo (vidéo dans Internet Explorer. WebM)
● "Vidéo / AVC" - H.264 / AVC vidéo
● "vidéo / MP4V-es" - MPEG4 vidéo
● "vidéo / 3GPP" - H.263 vidéo
● "Audio / 3GPP" - AMR Audio bande étroite
● « Audio / AMR-WB "- l'AMR Wideband Audio
●" Audio / MPEG "- MPEG1 / 2 Audio Layer III
●" Audio / MP4A-1 atm "- AAC Audio (! Remarque, la présente IS paquets AAC RAW, non emballé dans LATM)
●" audio / Vorbis "- audio Vorbis
●" audio / G711-aLaw "- audio aLaw G.711
●" audio / G711-MLaw « - le G.711 audio uLaw
Bien sûr, MediaCodec fournit également une méthode createByCodecName (String name), soutenir l'utilisation de composants de nom spécifique pour créer un codec. Cependant, cette méthode utilise un peu de peine, et le fonctionnaire est la meilleure façon est avec MediaCodecList utilisé parce que MediaCodecList enregistre tous les codecs disponibles. Bien sûr, nous pouvons également utiliser ce type de paramètres passés juge minmeType, pour correspondre à la MediaCodec que le soutien pour le type mineType de codec. Pour spécifier à titre d'exemple un type MIME « vidéo / avc », comme suit:

 

 private static MediaCodecInfo selectCodec(String mimeType) {
     // 获取所有支持编解码器数量
     int numCodecs = MediaCodecList.getCodecCount();
     for (int i = 0; i < numCodecs; i++) {
        // 编解码器相关性信息存储在MediaCodecInfo中
         MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
         // 判断是否为编码器
         if (!codecInfo.isEncoder()) {
             continue;
         }
        // 获取编码器支持的MIME类型,并进行匹配
         String[] types = codecInfo.getSupportedTypes();
         for (int j = 0; j < types.length; j++) {
             if (types[j].equalsIgnoreCase(mimeType)) {
                 return codecInfo;
             }
         }
     }
     return null;
 }

(2) configure, codeur / décodeur démarre
configuration codec est utilisé pour configurer la méthode MediaCodec les données sont d'abord stockées dans la carte MediaFormat extraite, et appel de méthode locale pour le travail de configuration native_configure codec. Lors de la configuration, les méthodes de configure doivent passer le format, la surface, Crypto, paramètre flags, dans lequel le format est par exemple MediaFormat, qui utilise les informations de format de clé « valeur clé » stockée sous forme de données multimédia; Surface pour indiquer le décodeur de la source de données à la surface, un objet précise crypto MediaCrypto pour décrypter la sécurité des données des médias, les drapeaux indique que la configuration du codeur (CONFIGURE_FLAG_ENCODE).

 

MediaFormat mFormat = MediaFormat.createVideoFormat("video/avc", 640 ,480);     // 创建MediaFormat
mFormat.setInteger(MediaFormat.KEY_BIT_RATE,600);       // 指定比特率
mFormat.setInteger(MediaFormat.KEY_FRAME_RATE,30);  // 指定帧率
mFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,mColorFormat);  // 指定编码器颜色格式  
mFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL,10); // 指定关键帧时间间隔
mVideoEncodec.configure(mFormat,null,null,MediaCodec.CONFIGURE_FLAG_ENCODE); 

Le code ci - dessus est agencé au niveau du procédé de codage H.264, createVideoFormat ( "video / avc" , 640, 480) en tant que type "vidéo / avc" ( à savoir H.264) du codeur objet MediaFormat, il est nécessaire de préciser la largeur des données vidéo élevé, si le codage et le décodage de données audio, le MediaFormat d'appel createAudioFormat (mime String, int sampleRate, int channelCount) méthode. En outre une configuration vidéo comme une fréquence d'image vidéo, la fréquence d'échantillonnage audio et d' autres paramètres, doit être souligné expliquer l' attribut de configuration MediaFormat.KEY_COLOR_FORMAT, qui indique le format de couleur pour l'encodeur vidéo, le format de couleur d'entrée et sélectionné la couleur spécifique de source de données sur le format. Par exemple, nous savons tous que l'acquisition du flux d'images de prévisualisation de la caméra est généralement NV21 ou YV12, le besoin de l' encodeur pour spécifier le format de couleur appropriée ou données codées obtenues peuvent apparaître Huaping, fantôme, distorsion des couleurs et ainsi de suite. . Encodeurs MediaCodecInfo.CodecCapabilities magasins prennent en charge tous les formats de couleur, format commun couleur mappage est la suivante:
l'encodeur de données brutes
NV12 (yuv420sp) ---> COLOR_FormatYUV420PackedSemiPlanar
NV21 ----> COLOR_FormatYUV420SemiPlanar
YV12 (I420) ----> COLOR_FormatYUV420Planar
lorsque la configuration codec est terminée, vous pouvez appeler la méthode, qui appelle la méthode native_start bas niveau () pour démarrer le codeur, et appelle la méthode à faible niveau ByteBuffer [] getBuffers (entrée) pour ouvrir une série d'entrée, sortie début MediaCodec () tampon. commencer méthode code source () comme suit:

 

public final void start() {
        native_start();
        synchronized(mBufferLock) {
            cacheBuffers(true /* input */);
            cacheBuffers(false /* input */);
        }
 }

(3) Traitement de données
codec MediaCodec supporte deux modes, à savoir synchrone synchrone, asynchrone Asynchronous, un soi-disant moyens de mode synchrone les données codec entrée et de sortie est synchronisé, la sortie du codec traitement est terminé sera entré de nouveau reçu transactions, entrée asynchrone et les codecs de sortie sont des données asynchrones, le codec n'attend pas avant de recevoir des données de sortie des données d'entrée sont traitées à nouveau. Ici, nous présentons le prochain grand codec de synchronisation, car de cette façon , nous sommes plus que. Nous savons que lorsque le codec est démarré, chaque codec aura un ensemble d'entrée et de tampon de sortie, mais le tampon ne peut pas être utilisé temporairement, pour obtenir l' autorisation d'entrée MediaCodec et le tampon de sortie de la méthode dequeueInputBuffer / dequeueOutputBuffer, ID retourné pour faire fonctionner ces tampons. Nous avons adopté la pièce suivante du code fourni par l'analyse officielle, étendue:

 

 MediaCodec codec = MediaCodec.createByCodecName(name);
 codec.configure(format, …);
 MediaFormat outputFormat = codec.getOutputFormat(); // option B
 codec.start();
 for (;;) {
   int inputBufferId = codec.dequeueInputBuffer(timeoutUs);
   if (inputBufferId >= 0) {
     ByteBuffer inputBuffer = codec.getInputBuffer(…);
     // fill inputBuffer with valid data
     …
     codec.queueInputBuffer(inputBufferId, …);
   }
   int outputBufferId = codec.dequeueOutputBuffer(…);
   if (outputBufferId >= 0) {
     ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
     MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A
     // bufferFormat is identical to outputFormat
     // outputBuffer is ready to be processed or rendered.
     …
     codec.releaseOutputBuffer(outputBufferId, …);
   } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
     // Subsequent data will conform to new format.
     // Can ignore if using getOutputFormat(outputBufferId)
     outputFormat = codec.getOutputFormat(); // option B
   }
 }
 codec.stop();
 codec.release();

Dans le code ci-dessus, lorsque le début codec, va entrer dans une boucle for (;;), qui est une boucle infinie pour mettre en oeuvre un tampon continu est d'acquérir des données à partir du tampon d'entrée contient le pool codec , alors bon codec sortie de données de sortie du pool de mémoire tampon acquis.

● Obtenir tampon d'entrée codec, les données sont écrites en
premier lieu , la méthode d' appel MediaCodec dequeueInputBuffer ( à long timeoutUs) prend un tampon d'entrée de l'entrée de jeu de la mémoire tampon du codeur et retourne l'index de l'index de cache, et si index = -1 instructions disponibles du cache temporaire, lorsque timeoutUs = 0 quand dequeueInputBuffer retournera immédiatement. L'appel MediaCodec getInputBuffer (int index), qui sera transmis à la méthode d'index local GetBuffer (true / * entrée * / , index) Retourne le tampon ByteBuffer et stocké objet ByteBuffer et son index au BufferMap obtenu objet pour le processus de libération pour la fin de la mémoire tampon d'entrée, vers le codec. source comme suit getInputBuffer (int index):

 

    @Nullable
    public ByteBuffer getInputBuffer(int index) {
        ByteBuffer newBuffer = getBuffer(true /* input */, index);
        synchronized(mBufferLock) {
            invalidateByteBuffer(mCachedInputBuffers, index);
     // mDequeuedInputBuffers是BufferMap的实例
            mDequeuedInputBuffers.put(index, newBuffer);
        }
        return newBuffer;
    }

Puis, après avoir obtenu le tampon d'entrée, les données dans les données et l'utilise pour soumettre le traitement queueInputBuffer codec tandis que le tampon d'entrée est relâché au codec. queueInputBuffer code source comme suit:

 

    public final void queueInputBuffer(
            int index,
            int offset, int size, long presentationTimeUs, int flags)
        throws CryptoException {
        synchronized(mBufferLock) {
            invalidateByteBuffer(mCachedInputBuffers, index);
             // 移除输入缓存区
            mDequeuedInputBuffers.remove(index);
        }
        try {
            native_queueInputBuffer(
                    index, offset, size, presentationTimeUs, flags);
        } catch (CryptoException | IllegalStateException e) {
            revalidateByteBuffer(mCachedInputBuffers, index);
            throw e;
        }
    }

A partir des codes ci-dessus, queueInputBuffer native_queueInputBuffer niveau bas principalement en appelant la méthode, qui doivent passer cinq paramètres, où l'indice est le tampon d'entrée d'index du codec est par l'index pour trouver l'emplacement du tampon; décalage est des données valides le tampon de décalage; taille est la taille des données d'entrée d'origine est valide; estampille temporelle de présentation presentationTimeUs du tampon, en général 0; drapeaux de drapeau de la mémoire tampon d'entrée est généralement fixé à BUFFER_FLAG_END_OF_STREAM.

● codec acquis tampon de sortie, les données sont lues en
premier, et le tampon similaire, MediaCodec dequeueOutputBuffer par acquisition d' une entrée décrit ci-dessus dequeueInputBuffer getInputBuffer fournit également un procédé et nous aider à obtenir un tampon de sortie codec getOutputBuffer. Mais la différence est que , avec dequeueInputBuffer, dequeueOutputBuffer doivent également passer un objet MediaCodec.BufferInfo. MediaCodec.BufferInfo MediaCodec est une classe interne, qui enregistre le bon codec de données de décalage et la taille de la zone tampon de sortie.

 

  public final static class BufferInfo {
        public void set(
                int newOffset, int newSize, long newTimeUs, @BufferFlag int newFlags) {
            offset = newOffset;
            size = newSize;
            presentationTimeUs = newTimeUs;
            flags = newFlags;
        }
        public int offset // 偏移量
        public int size;    // 缓存区有效数据大小
        public long presentationTimeUs; // 显示时间戳
        public int flags;                   // 缓存区标志

        @NonNull
        public BufferInfo dup() {
            BufferInfo copy = new BufferInfo();
            copy.set(offset, size, presentationTimeUs, flags);
            return copy;
        }
    };

Ensuite, la source dequeueOutputBuffer entendu, lorsque la valeur de retour dequeueOutputBuffer> = 0, la sortie de données du tampon sont valides. Lorsqu'un appel de méthode locale retourne native_dequeueOutputBuffer INFO_OUTPUT_BUFFERS_CHANGED, les appels de méthode de cacheBuffers récupérer un ensemble de mémoire tampon de sortie mCachedOutputBuffers (ByteBuffer []). Cela explique que, si nous utilisons la méthode getOutputBuffers (API21 jetés après usage getOutputBuffer (indice) en place) pour obtenir le tampon de sortie codec, il doit appeler la valeur de retour est déterminée dequeueOutputBuffer, si la valeur de retour MediaCodec. INFO_OUTPUT_BUFFERS_CHANGED, vous aurez besoin de réacquérir tampon de sortie collective. En outre, ici aussi deux valeurs de retour supplémentaires dequeueOutputBuffer: MediaCodec.INFO_TRY_AGAIN_LATER, MediaCodec.INFO_OUTPUT_FORMAT_CHANGED, l'ancien délai d'attente récupère tampon de sortie codec, ce qui représente les données codec modifier le format de sortie, puis utilisez la nouvelle sortie de données format. Par conséquent, nous devons appeler dequeueOutputBuffer déterminer si la valeur de retour est INFO_OUTPUT_FORMAT_CHANGED, le besoin de définir la cible re-MediaFormt par MediaCodec de getOutputFormat.

 

  public final int dequeueOutputBuffer(
            @NonNull BufferInfo info, long timeoutUs) {
        int res = native_dequeueOutputBuffer(info, timeoutUs);
        synchronized(mBufferLock) {
            if (res == INFO_OUTPUT_BUFFERS_CHANGED) {
            // 将会调用getBuffers()底层方法
                cacheBuffers(false /* input */);
            } else if (res >= 0) {
                validateOutputByteBuffer(mCachedOutputBuffers, res, info);
                if (mHasSurface) {
                    mDequeuedOutputInfos.put(res, info.dup());
                }
            }
        }
        return res;
    }

Enfin, la mémoire tampon de sortie lorsque les données sont traitées, est libérée par l'appelant tampon de sortie MediaCodec releaseOutputBuffer et retourné au codec, le tampon de sortie ne sera pas utilisé jusqu'à ce que le prochain disponible par dequeueOutputBuffer. Procédé releaseOutputBuffer reçoit deux paramètres: Index, render, dans lequel, l'indice est la sortie du tampon index; représente la rendre surface est spécifiée lorsque le codeur est configuré, il doit être réglé sur vrai, la mémoire tampon de sortie de données est transférée à la surface. Source comme suit:

 

   public final void releaseOutputBuffer(int index, boolean render) {
        BufferInfo info = null;
        synchronized(mBufferLock) {
            invalidateByteBuffer(mCachedOutputBuffers, index);
            mDequeuedOutputBuffers.remove(index);
            if (mHasSurface) {
                info = mDequeuedOutputInfos.remove(index);
            }
        }
        releaseOutputBuffer(index, render, false /* updatePTS */, 0 /* dummy */);
    }

 

Publié 351 articles originaux · louange gagné 52 · vues 190 000 +

Je suppose que tu aimes

Origine blog.csdn.net/u014644594/article/details/104413563
conseillé
Classement