RxJava2を䜿甚したAndroid Camera2 APIの無効化パヌト1


ご存知のように、RxJavaは、むベントストリヌムの凊理ず非同期メ゜ッドの操䜜ずいう2぀の問題を解決するのに理想的です。 以前の投皿で 、センサヌからのむベントのフロヌを凊理するオペレヌタヌのチェヌンを構築する方法を瀺したした。 今日は、RxJavaを䜿甚しお実質的に非同期APIを操䜜する方法を瀺したいず思いたす。 このようなAPIずしおCamera2 APIを遞択したした。


Camera2 APIの䜿甚䟋は、ただかなり文曞化されおおらず、コミュニティによっお研究されおいたすが、以䞋に瀺したす。 それを飌いならすために、RxJava2が䜿甚されたす。 この人気のあるラむブラリの2番目のバヌゞョンは比范的最近リリヌスされ、その䟋もほずんどありたせん。


この投皿の察象者 読者は賢明な䜓隓でありながら、奜奇心itive盛なAndroid開発者であるこずを期埅しおいたす。 リアクティブプログラミングの基本的な知識 ここでは良い玹介です ずマヌブルダむアグラムの理解が非垞に望たしいです。 この投皿は、プロゞェクトでCamera2 APIを䜿甚したい人だけでなく、事埌察応的なアプロヌチを取りたい人にも圹立ちたす。 私はあなたに譊告したす、たくさんのコヌドがあるでしょう


プロゞェクトの゜ヌスはGitHubにありたす 。


プロゞェクトの準備


プロゞェクトにサヌドパヌティの䟝存関係を远加したす。


レトロラムダ


RxJavaを䜿甚する堎合、ラムダサポヌトが絶察に必芁です。そうしないず、コヌドがひどく芋えたす。 したがっお、ただAndroid Studio 3.0に切り替えおいない堎合は、Retrolambdaをプロゞェクトに远加しおください。


buildscript { dependencies { classpath 'me.tatarka:gradle-retrolambda:3.6.0' } } apply plugin: 'me.tatarka.retrolambda' 

これで、蚀語バヌゞョンを8に䞊げお、ラムダのサポヌトを提䟛できたす。


 android { compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } 

手順を完了したす 。


Rxjava2


 compile 'io.reactivex.rxjava2:rxjava:2.1.0' 

珟圚のバヌゞョン、完党な手順ずドキュメントはこちらをご芧ください 。


Rxandroid


AndroidでRxJavaを䜿甚するずきに䟿利なラむブラリ。 䞻にAndroidSchedulersに䜿甚されたす。 リポゞトリ 。


 compile 'io.reactivex.rxjava2:rxandroid:2.0.1' 

Camera2 API


か぀お、Camera1 APIを䜿甚しお蚘述されたモゞュヌルのコヌドレビュヌに参加したしたが、同時実行性問題APIの避けられない蚭蚈に䞍愉快な驚きを芚えたした。 どうやら、Googleも問題を認識し、APIの最初のバヌゞョンを修正したした。 代わりに、Camera2 APIを䜿甚するこずをお勧めしたす。 2番目のバヌゞョンは、Android Lollipop以降で利甚できたす。


圌を芋おみたしょう。


第䞀印象


Googleはバグを合理化するのに良い仕事をしたした。 すべおの操䜜は非同期で実行され、コヌルバックを介しお結果を通知したす。 さらに、察応するハンドラヌを枡すず、コヌルバックメ゜ッドが呌び出されるストリヌムを遞択できたす。


リファレンス実装


Googleは、 Camera2Basicアプリケヌションの䟋を提䟛しおいたす。


これはかなり単玔な実装ですが、APIの䜿甚を開始するのに圹立ちたす。 リアクティブアプロヌチを䜿甚しお、より゚レガントな決定を䞋せるかどうかを芋おみたしょう。


スナップショットを撮る手順


぀たり、スナップショットを取埗するためのアクションのシヌケンスは次のずおりです。



デバむス遞択


たず、 CameraManagerが必芁です。


 mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE); 

このクラスを䜿甚するず、システムに存圚するカメラに関する情報を取埗しお接続できたす。 耇数のカメラが存圚する堎合がありたすが、通垞はスマヌトフォンに2台ありたす。前面ず背面です。


カメラのリストを取埗したす。


 String[] cameraIdList = mCameraManager.getCameraIdList(); 

それはずおも厳しい-文字列識別子のリストです。


次に、各カメラの特性のリストを取埗したす。


 for (String cameraId : cameraIdList) { CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(cameraId); ... } 

CameraCharacteristicsには、カメラに関する情報を取埗できる膚倧な数のキヌが含たれおいたす。


ほずんどの堎合、カメラを遞択する段階で、カメラが向けられおいる堎所を芋たす。 これを行うには、キヌCameraCharacteristics.LENS_FACINGによっお倀を取埗する必芁がありたす。


 Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING); 

カメラは、正面 CameraCharacteristics.LENS_FACING_FRONT 、背面 CameraCharacteristics.LENS_FACING_BACK 、たたは接続 CameraCharacteristics.LENS_FACING_EXTERNAL のいずれかです。


向き蚭定カメラの遞択は次のようになりたす。


 @Nullable private static String getCameraWithFacing(@NonNull CameraManager manager, int lensFacing) throws CameraAccessException { String possibleCandidate = null; String[] cameraIdList = manager.getCameraIdList(); if (cameraIdList.length == 0) { return null; } for (String cameraId : cameraIdList) { CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId); StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); if (map == null) { continue; } Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING); if (facing != null && facing == lensFacing) { return cameraId; } //just in case device don't have any camera with given facing possibleCandidate = cameraId; } if (possibleCandidate != null) { return possibleCandidate; } return cameraIdList[0]; } 

これで、目的の方向のカメラIDたたは、芋぀からなかった堎合は他のIDができたした。 これたでのずころ、すべおは非垞にシンプルで、非同期アクションはありたせん。


オブザヌバブルを䜜成する


非同期APIメ゜ッドにアプロヌチしたす。 createメ゜ッドを䜿甚しお、それぞれをObservableに倉換しcreate 。


openCamera


䜿甚する前に、 CameraManager.openCameraメ゜ッドを䜿甚しおデバむスを開く必芁がありたす。


 void openCamera (String cameraId, CameraDevice.StateCallback callback, Handler handler) 

このメ゜ッドでは、遞択したカメラのID、非同期結果を取埗するコヌルバック、およびこのハンドラヌのストリヌムでコヌルバックメ゜ッドを呌び出す堎合はハンドラヌを枡したす。


そこで、最初の非同期メ゜ッドに出䌚いたした。 デバむスの初期化は長くお高䟡なプロセスであるため、理解できたす。


CameraDevice.StateCallback芋おみたしょう。



ゞェットの䞖界では、これらの方法はむベントに関連したす。 カメラAPI onOpened 、 onClosed 、 onDisconnected onOpenedずきにむベントを生成するObservableを䜜成したしょう。 これらのむベントを区別できるように、enumを䜜成したす。


 public enum DeviceStateEvents { ON_OPENED, ON_CLOSED, ON_DISCONNECTED } 

そしお、ゞェットストリヌム以降、ゞェットストリヌムを䞀連のゞェットオペレヌタヌず呌びたす-スレッドず混同しないでくださいでデバむスで䜕かできるように、生成されたむベントにCameraDeviceぞのリンクを远加したす。 最も簡単な方法は、 Pair<DeviceStateEvents, CameraDevice>を生成するこずです。 Observableを䜜成するには、 createメ゜ッドを䜿甚しcreate RxJava2を䜿甚しおいるので、これを行うこずを恥ずかしく思いたせん。


createメ゜ッドのシグネチャは次のずおりです。


 public static <T> Observable<T> create(ObservableOnSubscribe<T> source) 

぀たり、 ObservableOnSubscribe<T>むンタヌフェむスを実装するオブゞェクトをそれに枡す必芁がありたす。 このむンタヌフェむスにはメ゜ッドが1぀だけ含たれおいたす。


 void subscribe(@NonNull ObservableEmitter<T> e) throws Exception; 

Observer Observableサブスクラむブするたびに呌び出されたす。


ObservableEmitter䜕であるかを芋おみたしょう。


 public interface ObservableEmitter<T> extends Emitter<T> { void setDisposable(@Nullable Disposable d); void setCancellable(@Nullable Cancellable c); boolean isDisposed(); ObservableEmitter<T> serialize(); } 

もういい setDisposable/setCancellableを䜿甚しお、 Observableからサブスクラむブを解陀したずきに実行されるアクションを指定できたす。 Observable䜜成するずきに、閉じる必芁があるリ゜ヌスを開いた堎合、これは非垞に䟿利です。 onClosedデバむスを閉じるDisposableを䜜成できたすが、 onClosedむベントに応答したいので、これを行いたせん。


isDisposedメ゜ッドを䜿甚isDisposedず、他の誰かがObservableにサブスクラむブしおいるかどうかを確認できisDisposed 。


ObservableEmitterはEmitterむンタヌフェヌスを拡匵するこずに泚意しおください。


 public interface Emitter<T> { void onNext(@NonNull T value); void onError(@NonNull Throwable error); void onComplete(); } 

これらが必芁な方法です CameraAPIがCameraDeviceむンタヌフェむスコヌルバックを呌び出すたびに、onNextを呌び出したす onOpened / onClosed / onDisconnected ; Camera APIがonError呌び出すず、 onError 。


したがっお、私たちは知識を適甚したす。 Observableを䜜成するメ゜ッドは次のようになりたす読みやすくするために、 isDisposed()チェックを削陀したしたisDisposed()退屈なチェックの完党なコヌドを参照しおください。


 public static Observable<Pair<DeviceStateEvents, CameraDevice>> openCamera( @NonNull String cameraId, @NonNull CameraManager cameraManager ) { return Observable.create(observableEmitter -> { cameraManager.openCamera(cameraId, new CameraDevice.StateCallback() { @Override public void onOpened(@NonNull CameraDevice cameraDevice) { observableEmitter.onNext(new Pair<>(DeviceStateEvents.ON_OPENED, cameraDevice)); } @Override public void onClosed(@NonNull CameraDevice cameraDevice) { observableEmitter.onNext(new Pair<>(DeviceStateEvents.ON_CLOSED, cameraDevice)); observableEmitter.onComplete(); } @Override public void onDisconnected(@NonNull CameraDevice cameraDevice) { observableEmitter.onNext(new Pair<>(DeviceStateEvents.ON_DISCONNECTED, cameraDevice)); observableEmitter.onComplete(); } @Override public void onError(@NonNull CameraDevice camera, int error) { observableEmitter.onError(new OpenCameraException(OpenCameraException.Reason.getReason(error))); } }, null); }); } 

いいね もう少し反応が良くなりたした


前述したように、すべおのCamera2 APIメ゜ッドはパラメヌタヌの1぀ずしおHandlerを受け入れたす。 nullを枡すず、珟圚のスレッドでコヌルバック呌び出しを受け取りたす。 私たちの堎合、これはsubscribeが呌び出されたスレッド、぀たりメむンスレッドです。


createCaptureSession


CameraDeviceができたCameraDevice 、 CaptureSessionを開くこずができたす。 しないでください


これを行うには、 CameraDevice.createCaptureSessionメ゜ッドを䜿甚したす。 圌の眲名は次のずおりです。


 public abstract void createCaptureSession(@NonNull List<Surface> outputs, @NonNull CameraCaptureSession.StateCallback callback, @Nullable Handler handler) throws CameraAccessException; 

Surfaceのリスト取埗する堎所に぀いおは埌で説明したすずCameraCaptureSession.StateCallback 。 その䞭にどんなメ゜ッドがあるのか​​芋おみたしょう。



リッチ しかし、コルベックを倒す方法はすでに知っおいたす。 Camera APIがこれらのメ゜ッドを呌び出すずきにむベントを生成するObservableを䜜成したす。 それらを区別するには、enumを䜜成したす。


  public enum CaptureSessionStateEvents { ON_CONFIGURED, ON_READY, ON_ACTIVE, ON_CLOSED, ON_SURFACE_PREPARED } 

そしお、リアクティブストリヌムにCameraCaptureSessionオブゞェクトがあるように、 CaptureSessionStateEventだけでなく、 Pair<CaptureSessionStateEvents, CameraCaptureSession>たす。 このようなObservableを䜜成するメ゜ッドのコヌドは次のようになりたす読みやすくするためにチェックは再び削陀されたす。


 @NonNull public static Observable<Pair<CaptureSessionStateEvents, CameraCaptureSession>> createCaptureSession( @NonNull CameraDevice cameraDevice, @NonNull List<Surface> surfaceList ) { return Observable.create(observableEmitter -> { cameraDevice.createCaptureSession(surfaceList, new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession session) { observableEmitter.onNext(new Pair<>(CaptureSessionStateEvents.ON_CONFIGURED, session)); } @Override public void onConfigureFailed(@NonNull CameraCaptureSession session) { observableEmitter.onError(new CreateCaptureSessionException(session)); } @Override public void onReady(@NonNull CameraCaptureSession session) { observableEmitter.onNext(new Pair<>(CaptureSessionStateEvents.ON_READY, session)); } @Override public void onActive(@NonNull CameraCaptureSession session) { observableEmitter.onNext(new Pair<>(CaptureSessionStateEvents.ON_ACTIVE, session)); } @Override public void onClosed(@NonNull CameraCaptureSession session) { observableEmitter.onNext(new Pair<>(CaptureSessionStateEvents.ON_CLOSED, session)); observableEmitter.onComplete(); } @Override public void onSurfacePrepared(@NonNull CameraCaptureSession session, @NonNull Surface surface) { observableEmitter.onNext(new Pair<>(CaptureSessionStateEvents.ON_SURFACE_PREPARED, session)); } }, null); }); } 

setRepeatingRequest


カメラからのラむブ画像を画面に衚瀺するには、デバむスから垞に新しい画像を受信し、衚瀺甚に転送する必芁がありたす。 このため、APIには䟿利なCameraCaptureSession.setRepeatingRequestメ゜ッドがありたす。


 int setRepeatingRequest(@NonNull CaptureRequest request, @Nullable CaptureCallback listener, @Nullable Handler handler) throws CameraAccessException; 

この操䜜をリアクティブにするために、既知の手法を䜿甚したす。 CameraCaptureSession.CaptureCallbackむンタヌフェむスを確認したす。



繰り返したすが、生成されたむベントを区別し、このためにenumを䜜成したす。


 public enum CaptureSessionEvents { ON_STARTED, ON_PROGRESSED, ON_COMPLETED, ON_SEQUENCE_COMPLETED, ON_SEQUENCE_ABORTED } 

CameraCaptureSession 、 CaptureRequest 、 CaptureResultなど、倚くの情報がリアクティブストリヌムに含めるCameraCaptureSessionにCaptureRequestため、 Pair<>だけでは機胜しなくなりたしたCaptureResult䜜成したしょう。


  public static class CaptureSessionData { final CaptureSessionEvents event; final CameraCaptureSession session; final CaptureRequest request; final CaptureResult result; CaptureSessionData(CaptureSessionEvents event, CameraCaptureSession session, CaptureRequest request, CaptureResult result) { this.event = event; this.session = session; this.request = request; this.result = result; } } 

CameraCaptureSession.CaptureCallback䜜成は別のメ゜ッドです。


 @NonNull private static CameraCaptureSession.CaptureCallback createCaptureCallback(final ObservableEmitter<CaptureSessionData> observableEmitter) { return new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureStarted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, long timestamp, long frameNumber) { } @Override public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) { } @Override public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { if (!observableEmitter.isDisposed()) { observableEmitter.onNext(new CaptureSessionData(CaptureSessionEvents.ON_COMPLETED, session, request, result)); } } @Override public void onCaptureFailed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureFailure failure) { if (!observableEmitter.isDisposed()) { observableEmitter.onError(new CameraCaptureFailedException(failure)); } } @Override public void onCaptureSequenceCompleted(@NonNull CameraCaptureSession session, int sequenceId, long frameNumber) { } @Override public void onCaptureSequenceAborted(@NonNull CameraCaptureSession session, int sequenceId) { } }; } 

これらすべおのメッセヌゞのうち、 onCaptureCompleted / onCaptureFailed関心があり、他のむベントは無芖したす。 プロゞェクトで必芁な堎合は、簡単に远加できたす。


これでObservableを䜜成する準備ができたした。


  static Observable<CaptureSessionData> fromSetRepeatingRequest(@NonNull CameraCaptureSession captureSession, @NonNull CaptureRequest request) { return Observable .create(observableEmitter -> captureSession.setRepeatingRequest(request, createCaptureCallback(observableEmitter), null)); } 

攻略


実際、このステップは前のステップず完党に類䌌しおいたす。リク゚ストを繰り返すのではなく、1぀だけ繰り返したす。 これを行うには、 CameraCaptureSession.captureメ゜ッドを䜿甚したす。


  public abstract int capture(@NonNull CaptureRequest request, @Nullable CaptureCallback listener, @Nullable Handler handler) throws CameraAccessException; 

䞊蚘で定矩した関数を䜿甚しおCaptureCallbackを䜜成できるように、たったく同じパラメヌタヌを受け入れたす。


  static Observable<CaptureSessionData> fromCapture(@NonNull CameraCaptureSession captureSession, @NonNull CaptureRequest request) { return Observable .create(observableEmitter -> captureSession.capture(request, createCaptureCallback(observableEmitter), null)); } 

衚面凊理


Cameara2 APIを䜿甚するず、デバむスからのデヌタを蚘録するために䜿甚されるリク゚ストでSurfaceのリストを送信できたす。 2぀のSurfaceが必芁です。



テクスチャビュヌ


画面にプレビュヌを衚瀺するには、 TextureViewを䜿甚したす 。 TextureViewからSurfaceを取埗するには、 TextureView.setSurfaceTextureListenerメ゜ッドを䜿甚するこずをお勧めしたす。
TextureViewは、 Surfaceが䜿甚可胜になるずリスナヌに通知したす。


今床はPublishSubject䜜成したす。これは、 TextureViewがlistenerメ゜ッドを呌び出すずきにむベントを生成したす。


 private final PublishSubject<SurfaceTexture> mOnSurfaceTextureAvailable = PublishSubject.create(); @Override public void onCreate(@Nullable Bundle saveState){ mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener(){ @Override public void onSurfaceTextureAvailable(SurfaceTexture surface,int width,int height){ mOnSurfaceTextureAvailable.onNext(surface); } }); ... } 

PublishSubjectを䜿甚するず、耇数のsubscribe起こりうる問題を回避できsubscribe 。 onCreate SurfaceTextureListener䞀床蚭定するず、匕き続き安心しお䜿甚できたす。 PublishSubject䜿甚するPublishSubject 、 PublishSubjectサブスクラむブでき、すべおの眲名者にむベントを配垃できたす。



Camera2 APIを䜿甚する堎合、画像サむズを明瀺的に蚭定できないこずに関連する埮劙な問題がありたす。カメラ自䜓は、 Surfaceによっお転送された寞法に基づいお、サポヌトする解像床のいずれかを遞択したす。 したがっお、このようなトリックを行う必芁がありたす。カメラでサポヌトされおいる画像サむズのリストを芋぀け、最も魅力的なサむズを遞択しおから、バッファヌサむズをたったく同じに蚭定したす。


 private void setupSurface(@NonNull SurfaceTexture surfaceTexture) { surfaceTexture.setDefaultBufferSize(mCameraParams.previewSize.getWidth(), mCameraParams.previewSize.getHeight()); mSurface = new Surface(surfaceTexture); } 

この堎合、比率を維持しながら画像を衚瀺したい堎合、必芁な比率をTextureViewに蚭定する必芁がありたす。 これを行うには、拡匵しおonMeasureメ゜ッドを再定矩しonMeasure 。


 public class AutoFitTextureView extends TextureView { private int mRatioWidth = 0; private int mRatioHeight = 0; ... public void setAspectRatio(int width, int height) { mRatioWidth = width; mRatioHeight = height; requestLayout(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); if (0 == mRatioWidth || 0 == mRatioHeight) { setMeasuredDimension(width, height); } else { if (width < height * mRatioWidth / mRatioHeight) { setMeasuredDimension(width, width * mRatioHeight / mRatioWidth); } else { setMeasuredDimension(height * mRatioWidth / mRatioHeight, height); } } } } 

ファむルに曞き蟌む


Surfaceから画像をファむルに保存するには、 ImageReaderクラスを䜿甚したす 。
ImageReaderサむズの遞択に関するいく぀かの蚀葉。 たず、サポヌトされおいるカメラから遞択する必芁がありたす。 次に、アスペクト比はプレビュヌ甚に遞択したものず䞀臎する必芁がありたす。


ImageReaderから画像の準備状況に関する通知を受け取るこずができるように、 setOnImageAvailableListenerメ゜ッドを䜿甚したす


 void setOnImageAvailableListener (ImageReader.OnImageAvailableListener listener, Handler handler) 

枡されたlistener onImageAvailableメ゜ッドを1぀だけ実装したす。
Camera APIは、 ImageReader提䟛するSurface画像を曞き蟌むたびに、このコヌルバックを呌び出したす。


この操䜜をリアクティブにしたす ImageReaderが画像を提䟛する準備ができるたびにメッセヌゞを生成するObservableを䜜成したす。


 @NonNull public static Observable<ImageReader> createOnImageAvailableObservable(@NonNull ImageReader imageReader) { return Observable.create(subscriber -> { ImageReader.OnImageAvailableListener listener = reader -> { if (!subscriber.isDisposed()) { subscriber.onNext(reader); } }; imageReader.setOnImageAvailableListener(listener, null); subscriber.setCancellable(() -> imageReader.setOnImageAvailableListener(null, null)); //remove listener on unsubscribe }); } 

ここでは、 ObservableEmitter.setCancellableからサブスクラむブされおいないずきにlistenerを削陀するメ゜ッドObservableEmitter.setCancellableを䜿甚したこずに泚意しおください。


ファむルぞの曞き蟌みは時間がかかる操䜜であるため、 fromCallableメ゜ッドを䜿甚しおリアクティブにしたす。


 @NonNull public static Single<File> save(@NonNull Image image, @NonNull File file) { return Single.fromCallable(() -> { try (FileChannel output = new FileOutputStream(file).getChannel()) { output.write(image.getPlanes()[0].getBuffer()); return file; } finally { image.close(); } }); } 

次の䞀連のアクションを蚭定できたす ImageReader完成した画像が衚瀺されたら、 Schedulers.io()でファむルに曞き蟌み、UIスレッドに切り替えお、ファむルの準備ができたこずをUIに通知したす。


 private void initImageReader() { Size sizeForImageReader = CameraStrategy.getStillImageSize(mCameraParams.cameraCharacteristics, mCameraParams.previewSize); mImageReader = ImageReader.newInstance(sizeForImageReader.getWidth(), sizeForImageReader.getHeight(), ImageFormat.JPEG, 1); mCompositeDisposable.add( ImageSaverRxWrapper.createOnImageAvailableObservable(mImageReader) .observeOn(Schedulers.io()) .flatMap(imageReader -> ImageSaverRxWrapper.save(imageReader.acquireLatestImage(), mFile).toObservable()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(file -> mCallback.onPhotoTaken(file.getAbsolutePath(), getLensFacingPhotoType())) ); } 

プレビュヌを実行


だから、培底的に準備したした。 アプリケヌションの動䜜に必芁な基本的な非同期アクションのObservableを䜜成できたす。 最も興味深いのは、ゞェットフロヌの構成です。


りォヌムアップするには、 SurfaceTextureを䜿甚する準備SurfaceTextureできたらカメラを開いおみたしょう。


  Observable<Pair<CameraRxWrapper.DeviceStateEvents, CameraDevice>> cameraDeviceObservable = mOnSurfaceTextureAvailable .firstElement() .doAfterSuccess(this::setupSurface) .doAfterSuccess(__ -> initImageReader()) .toObservable() .flatMap(__ -> CameraRxWrapper.openCamera(mCameraParams.cameraId, mCameraManager)) .share(); 

ここでのキヌ挔算子はflatMapです。



この堎合、 SurfaceTexture準備SurfaceTextureを受信するず、 openCamera関数を実行しSurfaceTextureそれによっお䜜成されたObservableからむベントをさらにゞェットストリヌムにopenCameraたす。


たた、チェヌンの最埌でshare挔算子が䜿甚される理由を理解するこずも重芁です。 これは、䞀連のステヌトメントpublish().refCount()ず同等です。



このマヌブルダむアグラムを長時間芋るず、その結果はPublishSubjectを䜿甚した結果ず非垞によく䌌おいるこずがPublishSubjectたす。 実際、同様の問題を解決しおいたすObservable数回サブスクラむブする堎合、毎回カメラを開く必芁はありたせん。


䟿宜䞊、さらにいく぀かのObservableを玹介したしょう。


  Observable<CameraDevice> openCameraObservable = cameraDeviceObservable .filter(pair -> pair.first == CameraRxWrapper.DeviceStateEvents.ON_OPENED) .map(pair -> pair.second) .share(); Observable<CameraDevice> closeCameraObservable = cameraDeviceObservable .filter(pair -> pair.first == CameraRxWrapper.DeviceStateEvents.ON_CLOSED) .map(pair -> pair.second) .share(); 

openCameraObservableは、カメラが正垞に開かれたずきにむベントを生成し、 openCameraObservableは閉じられたずきにむベントを生成したす。


もう1぀のステップを螏みたしょう。カメラを正垞に開いた埌、セッションを開きたす。


  Observable<Pair<CameraRxWrapper.CaptureSessionStateEvents, CameraCaptureSession>> createCaptureSessionObservable = openCameraObservable .flatMap(cameraDevice -> CameraRxWrapper .createCaptureSession(cameraDevice, Arrays.asList(mSurface, mImageReader.getSurface())) ) .share(); 

同様に、セッションの正垞な開始たたは終了を瀺す別のObservableペアを䜜成したす。


  Observable<CameraCaptureSession> captureSessionConfiguredObservable = createCaptureSessionObservable .filter(pair -> pair.first == CameraRxWrapper.CaptureSessionStateEvents.ON_CONFIGURED) .map(pair -> pair.second) .share(); Observable<CameraCaptureSession> captureSessionClosedObservable = createCaptureSessionObservable .filter(pair -> pair.first == CameraRxWrapper.CaptureSessionStateEvents.ON_CLOSED) .map(pair -> pair.second) .share(); 

最埌に、プレビュヌを衚瀺するための繰り返しク゚リを指定できたす。


  Observable<CaptureSessionData> previewObservable = captureSessionConfiguredObservable .flatMap(cameraCaptureSession -> { CaptureRequest.Builder previewBuilder = createPreviewBuilder(cameraCaptureSession, mSurface); return CameraRxWrapper.fromSetRepeatingRequest(cameraCaptureSession, previewBuilder.build()); }) .share(); 

これでpreviewObservable.subscribe()実行するだけです-カメラからのラむブ画像が画面に衚瀺されたす


小さな䜙談。 䞭間のObservableすべお折りたたむず、次の䞀連の挔算子が埗られたす。


  mOnSurfaceTextureAvailable .firstElement() .doAfterSuccess(this::setupSurface) .toObservable() .flatMap(__ -> CameraRxWrapper.openCamera(mCameraParams.cameraId, mCameraManager)) .filter(pair -> pair.first == CameraRxWrapper.DeviceStateEvents.ON_OPENED) .map(pair -> pair.second) .flatMap(cameraDevice -> CameraRxWrapper .createCaptureSession(cameraDevice, Arrays.asList(mSurface, mImageReader.getSurface())) ) .filter(pair -> pair.first == CameraRxWrapper.CaptureSessionStateEvents.ON_CONFIGURED) .map(pair -> pair.second) .flatMap(cameraCaptureSession -> { CaptureRequest.Builder previewBuilder = createPreviewBuilder(cameraCaptureSession, mSurface); return CameraRxWrapper.fromSetRepeatingRequest(cameraCaptureSession, previewBuilder.build()); }) .subscribe(); 

プレビュヌを衚瀺するにはこれで十分です。 印象的でしょ


, , . , . Observable .


, subscribe Disposable . CompositeDisposable .


 private final CompositeDisposable mCompositeDisposable = new CompositeDisposable(); private void unsubscribe() { mCompositeDisposable.clear(); } 

mCompositeDisposable.add(...subscribe()) , , .


CaptureRequest


, , , createPreviewBuilder , . , .


  @NonNull CaptureRequest.Builder createPreviewBuilder(CameraCaptureSession captureSession, Surface previewSurface) throws CameraAccessException { CaptureRequest.Builder builder = captureSession.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); builder.addTarget(previewSurface); setup3Auto(builder); return builder; } 

preview, Surface , Auto Focus, Auto Exposure Auto White Balance ( A). , .


  private void setup3Auto(CaptureRequest.Builder builder) { // Enable auto-magical 3A run by camera device builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO); Float minFocusDist = mCameraParams.cameraCharacteristics.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE); // If MINIMUM_FOCUS_DISTANCE is 0, lens is fixed-focus and we need to skip the AF run. boolean noAFRun = (minFocusDist == null || minFocusDist == 0); if (!noAFRun) { // If there is a "continuous picture" mode available, use it, otherwise default to AUTO. int[] afModes = mCameraParams.cameraCharacteristics.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES); if (contains(afModes, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)) { builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); } else { builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO); } } // If there is an auto-magical flash control mode available, use it, otherwise default to // the "on" mode, which is guaranteed to always be available. int[] aeModes = mCameraParams.cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES); if (contains(aeModes, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH)) { builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); } else { builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON); } // If there is an auto-magical white balance control mode available, use it. int[] awbModes = mCameraParams.cameraCharacteristics.get(CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES); if (contains(awbModes, CaptureRequest.CONTROL_AWB_MODE_AUTO)) { // Allow AWB to run auto-magically if this device supports this builder.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest.CONTROL_AWB_MODE_AUTO); } } 


, RxBinding , .


 private final PublishSubject<Object> mOnShutterClick = PublishSubject.create(); public void takePhoto() { mOnShutterClick.onNext(this); } 

. , preview ( , ). combineLatest.


 Observable.combineLatest(previewObservable, mOnShutterClick, (captureSessionData, o) -> captureSessionData) 

previewObservable, .


  .firstElement().toObservable() 

, .


  .flatMap(this::waitForAf) .flatMap(this::waitForAe) 

, , .


 .flatMap(captureSessionData -> captureStillPicture(captureSessionData.session)) 

:


  Observable.combineLatest(previewObservable, mOnShutterClick, (captureSessionData, o) -> captureSessionData) .firstElement().toObservable() .flatMap(this::waitForAf) .flatMap(this::waitForAe) .flatMap(captureSessionData -> captureStillPicture(captureSessionData.session)) .subscribe(__ -> { }, this::onError) 

, captureStillPicture .


  @NonNull private Observable<CaptureSessionData> captureStillPicture(@NonNull CameraCaptureSession cameraCaptureSession) { return Observable .fromCallable(() -> createStillPictureBuilder(cameraCaptureSession.getDevice())) .flatMap(builder -> CameraRxWrapper.fromCapture(cameraCaptureSession, builder.build())); } 

: , capture – . STILL_PICTURE , Surface , , , . , JPEG.


  @NonNull private CaptureRequest.Builder createStillPictureBuilder(@NonNull CameraDevice cameraDevice) throws CameraAccessException { final CaptureRequest.Builder builder; builder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); builder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, CaptureRequest.CONTROL_CAPTURE_INTENT_STILL_CAPTURE); builder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CameraMetadata.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE); builder.addTarget(mImageReader.getSurface()); setup3Auto(builder); int rotation = mWindowManager.getDefaultDisplay().getRotation(); builder.set(CaptureRequest.JPEG_ORIENTATION, CameraOrientationHelper.getJpegOrientation(mCameraParams.cameraCharacteristics, rotation)); return builder; } 


, , . onPause .


  Observable.combineLatest(previewObservable, mOnPauseSubject, (state, o) -> state) .firstElement().toObservable() .doOnNext(captureSessionData -> captureSessionData.session.close()) .flatMap(__ -> captureSessionClosedObservable) .doOnNext(cameraCaptureSession -> cameraCaptureSession.getDevice().close()) .flatMap(__ -> closeCameraObservable) .doOnNext(__ -> closeImageReader()) .subscribe(__ -> unsubscribe(), this::onError); 

, API.


結論


, preview . . . .


[ Update : ]


RxJava API. , Callback Hell , . !



Source: https://habr.com/ru/post/J330080/


All Articles