@@ -213,6 +213,26 @@ public void onMethodCall(MethodCall call, final Result result) {
213
213
camera .stopVideoRecording (result );
214
214
break ;
215
215
}
216
+ case "startImageStream" :
217
+ {
218
+ try {
219
+ camera .startPreviewWithImageStream ();
220
+ result .success (null );
221
+ } catch (CameraAccessException e ) {
222
+ result .error ("CameraAccess" , e .getMessage (), null );
223
+ }
224
+ break ;
225
+ }
226
+ case "stopImageStream" :
227
+ {
228
+ try {
229
+ camera .startPreview ();
230
+ result .success (null );
231
+ } catch (CameraAccessException e ) {
232
+ result .error ("CameraAccess" , e .getMessage (), null );
233
+ }
234
+ break ;
235
+ }
216
236
case "dispose" :
217
237
{
218
238
if (camera != null ) {
@@ -258,7 +278,8 @@ private class Camera {
258
278
private CameraDevice cameraDevice ;
259
279
private CameraCaptureSession cameraCaptureSession ;
260
280
private EventChannel .EventSink eventSink ;
261
- private ImageReader imageReader ;
281
+ private ImageReader pictureImageReader ;
282
+ private ImageReader imageStreamReader ;
262
283
private int sensorOrientation ;
263
284
private boolean isFrontFacing ;
264
285
private String cameraName ;
@@ -458,9 +479,15 @@ private void open(@Nullable final Result result) {
458
479
if (result != null ) result .error ("cameraPermission" , "Camera permission not granted" , null );
459
480
} else {
460
481
try {
461
- imageReader =
482
+ pictureImageReader =
462
483
ImageReader .newInstance (
463
484
captureSize .getWidth (), captureSize .getHeight (), ImageFormat .JPEG , 2 );
485
+
486
+ // Used to steam image byte data to dart side.
487
+ imageStreamReader =
488
+ ImageReader .newInstance (
489
+ previewSize .getWidth (), previewSize .getHeight (), ImageFormat .YUV_420_888 , 2 );
490
+
464
491
cameraManager .openCamera (
465
492
cameraName ,
466
493
new CameraDevice .StateCallback () {
@@ -553,7 +580,7 @@ private void takePicture(String filePath, @NonNull final Result result) {
553
580
return ;
554
581
}
555
582
556
- imageReader .setOnImageAvailableListener (
583
+ pictureImageReader .setOnImageAvailableListener (
557
584
new ImageReader .OnImageAvailableListener () {
558
585
@ Override
559
586
public void onImageAvailable (ImageReader reader ) {
@@ -571,7 +598,7 @@ public void onImageAvailable(ImageReader reader) {
571
598
try {
572
599
final CaptureRequest .Builder captureBuilder =
573
600
cameraDevice .createCaptureRequest (CameraDevice .TEMPLATE_STILL_CAPTURE );
574
- captureBuilder .addTarget (imageReader .getSurface ());
601
+ captureBuilder .addTarget (pictureImageReader .getSurface ());
575
602
captureBuilder .set (CaptureRequest .JPEG_ORIENTATION , getMediaOrientation ());
576
603
577
604
cameraCaptureSession .capture (
@@ -697,7 +724,7 @@ private void startPreview() throws CameraAccessException {
697
724
surfaces .add (previewSurface );
698
725
captureRequestBuilder .addTarget (previewSurface );
699
726
700
- surfaces .add (imageReader .getSurface ());
727
+ surfaces .add (pictureImageReader .getSurface ());
701
728
702
729
cameraDevice .createCaptureSession (
703
730
surfaces ,
@@ -727,6 +754,107 @@ public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession
727
754
null );
728
755
}
729
756
757
+ private void startPreviewWithImageStream () throws CameraAccessException {
758
+ closeCaptureSession ();
759
+
760
+ SurfaceTexture surfaceTexture = textureEntry .surfaceTexture ();
761
+ surfaceTexture .setDefaultBufferSize (previewSize .getWidth (), previewSize .getHeight ());
762
+
763
+ captureRequestBuilder =
764
+ cameraDevice .createCaptureRequest (CameraDevice .TEMPLATE_STILL_CAPTURE );
765
+
766
+ List <Surface > surfaces = new ArrayList <>();
767
+
768
+ Surface previewSurface = new Surface (surfaceTexture );
769
+ surfaces .add (previewSurface );
770
+ captureRequestBuilder .addTarget (previewSurface );
771
+
772
+ surfaces .add (imageStreamReader .getSurface ());
773
+ captureRequestBuilder .addTarget (imageStreamReader .getSurface ());
774
+
775
+ cameraDevice .createCaptureSession (
776
+ surfaces ,
777
+ new CameraCaptureSession .StateCallback () {
778
+ @ Override
779
+ public void onConfigured (@ NonNull CameraCaptureSession session ) {
780
+ if (cameraDevice == null ) {
781
+ sendErrorEvent ("The camera was closed during configuration." );
782
+ return ;
783
+ }
784
+ try {
785
+ cameraCaptureSession = session ;
786
+ captureRequestBuilder .set (
787
+ CaptureRequest .CONTROL_MODE , CameraMetadata .CONTROL_MODE_AUTO );
788
+ cameraCaptureSession .setRepeatingRequest (captureRequestBuilder .build (), null , null );
789
+ } catch (CameraAccessException e ) {
790
+ sendErrorEvent (e .getMessage ());
791
+ }
792
+ }
793
+
794
+ @ Override
795
+ public void onConfigureFailed (@ NonNull CameraCaptureSession cameraCaptureSession ) {
796
+ sendErrorEvent ("Failed to configure the camera for streaming images." );
797
+ }
798
+ },
799
+ null );
800
+
801
+ registerImageStreamEventChannel ();
802
+ }
803
+
804
+ private void registerImageStreamEventChannel () {
805
+ final EventChannel imageStreamChannel =
806
+ new EventChannel (registrar .messenger (), "plugins.flutter.io/camera/imageStream" );
807
+
808
+ imageStreamChannel .setStreamHandler (
809
+ new EventChannel .StreamHandler () {
810
+ @ Override
811
+ public void onListen (Object o , EventChannel .EventSink eventSink ) {
812
+ setImageStreamImageAvailableListener (eventSink );
813
+ }
814
+
815
+ @ Override
816
+ public void onCancel (Object o ) {
817
+ imageStreamReader .setOnImageAvailableListener (null , null );
818
+ }
819
+ });
820
+ }
821
+
822
+ private void setImageStreamImageAvailableListener (final EventChannel .EventSink eventSink ) {
823
+ imageStreamReader .setOnImageAvailableListener (
824
+ new ImageReader .OnImageAvailableListener () {
825
+ @ Override
826
+ public void onImageAvailable (final ImageReader reader ) {
827
+ Image img = reader .acquireLatestImage ();
828
+ if (img == null ) return ;
829
+
830
+ List <Map <String , Object >> planes = new ArrayList <>();
831
+ for (Image .Plane plane : img .getPlanes ()) {
832
+ ByteBuffer buffer = plane .getBuffer ();
833
+
834
+ byte [] bytes = new byte [buffer .remaining ()];
835
+ buffer .get (bytes , 0 , bytes .length );
836
+
837
+ Map <String , Object > planeBuffer = new HashMap <>();
838
+ planeBuffer .put ("bytesPerRow" , plane .getRowStride ());
839
+ planeBuffer .put ("bytesPerPixel" , plane .getPixelStride ());
840
+ planeBuffer .put ("bytes" , bytes );
841
+
842
+ planes .add (planeBuffer );
843
+ }
844
+
845
+ Map <String , Object > imageBuffer = new HashMap <>();
846
+ imageBuffer .put ("width" , img .getWidth ());
847
+ imageBuffer .put ("height" , img .getHeight ());
848
+ imageBuffer .put ("format" , img .getFormat ());
849
+ imageBuffer .put ("planes" , planes );
850
+
851
+ eventSink .success (imageBuffer );
852
+ img .close ();
853
+ }
854
+ },
855
+ null );
856
+ }
857
+
730
858
private void sendErrorEvent (String errorDescription ) {
731
859
if (eventSink != null ) {
732
860
Map <String , String > event = new HashMap <>();
@@ -750,9 +878,13 @@ private void close() {
750
878
cameraDevice .close ();
751
879
cameraDevice = null ;
752
880
}
753
- if (imageReader != null ) {
754
- imageReader .close ();
755
- imageReader = null ;
881
+ if (pictureImageReader != null ) {
882
+ pictureImageReader .close ();
883
+ pictureImageReader = null ;
884
+ }
885
+ if (imageStreamReader != null ) {
886
+ imageStreamReader .close ();
887
+ imageStreamReader = null ;
756
888
}
757
889
if (mediaRecorder != null ) {
758
890
mediaRecorder .reset ();
0 commit comments