2013-02-11 15 views
10

Finora sono in grado di configurare un MediaCodec per codificare un flusso video. L'obiettivo è di salvare il mio materiale illustrativo generato dall'utente in un file video.Come utilizzare ByteBuffer nel contesto MediaCodec in Android

Uso gli oggetti bitmap Android del disegno utente per inserire fotogrammi nello stream.

Vedi il frammento di codice che uso in fondo a questo post (che è il codice completo non viene tagliato):

MediaCodec utilizza ByteBuffer a che fare con i flussi video/audio.

Bitmap si basano su int [] che, se convertito in byte [] richiederà x4 la dimensione del int []

ho fatto qualche ricerca per capire che cosa i contratti sono in atto per la ByteBuffer quando si tratta con flussi video/audio in MediaCodec, ma le informazioni sono quasi vicine a zilch.

Quindi, quali sono i contratti di utilizzo ByteBuffer in MediaCodec?

Specificare le dimensioni della cornice in MediaFormat significa automaticamente che i ByteBuffer hanno larghezza * altezza * 4 byte di capacità?

(uso un oggetto bitmap alla volta per ogni fotogramma)

Grazie per qualsiasi aiuto.

(a cura, codice aggiunto)

import java.io.ByteArrayOutputStream; 
    import java.io.DataOutputStream; 
    import java.io.File; 
    import java.io.FileOutputStream; 
    import java.nio.ByteBuffer; 

    import android.graphics.Rect; 
    import android.graphics.Bitmap.CompressFormat; 
    import android.media.MediaCodec; 
    import android.media.MediaCodec.BufferInfo; 
    import android.media.CamcorderProfile; 
    import android.media.MediaCodecInfo; 
    import android.media.MediaFormat; 
    import android.util.Log; 
    import android.view.View; 

    public class VideoCaptureManager { 

     private boolean running; 

     private long presentationTime; 

     public void start(View rootView, String saveFilePath){ 
      Log.e("OUT", saveFilePath); 
      this.running = true; 
      this.presentationTime = 0; 
      this.capture(rootView, saveFilePath); 
     } 

     private void capture(final View rootView, String saveFilePath){ 
      if(rootView != null){ 
       rootView.setDrawingCacheEnabled(true); 

       final Rect drawingRect = new Rect(); 
       rootView.getDrawingRect(drawingRect); 

       try{ 
        final File file = new File(saveFilePath); 
        if(file.exists()){ 
         // File exists return 
         return; 
        } else { 
         File parent = file.getParentFile(); 
         if(!parent.exists()){ 
          parent.mkdirs(); 
         } 
        } 

      new Thread(){ 
       public void run(){ 
        try{ 
         DataOutputStream dos = new DataOutputStream(new FileOutputStream(file)); 

         MediaCodec codec = MediaCodec.createEncoderByType("video/mp4v-es"); 
         MediaFormat mediaFormat = null; 
         if(CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P)){ 
          mediaFormat = MediaFormat.createVideoFormat("video/mp4v-es", 720, 1280); 
         } else { 
          mediaFormat = MediaFormat.createVideoFormat("video/mp4v-es", 480, 720); 
         } 


         mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 700000); 
         mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 10); 
         mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar); 
         mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5); 
         codec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); 

         codec.start(); 

         ByteBuffer[] inputBuffers = codec.getInputBuffers(); 
         ByteBuffer[] outputBuffers = codec.getOutputBuffers(); 

         while(VideoCaptureManager.this.running){ 
          try{ 
           int inputBufferIndex = codec.dequeueInputBuffer(-2); 
           if(inputBufferIndex >= 0){ 
            // Fill in the bitmap bytes 
            // inputBuffers[inputBufferIndex]. 
            ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
            rootView.getDrawingCache().compress(CompressFormat.JPEG, 80, baos); 
            inputBuffers[inputBufferIndex].put(baos.toByteArray()); 

            codec.queueInputBuffer(inputBufferIndex, 0, inputBuffers[inputBufferIndex].capacity(), presentationTime, MediaCodec.BUFFER_FLAG_CODEC_CONFIG); 
            presentationTime += 100; 
           } 

           BufferInfo info = new BufferInfo(); 
           int outputBufferIndex = codec.dequeueOutputBuffer(info, -2); 
           if(outputBufferIndex >= 0){ 
            // Write the bytes to file 
            byte[] array = outputBuffers[outputBufferIndex].array(); // THIS THORWS AN EXCEPTION. WHAT IS THE CONTRACT TO DEAL WITH ByteBuffer in this code? 
            if(array != null){ 
             dos.write(array); 
            } 

            codec.releaseOutputBuffer(outputBufferIndex, false); 
           } else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED){ 
            outputBuffers = codec.getOutputBuffers(); 
           } else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){ 
            // codec format is changed 
            MediaFormat format = codec.getOutputFormat(); 
           } 

           Thread.sleep(100); 
          }catch(Throwable th){ 
           Log.e("OUT", th.getMessage(), th); 
          } 
         } 

         codec.stop(); 
         codec.release(); 
         codec = null; 

         dos.flush(); 
         dos.close(); 
        }catch(Throwable th){ 
         Log.e("OUT", th.getMessage(), th); 
        } 
       } 
        }.start(); 

       }catch(Throwable th){ 
        Log.e("OUT", th.getMessage(), th); 
       } 
      } 
     } 

     public void stop(){ 
      this.running = false; 
     } 
    } 

risposta

7

La disposizione esatta del ByteBuffer è determinato dal codec per il formato di input che hai scelto. Non tutti i dispositivi supportano tutti i formati di input possibili (ad esempio alcuni codificatori AVC richiedono 420 YU planari, altri richiedono semi-planari). Le versioni precedenti di Android (< = API 17) non fornivano realmente un modo portabile per la generazione di frame video per il software per MediaCodec.

In Android 4.3 (API 18), sono disponibili due opzioni. Innanzitutto, MediaCodec ora accetta l'input da una superficie, il che significa che qualsiasi cosa tu possa disegnare con OpenGL ES può essere registrata come un film. Vedi, ad esempio, lo EncodeAndMuxTest sample.

In secondo luogo, è ancora possibile utilizzare i buffer YUV 420 generati dal software, ma ora è più probabile che funzionino perché ci sono test CTS che li esercitano. Devi ancora eseguire il rilevamento in runtime di planare o semi-planare, ma ci sono solo due layout. Vedere le varianti da buffer a buffer del EncodeDecodeTest per un esempio.

Problemi correlati