13

これまでのところ、ビデオストリームをエンコードするようにMediaCodecをセットアップすることができます。目的は、ユーザーが生成したアートワークをビデオファイルに保存することです。

ユーザーアートワークのAndroidビットマップオブジェクトを使用して、フレームをストリームにプッシュします。

この投稿の下部にあるコードスニペットを参照してください(これは完全なコードであり、何もトリミングされていません)。

MediaCodecは、ByteBufferを使用してビデオ/オーディオストリームを処理します。

ビットマップはint[]に基づいており、byte []に​​変換すると、int[]のサイズのx4が必要になります。

MediaCodecでビデオ/オーディオストリームを処理するときにByteBufferにどのようなコントラクトがあるかを調べるためにいくつかの調査を行いましたが、情報はほぼzilchに近いものです。

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

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

(フレームごとに一度にビットマップオブジェクトを使用します)

助けてくれてありがとう。

(編集、コード追加)

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

1 に答える 1

9

の正確なレイアウトは、ByteBuffer選択した入力形式のコーデックによって決まります。すべてのデバイスが可能なすべての入力フォーマットをサポートしているわけではありません (たとえば、一部の AVC エンコーダーは平面 420 YUV を必要とし、他のエンコーダーは半平面を必要とします)。古いバージョンの Android (<= API 17) では、MediaCodec.

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

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

于 2013-04-02T17:39:22.783 に答える