El principio básico del sistema y el uso de multimedia MediaCodec

obras MediaCodec

clase Android MediaCodec proporciona acceso de bajo nivel para multimedia codificador / decodificador de la interfaz, que es parte de la de bajo nivel de arquitectura multimedia Android, se utiliza a menudo con MediaExtractor, MediaMuxer, AudioTrack, tal como un codec puede ser H.264, H.265, AAC, 3GP y otros formatos de audio y video comunes. En términos generales, el principio de funcionamiento MediaCodec está procesando los datos de entrada a los datos de salida de productos. Específicamente, MediaCodec utiliza un conjunto de memoria intermedia de entrada / salida para el procesamiento síncrono o asíncrono de datos durante la codificación y la decodificación: En primer lugar, los datos del cliente se escribe en el buffer de entrada codec y el códec adquirió presentado al codec, el codec será procesada moverlo a la salida de la memoria tampón del codificador, mientras que la propiedad del cliente de recuperación de la memoria intermedia de entrada y, a continuación, el cliente adquirida desde la salida codec para el búfer de lectura buena datos se procesan en un código, que será procesado para recuperar la propiedad del cliente códec del búfer de salida. Todo el proceso se repite hasta que el codificador deja de funcionar o cerrarse de forma inesperada.

Puede verse en la figura, el efecto mediacodec generar datos de salida es el procesamiento de la entrada de datos. Generación de una primera memoria intermedia de datos de entrada, los datos en el tampón proporcionado a codec, el codec serán utilizados para los datos de proceso de forma asíncrona estas entradas, entonces la memoria intermedia de salida se llena hasta el consumidor, después de la memoria intermedia de gasto de los consumidores devuelto al codec.

MediaCodec proceso de codificación

A lo largo del proceso de codificación y decodificación, MediaCodec uso experiencia configurar, iniciar, se detiene el procesamiento de datos, la liberación de varios procesos, el estado correspondiente se puede resumir como parado (el detenido), ejecución (Ejecución) y la liberación (liberado) tres estados, estado y se detuvo se puede subdividir en no inicializado (sin inicializar), configuración (configurada), anormal (Error), Estado de ejecución puede dividirse en datos de escritura (enrojecida), en funcionamiento (marcha) y el final de la corriente (fin-de- stream). toda MediaCodec estado de la estructura es la siguiente:

 

 

Después de verse en la figura, se crea cuando MediaCodec entrará en el estado no inicializado, y la información de configuración que se establezca llamadas start () se inicia, MediaCodec entrará en el estado de ejecución, y puede leer y escribir datos. Si se produce un error en este proceso, MediaCodec entrará en el estado de parada, vamos a utilizar el método de reinicio para reiniciar el códec, o MediaCodec llevó a cabo con el tiempo se liberan los recursos. Por supuesto, si el uso normal MediaCodec, podemos también enviar instrucciones para el códec EOS, mientras que la parada de llamada de destino y el método de liberación utilizando códecs.

(1) crear codificador / decodificador
MediaCodec principalmente proporciona createEncoderByType (tipo String), dos métodos createDecoderByType (tipo String) para crear un codec, todos ellos necesidad de pasar un tipo MIME de formatos multimedia. tipo común MIME del formato multimedia es la siguiente:
● "Video / X - vnd.on2.vp8" - VP8 vídeo (Vídeo en IE .webm)
● "Video / X - vnd.on2.vp9" - VP9 Vídeo (Video en IE. WebM)
● "video / AVC" - H.264 / AVC video
● "video / MP4V-ES" - vídeo MPEG4
● "video / 3GPP" - vídeo H.263
● "Audio / 3GPP" - AMR de banda estrecha de audio
● "Audio / AMR-WB "- el AMR audio de banda ancha
●" audio / mpeg "- el MPEG1 / 2 Audio Layer III
●" Audio / MP4A-1 atm "- Audio AAC (! Nota, la presente ES paquetes AAC RAW, que no estén envasados en LATM)
●" audio / Vorbis "- de audio Vorbis
●" audio / G711-aLaw "- audio aLaw G.711
●" audio / G711-mlaw "- el audio G.711 uLaw
Por supuesto, MediaCodec también proporciona un método createByCodecName (String name), apoyar el uso de componentes de nombre específico para crear un codec. Sin embargo, este método utiliza un poco de problemas, y el oficial es la mejor forma de hacerlo es con MediaCodecList utiliza porque MediaCodecList registra todos los codecs disponibles. Por supuesto, también podemos utilizar este tipo de parámetros pasados ​​juez minmeType, para que coincida con el MediaCodec si el apoyo para el tipo de códec mineType. Para especificar un tipo MIME "video / AVC" como un ejemplo, de la siguiente manera:

 

 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) configurar, iniciar codificador / decodificador
codec de configuración se utiliza para configurar método MediaCodec los datos se almacenan primero en el mapa MediaFormat extraído, y llamada a un método local de trabajo de configuración native_configure codec. Al configurar, los métodos de configure necesitan pasar formato, superficie, crypto, parámetro banderas, en el que el formato es MediaFormat ejemplo, que utiliza "clave-valor" información de formato de clave almacenada en forma de datos multimedia; Surface para indicar el decodificador desde la fuente de datos a la superficie; especifica cripto un objeto MediaCrypto para desencriptar la seguridad de los datos de medios; las banderas indica que la configuración del codificador (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); 

El código de seguridad está dispuesto en el método de codificación H.264, createVideoFormat ( "video / AVC" , 640, 480) como "/ AVC video" tipo (es decir, H.264) codificador de objetos MediaFormat, es necesario especificar la anchura de los datos de vídeo alta, si la codificación y decodificación de datos de audio, la MediaFormat de llamada createAudioFormat (MIME String, int de sampleRate, int channelCount) método. Además alguna configuración de vídeo, como una velocidad de fotogramas de vídeo, velocidad de muestreo de audio y otros parámetros, hay que subrayar explicar MediaFormat.KEY_COLOR_FORMAT atributo de configuración, que indica el formato de color para el codificador de vídeo, el formato de color de entrada y de color específico fuente de datos seleccionada sobre el formato. Por ejemplo, todos sabemos que la adquisición del flujo de imágenes de la cámara de vista previa es por lo general NV21 o YV12, entonces la necesidad codificador para especificar el formato de color apropiado, o datos codificados obtenidos pueden aparecer Huaping, fantasma, distorsión de color y así sucesivamente. . MediaCodecInfo.CodecCapabilities almacena los codificadores soportan todos los formatos de color, mapeo formato de color común es el siguiente:
el codificador de datos en bruto
NV12 (yuv420sp) ---> COLOR_FormatYUV420PackedSemiPlanar
NV21 ----> COLOR_FormatYUV420SemiPlanar
YV12 (I420) ----> COLOR_FormatYUV420Planar
cuando se ha completado la configuración del códec, puede llamar al método start MediaCodec (), que llama al método de bajo nivel native_start () para iniciar el codificador, y llama al método de bajo nivel ByteBuffer [] getBuffers (entrada) para abrir una serie de entrada, salida amortiguar. método start () el código fuente de la siguiente manera:

 

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

el procesamiento (3) Los datos
MediaCodec codec soporta dos modos, es decir síncrono síncronos, asíncronos asíncrono, uno de los llamados medios de modo síncrono que los datos codec de entrada y de salida se sincroniza, la salida del procesamiento de codec se completa será introducida de nuevo recibido transacciones; entrada asíncrona y codecs de salida son datos asíncronos, el códec no espera antes de recibir datos de salida de los datos de entrada se procesa de nuevo. A continuación, presentamos el siguiente códec de sincronización mayor, porque de esta manera nos encontramos con más de. Sabemos que cuando se inicia el códec, cada códec tendrá un conjunto de entrada y búfer de salida, pero el buffer no se puede utilizar temporalmente, para obtener la autorización de entrada y MediaCodec búfer de salida del método dequeueInputBuffer / dequeueOutputBuffer, ID volvió a operar estos tampones. Hemos adoptado el siguiente fragmento de código proporcionado por el análisis oficial, extendida:

 

 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();

En el código anterior, cuando el inicio códec, entrará en un for (;;) de bucle, que es un bucle infinito para implementar una memoria intermedia continua es a los datos adquieren desde el búfer de entrada contiene la piscina códec , entonces bueno codec de salida de datos de salida de la agrupación de almacenamiento intermedio adquirido.

● Obtener buffer de entrada codec, los datos se escriben
en primer lugar, la llamada MediaCodec dequeueInputBuffer (larga timeoutUs) toma un buffer de entrada de la entrada de ajuste de la memoria tampón del codificador y devuelve el índice del índice de caché, y si index = -1 instrucciones disponibles caché temporal, cuando timeoutUs = 0 cuando dequeueInputBuffer devolverá inmediatamente. Entonces la llamada MediaCodec getInputBuffer (int index), que se pasa al método índice local GetBuffer (verdadero / * entrada * / , index) Devuelve el tampón ByteBuffer, y el objeto ByteBuffer almacenado y su índice a la BufferMap obtenida objeto para el proceso de liberación para el final de la memoria intermedia de entrada, de vuelta al codec. getInputBuffer fuente (int index) como sigue:

 

    @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;
    }

Entonces, después de obtener el buffer de entrada, los datos en los datos y lo utiliza para presentar queueInputBuffer códec de procesamiento durante el buffer de entrada se libera de nuevo al codec. código fuente queueInputBuffer como sigue:

 

    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 de los códigos anteriores, queueInputBuffer native_queueInputBuffer principalmente bajo nivel llamando al método, que deben pasar cinco parámetros, donde el índice es el índice del buffer de entrada del codec es a través del índice para encontrar la ubicación de la memoria intermedia; compensado datos válidos el tampón offset; tamaño es el tamaño de los datos de entrada original es válida; presentationTimeUs sello de tiempo de presentación de la memoria intermedia, típicamente 0; banderas bandera memoria intermedia de entrada se ajusta usualmente a BUFFER_FLAG_END_OF_STREAM.

● codec búfer de salida adquirido, los datos se leen
en primer lugar, y el descrito anteriormente dequeueInputBuffer getInputBuffer mediante la adquisición de una entrada tampón similares, MediaCodec dequeueOutputBuffer también proporciona un método y ayudarnos a obtener búfer de salida getOutputBuffer codec. Pero la diferencia es que con dequeueInputBuffer, dequeueOutputBuffer también tienen que pasar un objeto MediaCodec.BufferInfo. MediaCodec.BufferInfo MediaCodec es una clase interna, que registra la buena códec de datos de corrección y el tamaño en el área de memoria intermedia de salida.

 

  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;
        }
    };

Entonces, la fuente dequeueOutputBuffer entiende, cuando el valor de retorno dequeueOutputBuffer> = 0, la salida de datos de tampón son válidos. Cuando una llamada método local vuelve native_dequeueOutputBuffer INFO_OUTPUT_BUFFERS_CHANGED, las llamadas a métodos cacheBuffers recuperar un conjunto de búfer de salida mCachedOutputBuffers (ByteBuffer []). Esto explica que si usamos los getOutputBuffers método (API21 desechado después de su uso getOutputBuffer (índice) en su lugar) para obtener el búfer de salida codec, entonces tiene que llamar el valor de retorno se determina dequeueOutputBuffer, si el valor de retorno MediaCodec. INFO_OUTPUT_BUFFERS_CHANGED, tendrá que búfer de salida colectiva volver a adquirir. Además, aquí también dos valores de retorno adicionales dequeueOutputBuffer: MediaCodec.INFO_TRY_AGAIN_LATER, MediaCodec.INFO_OUTPUT_FORMAT_CHANGED, la ex recupera búfer de salida codec de tiempo de espera, que representa los datos de códec de cambiar el formato de salida, y luego usar la nueva salida de datos formato. Por lo tanto, tenemos que llamar dequeueOutputBuffer determinar si el valor de retorno es INFO_OUTPUT_FORMAT_CHANGED, necesidad de re-establecer el objetivo MediaFormt por 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;
    }

Por último, la memoria intermedia de salida cuando se procesan los datos, es liberado por el tampón de salida MediaCodec releaseOutputBuffer llamando, y se devuelve al codec, el búfer de salida no será utilizado hasta la próxima disponible a través de dequeueOutputBuffer. método releaseOutputBuffer recibe dos parámetros: Índice, rinda, en el que, índice es la salida de tampón de índice; representa la superficie render se especifica cuando se configura el codificador, se debe establecer en true, la memoria intermedia de salida de datos será transferido a la superficie. Fuente de la siguiente manera:

 

   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 */);
    }

 

Publicados 351 artículos originales · ganado elogios 52 · vistas 190 000 +

Supongo que te gusta

Origin blog.csdn.net/u014644594/article/details/104413563
Recomendado
Clasificación