9

私はこの質問を何度も検索しようとしましたが、満足のいく答えは見たことがないので、ここで最後の希望があります。

コールバックをonPreviewFrame設定しました。これにより、byte[]サポートされているプレビュー形式(エンコードされたタイプ)の生フレームが得られNV21ますH.264

現在、問題は、コールバックが常に固定方向からフレームを提供し始めることです。デバイスが回転するたびに、キャプチャされたフレームbyte[]に反映されません。試してbyte[]みましたが、これらのAPIはプレビューにのみ反映されており、キャプチャされたフレームにはまったく表示されていません。setDisplayOrientationsetRotationbyte []

Androidドキュメントによると、Camera.setDisplayOrientationフレームバイトではなく、表示プレビューにのみ影響します。

これは、onPreviewFrame(byte []、Camera)、JPEG画像、または録画されたビデオで渡されるバイト配列の順序には影響しません。

最後に、APIレベルで、byte[]フレームの向きを変更する方法はありますか?

4

2 に答える 2

2

形式を気にしない場合に考えられる方法の 1 つは、YuvImageクラスを使用して JPEG バッファを取得し、このバッファを使用して を作成Bitmapし、対応する角度に回転させることです。そんな感じ:

@Override
public void onPreviewFrame(byte[] data, Camera camera) {

    Size previewSize = camera.getParameters().getPreviewSize();
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    byte[] rawImage = null;

    // Decode image from the retrieved buffer to JPEG
    YuvImage yuv = new YuvImage(data, ImageFormat.NV21, previewSize.width, previewSize.height, null);
    yuv.compressToJpeg(new Rect(0, 0, previewSize.width, previewSize.height), YOUR_JPEG_COMPRESSION, baos);
    rawImage = baos.toByteArray();

    // This is the same image as the preview but in JPEG and not rotated
    Bitmap bitmap = BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length);
    ByteArrayOutputStream rotatedStream = new ByteArrayOutputStream();

    // Rotate the Bitmap
    Matrix matrix = new Matrix();
    matrix.postRotate(YOUR_DEFAULT_ROTATION);

    // We rotate the same Bitmap
    bitmap = Bitmap.createBitmap(bitmap, 0, 0, previewSize.width, previewSize.height, matrix, false);

    // We dump the rotated Bitmap to the stream 
    bitmap.compress(CompressFormat.JPEG, YOUR_JPEG_COMPRESSION, rotatedStream);

    rawImage = rotatedStream.toByteArray();

    // Do something we this byte array
}
于 2013-03-19T12:51:32.870 に答える
1

このオープン ソースの Android Touch-To-Record ライブラリの onPreviewFrame メソッドを変更して、キャプチャしたフレームの転置とサイズ変更を行いました。

setCameraParams() メソッドで次のように「yuvIplImage」を定義しました。

IplImage yuvIplImage = IplImage.create(mPreviewSize.height, mPreviewSize.width, opencv_core.IPL_DEPTH_8U, 2);

これは私の onPreviewFrame() メソッドです:

@Override
public void onPreviewFrame(byte[] data, Camera camera)
{

    long frameTimeStamp = 0L;

    if(FragmentCamera.mAudioTimestamp == 0L && FragmentCamera.firstTime > 0L)
    {
        frameTimeStamp = 1000L * (System.currentTimeMillis() - FragmentCamera.firstTime);
    }
    else if(FragmentCamera.mLastAudioTimestamp == FragmentCamera.mAudioTimestamp)
    {
        frameTimeStamp = FragmentCamera.mAudioTimestamp + FragmentCamera.frameTime;
    }
    else
    {
        long l2 = (System.nanoTime() - FragmentCamera.mAudioTimeRecorded) / 1000L;
        frameTimeStamp = l2 + FragmentCamera.mAudioTimestamp;
        FragmentCamera.mLastAudioTimestamp = FragmentCamera.mAudioTimestamp;
    }

    synchronized(FragmentCamera.mVideoRecordLock)
    {
        if(FragmentCamera.recording && FragmentCamera.rec && lastSavedframe != null && lastSavedframe.getFrameBytesData() != null && yuvIplImage != null)
        {
            FragmentCamera.mVideoTimestamp += FragmentCamera.frameTime;

            if(lastSavedframe.getTimeStamp() > FragmentCamera.mVideoTimestamp)
            {
                FragmentCamera.mVideoTimestamp = lastSavedframe.getTimeStamp();
            }

            try
            {
                yuvIplImage.getByteBuffer().put(lastSavedframe.getFrameBytesData());

                IplImage bgrImage = IplImage.create(mPreviewSize.width, mPreviewSize.height, opencv_core.IPL_DEPTH_8U, 4);// In my case, mPreviewSize.width = 1280 and mPreviewSize.height = 720
                IplImage transposed = IplImage.create(mPreviewSize.height, mPreviewSize.width, yuvIplImage.depth(), 4);
                IplImage squared = IplImage.create(mPreviewSize.height, mPreviewSize.height, yuvIplImage.depth(), 4);

                int[] _temp = new int[mPreviewSize.width * mPreviewSize.height];

                Util.YUV_NV21_TO_BGR(_temp, data, mPreviewSize.width,  mPreviewSize.height);

                bgrImage.getIntBuffer().put(_temp);

                opencv_core.cvTranspose(bgrImage, transposed);
                opencv_core.cvFlip(transposed, transposed, 1);

                opencv_core.cvSetImageROI(transposed, opencv_core.cvRect(0, 0, mPreviewSize.height, mPreviewSize.height));
                opencv_core.cvCopy(transposed, squared, null);
                opencv_core.cvResetImageROI(transposed);

                videoRecorder.setTimestamp(lastSavedframe.getTimeStamp());
                videoRecorder.record(squared);
            }
            catch(com.googlecode.javacv.FrameRecorder.Exception e)
            {
                e.printStackTrace();
            }
        }

        lastSavedframe = new SavedFrames(data, frameTimeStamp);
    }
}

このコードは、このリンクから見つけたメソッド「YUV_NV21_TO_BGR」を使用しています

基本的にこの方法は、私が「Android の緑の悪魔の問題」と呼んでいる解決に使用されます。他の SO スレッドで同じ問題に直面している他の A​​ndroid 開発者を見ることができます。「YUV_NV21_TO_BGR」メソッドを追加する前に、YuvIplImage の転置、さらに重要なことに、転置、反転 (サイズ変更の有無にかかわらず) の組み合わせを行ったときに、結果のビデオに緑がかった出力がありました。この「YUV_NV21_TO_BGR」メソッドは、その日を救いました。上記の Google グループ スレッドの @David Han に感謝します。

また、onPreviewFrame でのこのすべての処理 (転置、反転、およびサイズ変更) には多くの時間がかかり、フレーム/秒 (FPS) レートに非常に深刻な影響を与えることも知っておく必要があります。このコードを onPreviewFrame メソッド内で使用すると、記録されたビデオの FPS は 30fps から 3 フレーム/秒に低下しました。

このアプローチは使用しないことをお勧めします。代わりに、AsyncTask で JavaCV を使用して、ビデオ ファイルのレコーディング後の処理 (転置、反転、およびサイズ変更) を行うことができます。お役に立てれば。

于 2014-09-24T21:01:01.827 に答える