2013-02-11 19 views
10

Do tej pory jestem w stanie skonfigurować MediaCodec do kodowania strumienia wideo. Celem jest zapisanie mojej grafiki wygenerowanej przez użytkownika w pliku wideo.Jak korzystać ByteBuffer w kontekście MediaCodec w android

używam android obiekty bitmapy grafiki użytkownika naciskać ramki w strumieniu.

Zobacz fragment kodu używam na dole tego postu (jest to pełny kod nie jest przycinany):

MediaCodec wykorzystuje ByteBuffer do czynienia ze strumieni audio/video.

bitmapy są oparte na int [], która w przypadku przekształcenia do byte [] będzie wymagać x4 rozmiar int []

Zrobiłem kilka badań, aby dowiedzieć się, jakie kontrakty są tam miejsca dla ByteBuffer gdy mamy do czynienia ze strumieniami wideo/audio w MediaCodec, ale informacje są prawie zbliżone do zilcha.

Więc jakie są kontrakty użytkowania ByteBuffer w MediaCodec?

Czy podając wymiary klatki w MediaFormat automatycznie oznaczać, że ByteBuffers mieć pojemność szerokość * wysokość * 4 bajtów?

(używam obiekt bitmapy w czasie dla każdej ramki)

Dzięki za wszelką pomoc.

(edytowany, kod dodanej)

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

Odpowiedz

7

Dokładne rozplanowanie ByteBuffer jest określana przez kodek dla formatu wejściowego wybrałeś. Nie wszystkie urządzenia obsługują wszystkie możliwe formaty wejściowe (np. Niektóre enkodery AVC wymagają płaskiego 420 YUV, inne wymagają półpłaszczyznowego). Starsze wersje Androida (< = API 17) tak naprawdę nie zapewniały przenośnego sposobu generowania przez oprogramowanie klatek wideo dla MediaCodec.

W Androidzie 4.3 (API 18), masz dwie opcje. Po pierwsze, MediaCodec przyjmuje teraz dane wejściowe z powierzchni, co oznacza, że ​​wszystko, co można narysować za pomocą OpenGL ES, można nagrać jako film. Zobacz na przykład EncodeAndMuxTest sample.

Po drugie, wciąż masz możliwość korzystania z oprogramowania generowane YUV 420 buforów, ale teraz są one bardziej prawdopodobne, aby działać, ponieważ istnieją testy CTS, które je wykonują. Nadal musisz wykrywać planowe lub półpłaszczyznowe wykrywanie w czasie wykonywania, ale tak naprawdę istnieją tylko dwa układy. Zobacz przykłady bufora do bufora w wersji EncodeDecodeTest.