Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
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 Oct 24, 2018
f310580
dart side of byte streaming
bparrishMines Oct 25, 2018
87fdb60
Fix android streaming
bparrishMines Oct 25, 2018
673b003
Merge branch 'master' of github.com:flutter/plugins into camera_android
bparrishMines Nov 14, 2018
8d353ad
Add ios byte streaming
bparrishMines Nov 15, 2018
0291a29
Convert buffer to uiimage to pass over
bparrishMines Nov 16, 2018
31f746a
formatting
bparrishMines Nov 18, 2018
5fcfeb2
Stream yuv bytes instead
bparrishMines Nov 28, 2018
040d1ae
Make video format a constant
bparrishMines Nov 29, 2018
16d1d32
Pass back metadata for ios image
bparrishMines Nov 29, 2018
8e88978
Pass back metadata for android image
bparrishMines Nov 29, 2018
67f8304
Dart code now parses camera image buffer
bparrishMines Nov 30, 2018
646283a
YUV image to bgra
bparrishMines Nov 30, 2018
7e99691
Add documentation
bparrishMines Dec 6, 2018
4cbfab9
Only pass available data on Android
bparrishMines Dec 6, 2018
aa0e263
Merge branch 'master' of github.com:bparrishMines/plugins into camera…
bparrishMines Dec 6, 2018
9b2ae22
Merge branch 'master' of github.com:flutter/plugins into camera_andro…
bparrishMines Dec 6, 2018
297fe7a
Bump version
bparrishMines Dec 6, 2018
bdd9007
Formatting
bparrishMines Dec 6, 2018
ccc057b
create imageformat error
bparrishMines Dec 7, 2018
989edf6
Don't return from null
bparrishMines Dec 7, 2018
22ce601
Merge branch 'master' of github.com:bparrishMines/plugins into camera…
bparrishMines Dec 10, 2018
b1d7b89
Init buffers in constructor
bparrishMines Dec 10, 2018
aa3db0c
Add yuv ios format
bparrishMines Dec 11, 2018
0349ae2
Used presets with defined resolution. Sometimes resolution would come…
bparrishMines Dec 11, 2018
2633c49
Formatting
bparrishMines Dec 13, 2018
6377c64
Move CameraImage classes to separate file
bparrishMines Dec 13, 2018
6660a68
Move camera.dart to src folder
bparrishMines Dec 13, 2018
a4d278e
Create camera library
bparrishMines Dec 13, 2018
045dd53
Better name and comments
bparrishMines Dec 19, 2018
01520fe
Change from library camera file
bparrishMines Dec 19, 2018
6155620
bytestream -> imagestream
bparrishMines Dec 19, 2018
5898b4c
Comments and names
bparrishMines Dec 20, 2018
7617bb9
Formatting
bparrishMines Dec 20, 2018
f18db98
Added resolution and fps todo
bparrishMines Dec 20, 2018
0bf466a
Unmodify file
bparrishMines Dec 20, 2018
a53222b
Empty commit to rerun tests
bparrishMines Dec 20, 2018
fe965d1
Remove TODO from documentation
bparrishMines Dec 20, 2018
7c0228f
Merge branch 'master' of github.com:flutter/plugins into camera_andro…
bparrishMines Jan 3, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/camera/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.2.7

* Add byte streaming capability for the camera.

## 0.2.6

* Update the camera to use the physical device's orientation instead of the UI
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to separate readers because they use different formats.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be explained in a comment in the code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

private int sensorOrientation;
private boolean isFrontFacing;
private String cameraName;
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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) {
Expand All @@ -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(
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 =
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<>();
Expand All @@ -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();
Expand Down
24 changes: 4 additions & 20 deletions packages/camera/example/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,6 @@
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
FE224661708E6DA2A0F8B952 /* [CP] Embed Pods Frameworks */,
EACF0929FF12B6CC70C2D6BE /* [CP] Copy Pods Resources */,
);
buildRules = (
);
Expand All @@ -183,7 +182,7 @@
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
DevelopmentTeam = EQHXZ8M8AV;
DevelopmentTeam = S8QB4VV633;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think changes to this file should be checked in.

};
};
};
Expand Down Expand Up @@ -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 = (
Expand Down Expand Up @@ -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)",
Expand All @@ -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)",
Expand Down
Loading