This repository was archived by the owner on Feb 22, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 9.7k
Add byte streaming capability for the camera #965
Merged
Merged
Changes from 29 commits
Commits
Show all changes
39 commits
Select commit
Hold shift + click to select a range
c3f703e
Start of Android side of byte stream passing
bparrishMines f310580
dart side of byte streaming
bparrishMines 87fdb60
Fix android streaming
bparrishMines 673b003
Merge branch 'master' of github.com:flutter/plugins into camera_android
bparrishMines 8d353ad
Add ios byte streaming
bparrishMines 0291a29
Convert buffer to uiimage to pass over
bparrishMines 31f746a
formatting
bparrishMines 5fcfeb2
Stream yuv bytes instead
bparrishMines 040d1ae
Make video format a constant
bparrishMines 16d1d32
Pass back metadata for ios image
bparrishMines 8e88978
Pass back metadata for android image
bparrishMines 67f8304
Dart code now parses camera image buffer
bparrishMines 646283a
YUV image to bgra
bparrishMines 7e99691
Add documentation
bparrishMines 4cbfab9
Only pass available data on Android
bparrishMines aa0e263
Merge branch 'master' of github.com:bparrishMines/plugins into camera…
bparrishMines 9b2ae22
Merge branch 'master' of github.com:flutter/plugins into camera_andro…
bparrishMines 297fe7a
Bump version
bparrishMines bdd9007
Formatting
bparrishMines ccc057b
create imageformat error
bparrishMines 989edf6
Don't return from null
bparrishMines 22ce601
Merge branch 'master' of github.com:bparrishMines/plugins into camera…
bparrishMines b1d7b89
Init buffers in constructor
bparrishMines aa3db0c
Add yuv ios format
bparrishMines 0349ae2
Used presets with defined resolution. Sometimes resolution would come…
bparrishMines 2633c49
Formatting
bparrishMines 6377c64
Move CameraImage classes to separate file
bparrishMines 6660a68
Move camera.dart to src folder
bparrishMines a4d278e
Create camera library
bparrishMines 045dd53
Better name and comments
bparrishMines 01520fe
Change from library camera file
bparrishMines 6155620
bytestream -> imagestream
bparrishMines 5898b4c
Comments and names
bparrishMines 7617bb9
Formatting
bparrishMines f18db98
Added resolution and fps todo
bparrishMines 0bf466a
Unmodify file
bparrishMines a53222b
Empty commit to rerun tests
bparrishMines fe965d1
Remove TODO from documentation
bparrishMines 7c0228f
Merge branch 'master' of github.com:flutter/plugins into camera_andro…
bparrishMines File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -213,6 +213,26 @@ public void onMethodCall(MethodCall call, final Result result) { | |
| camera.stopVideoRecording(result); | ||
| break; | ||
| } | ||
| case "startByteStream": | ||
| { | ||
| try { | ||
| camera.startPreviewWithByteStream(); | ||
| result.success(null); | ||
| } catch (CameraAccessException e) { | ||
| result.error("CameraAccess", e.getMessage(), null); | ||
| } | ||
| break; | ||
| } | ||
| case "stopByteStream": | ||
| { | ||
| try { | ||
| camera.startPreview(); | ||
| result.success(null); | ||
| } catch (CameraAccessException e) { | ||
| result.error("CameraAccess", e.getMessage(), null); | ||
| } | ||
| break; | ||
| } | ||
| case "dispose": | ||
| { | ||
| if (camera != null) { | ||
|
|
@@ -258,7 +278,8 @@ private class Camera { | |
| private CameraDevice cameraDevice; | ||
| private CameraCaptureSession cameraCaptureSession; | ||
| private EventChannel.EventSink eventSink; | ||
| private ImageReader imageReader; | ||
| private ImageReader pictureImageReader; | ||
| private ImageReader byteImageReader; // Used to pass bytes to dart side. | ||
|
||
| private int sensorOrientation; | ||
| private boolean isFrontFacing; | ||
| private String cameraName; | ||
|
|
@@ -458,9 +479,13 @@ private void open(@Nullable final Result result) { | |
| if (result != null) result.error("cameraPermission", "Camera permission not granted", null); | ||
| } else { | ||
| try { | ||
| imageReader = | ||
| pictureImageReader = | ||
| ImageReader.newInstance( | ||
| captureSize.getWidth(), captureSize.getHeight(), ImageFormat.JPEG, 2); | ||
| byteImageReader = | ||
| ImageReader.newInstance( | ||
| previewSize.getWidth(), previewSize.getHeight(), ImageFormat.YUV_420_888, 2); | ||
|
|
||
| cameraManager.openCamera( | ||
| cameraName, | ||
| new CameraDevice.StateCallback() { | ||
|
|
@@ -553,7 +578,7 @@ private void takePicture(String filePath, @NonNull final Result result) { | |
| return; | ||
| } | ||
|
|
||
| imageReader.setOnImageAvailableListener( | ||
| pictureImageReader.setOnImageAvailableListener( | ||
| new ImageReader.OnImageAvailableListener() { | ||
| @Override | ||
| public void onImageAvailable(ImageReader reader) { | ||
|
|
@@ -571,7 +596,7 @@ public void onImageAvailable(ImageReader reader) { | |
| try { | ||
| final CaptureRequest.Builder captureBuilder = | ||
| cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); | ||
| captureBuilder.addTarget(imageReader.getSurface()); | ||
| captureBuilder.addTarget(pictureImageReader.getSurface()); | ||
| captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getMediaOrientation()); | ||
|
|
||
| cameraCaptureSession.capture( | ||
|
|
@@ -697,7 +722,7 @@ private void startPreview() throws CameraAccessException { | |
| surfaces.add(previewSurface); | ||
| captureRequestBuilder.addTarget(previewSurface); | ||
|
|
||
| surfaces.add(imageReader.getSurface()); | ||
| surfaces.add(pictureImageReader.getSurface()); | ||
|
|
||
| cameraDevice.createCaptureSession( | ||
| surfaces, | ||
|
|
@@ -727,6 +752,107 @@ public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession | |
| null); | ||
| } | ||
|
|
||
| private void startPreviewWithByteStream() throws CameraAccessException { | ||
| closeCaptureSession(); | ||
|
|
||
| SurfaceTexture surfaceTexture = textureEntry.surfaceTexture(); | ||
| surfaceTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight()); | ||
|
|
||
| captureRequestBuilder = | ||
| cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); | ||
|
|
||
| List<Surface> surfaces = new ArrayList<>(); | ||
|
|
||
| Surface previewSurface = new Surface(surfaceTexture); | ||
| surfaces.add(previewSurface); | ||
| captureRequestBuilder.addTarget(previewSurface); | ||
|
|
||
| surfaces.add(byteImageReader.getSurface()); | ||
| captureRequestBuilder.addTarget(byteImageReader.getSurface()); | ||
|
|
||
| cameraDevice.createCaptureSession( | ||
| surfaces, | ||
| new CameraCaptureSession.StateCallback() { | ||
| @Override | ||
| public void onConfigured(@NonNull CameraCaptureSession session) { | ||
| if (cameraDevice == null) { | ||
| sendErrorEvent("The camera was closed during configuration."); | ||
| return; | ||
| } | ||
| try { | ||
| cameraCaptureSession = session; | ||
| captureRequestBuilder.set( | ||
| CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO); | ||
| cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, null); | ||
| } catch (CameraAccessException e) { | ||
| sendErrorEvent(e.getMessage()); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) { | ||
| sendErrorEvent("Failed to configure the camera for streaming bytes."); | ||
| } | ||
| }, | ||
| null); | ||
|
|
||
| registerByteStreamEventChannel(); | ||
| } | ||
|
|
||
| private void registerByteStreamEventChannel() { | ||
| final EventChannel cameraChannel = | ||
bparrishMines marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| new EventChannel(registrar.messenger(), "plugins.flutter.io/camera/bytes"); | ||
|
|
||
| cameraChannel.setStreamHandler( | ||
| new EventChannel.StreamHandler() { | ||
| @Override | ||
| public void onListen(Object o, EventChannel.EventSink eventSink) { | ||
| setByteStreamImageAvailableListener(eventSink); | ||
| } | ||
|
|
||
| @Override | ||
| public void onCancel(Object o) { | ||
| byteImageReader.setOnImageAvailableListener(null, null); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| private void setByteStreamImageAvailableListener(final EventChannel.EventSink eventSink) { | ||
| byteImageReader.setOnImageAvailableListener( | ||
| new ImageReader.OnImageAvailableListener() { | ||
| @Override | ||
| public void onImageAvailable(final ImageReader reader) { | ||
| Image img = reader.acquireLatestImage(); | ||
| if (img == null) return; | ||
|
|
||
| List<Map<String, Object>> planes = new ArrayList<>(); | ||
| for (Image.Plane plane : img.getPlanes()) { | ||
| ByteBuffer buffer = plane.getBuffer(); | ||
|
|
||
| byte[] bytes = new byte[buffer.remaining()]; | ||
| buffer.get(bytes, 0, bytes.length); | ||
|
|
||
| Map<String, Object> planeBuffer = new HashMap<>(); | ||
| planeBuffer.put("bytesPerRow", plane.getRowStride()); | ||
| planeBuffer.put("bytesPerPixel", plane.getPixelStride()); | ||
| planeBuffer.put("bytes", bytes); | ||
|
|
||
| planes.add(planeBuffer); | ||
| } | ||
|
|
||
| Map<String, Object> imageBuffer = new HashMap<>(); | ||
| imageBuffer.put("width", img.getWidth()); | ||
| imageBuffer.put("height", img.getHeight()); | ||
| imageBuffer.put("format", img.getFormat()); | ||
| imageBuffer.put("planes", planes); | ||
|
|
||
| eventSink.success(imageBuffer); | ||
| img.close(); | ||
| } | ||
| }, | ||
| null); | ||
| } | ||
|
|
||
| private void sendErrorEvent(String errorDescription) { | ||
| if (eventSink != null) { | ||
| Map<String, String> event = new HashMap<>(); | ||
|
|
@@ -750,9 +876,13 @@ private void close() { | |
| cameraDevice.close(); | ||
| cameraDevice = null; | ||
| } | ||
| if (imageReader != null) { | ||
| imageReader.close(); | ||
| imageReader = null; | ||
| if (pictureImageReader != null) { | ||
| pictureImageReader.close(); | ||
| pictureImageReader = null; | ||
| } | ||
| if (byteImageReader != null) { | ||
| byteImageReader.close(); | ||
| byteImageReader = null; | ||
| } | ||
| if (mediaRecorder != null) { | ||
| mediaRecorder.reset(); | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -161,7 +161,6 @@ | |
| 9705A1C41CF9048500538489 /* Embed Frameworks */, | ||
| 3B06AD1E1E4923F5004D2608 /* Thin Binary */, | ||
| FE224661708E6DA2A0F8B952 /* [CP] Embed Pods Frameworks */, | ||
| EACF0929FF12B6CC70C2D6BE /* [CP] Copy Pods Resources */, | ||
| ); | ||
| buildRules = ( | ||
| ); | ||
|
|
@@ -183,7 +182,7 @@ | |
| TargetAttributes = { | ||
| 97C146ED1CF9000F007C117D = { | ||
| CreatedOnToolsVersion = 7.3.1; | ||
| DevelopmentTeam = EQHXZ8M8AV; | ||
| DevelopmentTeam = S8QB4VV633; | ||
|
||
| }; | ||
| }; | ||
| }; | ||
|
|
@@ -269,29 +268,14 @@ | |
| shellPath = /bin/sh; | ||
| shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; | ||
| }; | ||
| EACF0929FF12B6CC70C2D6BE /* [CP] Copy Pods Resources */ = { | ||
| isa = PBXShellScriptBuildPhase; | ||
| buildActionMask = 2147483647; | ||
| files = ( | ||
| ); | ||
| inputPaths = ( | ||
| ); | ||
| name = "[CP] Copy Pods Resources"; | ||
| outputPaths = ( | ||
| ); | ||
| runOnlyForDeploymentPostprocessing = 0; | ||
| shellPath = /bin/sh; | ||
| shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; | ||
| showEnvVarsInLog = 0; | ||
| }; | ||
| FE224661708E6DA2A0F8B952 /* [CP] Embed Pods Frameworks */ = { | ||
| isa = PBXShellScriptBuildPhase; | ||
| buildActionMask = 2147483647; | ||
| files = ( | ||
| ); | ||
| inputPaths = ( | ||
| "${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", | ||
| "${PODS_ROOT}/../../../../../../flutter/bin/cache/artifacts/engine/ios-release/Flutter.framework", | ||
| "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework", | ||
| ); | ||
| name = "[CP] Embed Pods Frameworks"; | ||
| outputPaths = ( | ||
|
|
@@ -433,7 +417,7 @@ | |
| buildSettings = { | ||
| ARCHS = arm64; | ||
| ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | ||
| DEVELOPMENT_TEAM = EQHXZ8M8AV; | ||
| DEVELOPMENT_TEAM = S8QB4VV633; | ||
| ENABLE_BITCODE = NO; | ||
| FRAMEWORK_SEARCH_PATHS = ( | ||
| "$(inherited)", | ||
|
|
@@ -456,7 +440,7 @@ | |
| buildSettings = { | ||
| ARCHS = arm64; | ||
| ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | ||
| DEVELOPMENT_TEAM = EQHXZ8M8AV; | ||
| DEVELOPMENT_TEAM = S8QB4VV633; | ||
| ENABLE_BITCODE = NO; | ||
| FRAMEWORK_SEARCH_PATHS = ( | ||
| "$(inherited)", | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.