2013-02-11 51 views
10

これまでは、ビデオストリームをエンコードするためにMediaCodecをセットアップすることができました。目的は、ユーザーが作成したアートワークをビデオファイルに保存することです。AndroidのMediaCodecコンテキストでByteBufferを使用する方法

私はフレームをストリームにプッシュするために、ユーザーのアートワークのアンドロイドビットマップオブジェクトを使用します。

私は(それは完全なコード何がトリミングされていないです)この記事の下部にある使用コードスニペットを参照してください:

MediaCodecは、ビデオ/オーディオストリームに対処するためのByteBufferを使用しています。 []私が扱うときに契約がByteBufferのための場所であるかを把握するためにいくつかの研究を行なった[]

int型のサイズX4必要になるのバイトに変換された場合

ビットマップはint型に基づいています

[] MediaCodecのビデオ/オーディオストリームを使用していますが、その情報はzilchとほぼ同じです。

だから、 MediaCodecのByteBuffer使用契約は何ですか?

MediaFormatでフレームのサイズを指定すると、自動的にByteBuffersの幅*高さ* 4バイトの容量が指定されますか?

(私は、各フレームの時間でビットマップオブジェクトを使用する)任意の助け

感謝。

ByteBufferの正確なレイアウトは、選択した入力フォーマットのコーデックによって決定される

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

答えて

7

を(コードが追加、編集します)。すべてのデバイスが可能なすべての入力フォーマットをサポートしているわけではありません(たとえば、一部のAVCエンコーダは平面420 YUVを必要とし、その他は半平面を必要とします)。古いバージョンのAndroid(< = API 17)は、実際にはMediaCodecのビデオフレームをソフトウェアで生成するポータブルな方法を提供していませんでした。

Android 4.3(API 18)には2つのオプションがあります。まず、MediaCodecはSurfaceからの入力を受け付けます。これは、OpenGL ESで描画できるものはすべてムービーとして記録できることを意味します。たとえば、EncodeAndMuxTest sampleを参照してください。

第2に、ソフトウェアで生成されたYUV 420バッファを使用するオプションがありますが、現在はCTSテストが実行されているため、動作する可能性が高くなります。あなたはまだ平面または半平面のランタイム検出を行う必要がありますが、実際には2つのレイアウトしかありません。例については、EncodeDecodeTestのバッファーからバッファーへの変種を参照してください。

関連する問題