58

起動直後に起動するAndroid1.5アプリケーションを作成しています。これはであり、Serviceプレビューなしで写真を撮る必要があります。このアプリは、いくつかの領域の光の密度をログに記録します。写真は撮れましたが、真っ黒でした。

長い間調査した後、私はそれについてのバグスレッドに出くわしました。プレビューを生成しない場合、Androidカメラは露出とフォーカスを設定するためにプレビューが必要なため、画像は黒になります。SurfaceViewとリスナーを作成しましたが、onSurfaceCreated()イベントが発生することはありません。

その理由は、表面が視覚的に作成されていないためだと思います。また、カメラを静的に呼び出してMediaStore.CAPTURE_OR_SOMETHING写真を撮り、2行のコードで目的のフォルダーに保存する例もいくつか見ましたが、写真も撮りません。

IPCを使用しbindService()てこの関数を呼び出す必要がありますか?または、これを達成するための代替方法はありますか?

4

9 に答える 9

53

Android プラットフォームのカメラが、有効なプレビュー サーフェスを提供するまでビデオをストリーミングできないのは本当に奇妙です。プラットフォームのアーキテクトは、サードパーティのビデオ ストリーミング アプリケーションについてまったく考えていなかったようです。拡張現実の場合でも、リアルタイムのカメラ ストリームではなく、ある種の視覚的な代替として画像を表示できます。

とにかく、プレビュー サーフェスのサイズを 1x1 ピクセルに変更し、ウィジェット (ビジュアル要素) の隅に配置するだけです。注意してください - カメラのフレーム サイズではなく、プレビュー サーフェスのサイズを変更してください。

もちろん、このようなトリックは、システム リソースとバッテリーを消費する不要なデータ ストリーミング (プレビュー用) を排除するものではありません。

于 2010-10-07T11:10:32.140 に答える
40

Android Camera Docsでこれに対する答えを見つけました。

注:MediaRecorder最初にカメラ プレビューを作成せずに使用して、このプロセスの最初のいくつかの手順をスキップすることができます。ただし、ユーザーは通常、記録を開始する前にプレビューを表示することを好むため、そのプロセスについてはここでは説明しません。

上記のリンクでステップバイステップの手順を見つけることができます。指示の後、上記で提供した見積もりが記載されます。

于 2012-06-06T06:57:41.103 に答える
37

実際には可能ですが、ダミーのSurfaceViewでプレビューを偽造する必要があります

SurfaceView view = new SurfaceView(this);
c.setPreviewDisplay(view.getHolder());
c.startPreview();
c.takePicture(shutterCallback, rawPictureCallback, jpegPictureCallback);

2011 年 9 月 21 日更新:どうやら、これはすべての Android デバイスで機能するとは限りません。

于 2011-03-21T23:23:27.613 に答える
36

写真を撮る

プレビューを非表示にする前に、まずこれを機能させてください。

  • プレビューを正しく設定する
    • SurfaceView(Android 4.0 より前の互換性) またはSurfaceTexture(Android 4 以降、透明にすることができます) を使用します。
    • 写真を撮る前に設定して初期化する
    • が( を介して) から報告されるか、が報告するSurfaceViewのを待ってから 、プレビューを設定して初期化してください。SurfaceHoldergetHolder()surfaceCreated()TextureViewonSurfaceTextureAvailableSurfaceTextureListener
  • プレビューが表示されていることを確認します。
    • に追加しますWindowManager
    • レイアウト サイズが 1x1 ピクセル以上であることを確認します (テスト用にMATCH_PARENTxにすることから始めることをお勧めしMATCH_PARENTます)。
    • その可視性を確認してくださいView.VISIBLE(指定しない場合、これがデフォルトのようです)
    • の場合はFLAG_HARDWARE_ACCELERATEDでを使用していることを確認してください。LayoutParamsTextureView
  • takePicture他のコールバックはすべてのデバイスでサポートされていないとドキュメントに記載されているため、 の JPEG コールバックを使用します。

トラブルシューティング

  • surfaceCreated/onSurfaceTextureAvailableが呼び出されない場合、おそらくSurfaceView/は表示されていません。TextureView
  • 失敗した場合takePictureは、まずプレビューが正しく機能していることを確認してください。通話を削除takePictureし、プレビューを実行して、画面に表示されるかどうかを確認できます。
  • takePicture画像が必要以上に暗い場合は、プレビューの開始後にカメラが露出を調整する時間を確保できるように、呼び出す前に約 1 秒待つ必要がある場合があります。

プレビューを非表示にする

  • プレビューをView1x1 サイズにして可視性を最小限に抑えます (または、信頼性を高めるために 8x16 を試してください) 。

    new WindowManager.LayoutParams(1, 1, /*...*/)
    
  • プレビューを中央から移動して、目立たないようにします。

    new WindowManager.LayoutParams(width, height,
        Integer.MIN_VALUE, Integer.MIN_VALUE, /*...*/)
    
  • プレビューを透明にする ( でのみ機能TextureView)

    WindowManager.LayoutParams params = new WindowManager.LayoutParams(
        width, height, /*...*/
        PixelFormat.TRANSPARENT);
    params.alpha = 0;
    

動作例(Sony Xperia M、Android 4.3でテスト済み)

/** Takes a single photo on service start. */
public class PhotoTakingService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        takePhoto(this);
    }

    @SuppressWarnings("deprecation")
    private static void takePhoto(final Context context) {
        final SurfaceView preview = new SurfaceView(context);
        SurfaceHolder holder = preview.getHolder();
        // deprecated setting, but required on Android versions prior to 3.0
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        holder.addCallback(new Callback() {
            @Override
            //The preview must happen at or after this point or takePicture fails
            public void surfaceCreated(SurfaceHolder holder) {
                showMessage("Surface created");

                Camera camera = null;

                try {
                    camera = Camera.open();
                    showMessage("Opened camera");

                    try {
                        camera.setPreviewDisplay(holder);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }

                    camera.startPreview();
                    showMessage("Started preview");

                    camera.takePicture(null, null, new PictureCallback() {

                        @Override
                        public void onPictureTaken(byte[] data, Camera camera) {
                            showMessage("Took picture");
                            camera.release();
                        }
                    });
                } catch (Exception e) {
                    if (camera != null)
                        camera.release();
                    throw new RuntimeException(e);
                }
            }

            @Override public void surfaceDestroyed(SurfaceHolder holder) {}
            @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
        });

        WindowManager wm = (WindowManager)context
            .getSystemService(Context.WINDOW_SERVICE);
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                1, 1, //Must be at least 1x1
                WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
                0,
                //Don't know if this is a safe default
                PixelFormat.UNKNOWN);

        //Don't set the preview visibility to GONE or INVISIBLE
        wm.addView(preview, params);
    }

    private static void showMessage(String message) {
        Log.i("Camera", message);
    }

    @Override public IBinder onBind(Intent intent) { return null; }
}
于 2014-11-22T23:43:47.623 に答える
22

Android 4.0 以降 (API レベル >= 14) では、TextureViewを使用してカメラ ストリームをプレビューし、ユーザーに表示されないように非表示にすることができます。方法は次のとおりです。

まず、プレビュー サーフェスの作成/更新コールバックを取得する SurfaceTextureListener を実装するクラスを作成します。このクラスはカメラ オブジェクトも入力として受け取るため、サーフェスが作成されるとすぐにカメラの startPreview 関数を呼び出すことができます。

public class CamPreview extends TextureView implements SurfaceTextureListener {

  private Camera mCamera;

  public CamPreview(Context context, Camera camera) {
    super(context);
    mCamera = camera;
   }

  @Override
  public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
    Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
    setLayoutParams(new FrameLayout.LayoutParams(
        previewSize.width, previewSize.height, Gravity.CENTER));

    try{
      mCamera.setPreviewTexture(surface);
     } catch (IOException t) {}

    mCamera.startPreview();
    this.setVisibility(INVISIBLE); // Make the surface invisible as soon as it is created
  }

  @Override
  public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
      // Put code here to handle texture size change if you want to
  }

  @Override
  public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
    return true;
  }

  @Override
  public void onSurfaceTextureUpdated(SurfaceTexture surface) {
      // Update your view here!
  }
}

また、プレビュー データを処理するためのコールバック クラスを実装する必要があります。

public class CamCallback implements Camera.PreviewCallback{
  public void onPreviewFrame(byte[] data, Camera camera){
     // Process the camera data here
  }
}

上記の CamPreview および CamCallback クラスを使用して、アクティビティの onCreate() または同様の起動関数でカメラをセットアップします。

// Setup the camera and the preview object
Camera mCamera = Camera.open(0);
CamPreview camPreview = new CamPreview(Context,mCamera);
camPreview.setSurfaceTextureListener(camPreview);

// Connect the preview object to a FrameLayout in your UI
// You'll have to create a FrameLayout object in your UI to place this preview in
FrameLayout preview = (FrameLayout) findViewById(R.id.cameraView); 
preview.addView(camPreview);

// Attach a callback for preview
CamCallback camCallback = new CamCallback();
mCamera.setPreviewCallback(camCallback);
于 2013-01-09T02:43:49.537 に答える
20

これを行う方法はありますが、少しトリッキーです。すべきことは、サービスからウィンドウマネージャーにサーフェスホルダーを接続することです

WindowManager wm = (WindowManager) mCtx.getSystemService(Context.WINDOW_SERVICE);
params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
            WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
            PixelFormat.TRANSLUCENT);        
wm.addView(surfaceview, params);

そして設定

surfaceview.setZOrderOnTop(true);
mHolder.setFormat(PixelFormat.TRANSPARENT);

mHolder は、サーフェス ビューから取得したホルダーです。

このようにして、サーフェスビューのアルファを操作して完全に透明にすることができますが、カメラは引き続きフレームを取得します。

それが私のやり方です。それが役に立てば幸い :)

于 2012-04-22T14:12:25.993 に答える
13

この問題は、バージョン 3.0 より前のダミーの SurfaceView (実際の G​​UI には追加されていません) を使用することで解決しました (または、タブレットのカメラ サービスとして 4.0 はあまり意味がありません)。バージョン >= 4.0 では、これはエミュレータでのみ機能しました ;( SurfaceView (および setSurfaceView()) の代わりに SurfaceTexture (および setSurfaceTexture()) を使用すると、ここで機能しました。少なくとも、これは Nexus S で機能します。

これは本当に Android フレームワークの欠点だと思います。

于 2012-04-20T15:05:32.803 に答える