web-dev-qa-db-ja.com

MediaCodecを使用して一連の画像をビデオとして保存する

MediaCodecを使用して、ファイルにバイト配列として保存された一連の画像をビデオファイルに保存しようとしています。これらの画像をSurfaceView(連続して再生)でテストしましたが、問題なく表示されます。 MediaCodecを使用して多くの例を見てきましたが、これが私が理解していることです(間違っている場合は訂正してください)。

MediaCodecオブジェクトからInputBuffersを取得->フレームの画像データで埋める->入力バッファをキューに入れる->コード化された出力バッファを取得する->ファイルに書き込む->プレゼンテーション時間を増やして繰り返す

ただし、これを何度もテストした結果、次の2つのケースのいずれかになります。

  • 私が模倣しようとしたすべてのサンプルプロジェクトにより、queueInputBufferを2回呼び出したときにメディアサーバーが停止しました。
  • 最後にcodec.flush()を呼び出してみましたが(出力バッファーをファイルに保存した後、これを行った例はありませんでした)、メディアサーバーは停止しませんでしたが、出力ビデオを開くことができませんメディアプレーヤーにファイルするので、何かがおかしい。

これが私のコードです:

MediaCodec codec = MediaCodec.createEncoderByType(MIMETYPE);
        MediaFormat mediaFormat = null;
        if(CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P)){
            mediaFormat = MediaFormat.createVideoFormat(MIMETYPE, 1280 , 720);
        } else {
            mediaFormat = MediaFormat.createVideoFormat(MIMETYPE, 720, 480);
        }


        mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 700000);
        mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 10);
        mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);
        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();
        boolean sawInputEOS = false;
        int inputBufferIndex=-1,outputBufferIndex=-1;
        BufferInfo info=null;

                    //loop to read YUV byte array from file

            inputBufferIndex = codec.dequeueInputBuffer(WAITTIME);
            if(bytesread<=0)sawInputEOS=true;

            if(inputBufferIndex >= 0){
                if(!sawInputEOS){
                    int samplesiz=dat.length;
                    inputBuffers[inputBufferIndex].put(dat);
                    codec.queueInputBuffer(inputBufferIndex, 0, samplesiz, presentationTime, 0);
                    presentationTime += 100;

                    info = new BufferInfo();
                    outputBufferIndex = codec.dequeueOutputBuffer(info, WAITTIME);
                    Log.i("BATA", "outputBufferIndex="+outputBufferIndex);
                    if(outputBufferIndex >= 0){
                        byte[] array = new byte[info.size];
                        outputBuffers[outputBufferIndex].get(array);

                        if(array != null){
                            try {
                                dos.write(array);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }

                        codec.releaseOutputBuffer(outputBufferIndex, false);
                        inputBuffers[inputBufferIndex].clear();
                        outputBuffers[outputBufferIndex].clear();

                        if(sawInputEOS) break;
                    }
                }else{
                    codec.queueInputBuffer(inputBufferIndex, 0, 0, presentationTime, MediaCodec.BUFFER_FLAG_END_OF_STREAM);

                    info = new BufferInfo();
                    outputBufferIndex = codec.dequeueOutputBuffer(info, WAITTIME);

                    if(outputBufferIndex >= 0){
                        byte[] array = new byte[info.size];
                        outputBuffers[outputBufferIndex].get(array);

                        if(array != null){
                            try {
                                dos.write(array);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }

                        codec.releaseOutputBuffer(outputBufferIndex, false);
                        inputBuffers[inputBufferIndex].clear();
                        outputBuffers[outputBufferIndex].clear();
                        break;
                    }
                }


            }
        }

        codec.flush();

        try {
            fstream2.close();
            dos.flush();
            dos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        codec.stop();
        codec.release();
        codec = null;

        return true;

    }

私の質問は、MediaCodecを使用して画像のストリームから実際のビデオを取得するにはどうすればよいですか。私は何が間違っているのですか?

別の質問(私があまり貪欲でない場合)、このビデオにオーディオトラックを追加したいのですが、MediaCodecでも実行できますか、それともFFmpegを使用する必要がありますか?

:Android 4.3のMediaMuxについて知っていますが、アプリが動作する必要があるため、これはオプションではありませんAndroid 4.1+。

更新答えが間違っていたおかげで、メディアサーバーが停止することなくEOSに到達できました(上記のコードは変更後です)。しかし、私が取得しているファイルは意味不明なものを生み出しています。これが私が取得したビデオのスナップショットです(.h264ファイルとしてのみ機能します)。

Video Output

私の入力画像フォーマットはYUV画像(カメラプレビューからのNV21)です。再生可能な形式にすることはできません。すべてのCOLOR_FormatYUV420フォーマットと同じ意味不明な出力を試しました。そして、オーディオを追加するために(MediaCodecを使用して)まだ見つけることができません。

16

私はあなたが正しい一般的な考えを持っていると思います。注意すべきいくつかの事柄:

  • すべてのデバイスが_COLOR_FormatYUV420SemiPlanar_をサポートしているわけではありません。平面のみを受け入れるものもあります。 (Android 4.3では、AVCコーデックがどちらか一方をサポートしていることを確認するためにCTSテストが導入されました。)
  • 入力バッファをキューに入れるとすぐに1つの出力バッファが生成されるわけではありません。一部のコーデックは、出力を生成する前に入力のいくつかのフレームを蓄積し、入力が終了した後に出力を生成する場合があります。ループがそれを考慮に入れていることを確認してください(たとえば、inputBuffers[].clear()がまだ-1の場合、爆発します)。
  • 同じqueueInputBuffer呼び出しでデータを送信してEOSを送信しようとしないでください。そのフレーム内のデータは破棄される可能性があります。常に長さゼロのバッファを使用してEOSを送信してください。

コーデックの出力は、一般的にかなり「生」です。 AVCコーデックは、「調理済み」の.mp4ファイルではなく、H.264エレメンタリストリームを出力します。多くのプレイヤーはこのフォーマットを受け入れません。 MediaMuxerの存在に頼ることができない場合は、データをクックする別の方法を見つける必要があります(アイデアについてはstackoverflowを検索してください)。

メディアサーバープロセスがクラッシュすることは確かに予想されていません。

4.3 CTSテストへのいくつかの例とリンクを見つけることができます ここ

更新:Android 4.3の時点で、MediaCodecCameraにはByteBufferがありませんフォーマットは共通しているので、少なくとも彩度面をいじる必要がありますが、そのような問題の現れ方は大きく異なります( この質問 の画像に示されているように)。

追加した画像はビデオのように見えますが、ストライドや配置の問題があります。ピクセルが正しく配置されていることを確認してください。 CTS EncodeDecodeTest で、generateFrame()メソッド(行906)は、MediaCodecの平面YUV420と半平面YUV420の両方をエンコードする方法を示しています。

フォーマットの問題を回避する最も簡単な方法は、フレームをSurface(CameraToMpegTestサンプルのように)に移動することですが、残念ながらAndroid 4.1では不可能です。

11
fadden