Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
165 changes: 165 additions & 0 deletions CAMERA2_INTEGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
# Camera2 API Integration

## Overview

This document describes the Camera2 API integration that has been added to the android-gpuimage-plus library. The implementation provides a flexible camera backend system that supports both the legacy Camera API and the modern Camera2 API.

## Features

- **Backward Compatibility**: Existing code continues to work with the legacy Camera API by default
- **Camera2 Support**: Full Camera2 API support for devices with API level 21+
- **Runtime Switching**: Users can choose which camera backend to use through UI controls
- **Automatic Fallback**: Graceful fallback to legacy API when Camera2 is not available
- **Persistent Settings**: User camera backend preferences are saved and restored

## Architecture

### Core Components

1. **CameraBackend** - Abstract base class defining the camera interface
2. **CameraBackendLegacy** - Implementation wrapping the legacy Camera API
3. **CameraBackend2** - Implementation using the Camera2 API
4. **CameraBackendFactory** - Factory class for creating camera backends
5. **CameraInstance** - Modified singleton that now delegates to backends

### Backend Selection

The camera backend can be selected using:

```java
// Set Camera2 backend
CameraInstance.setCameraBackendType(CameraBackendFactory.CameraBackendType.CAMERA2);

// Set legacy backend
CameraInstance.setCameraBackendType(CameraBackendFactory.CameraBackendType.LEGACY);

// Automatic selection (Camera2 if available, otherwise legacy)
CameraInstance.setCameraBackendType(CameraBackendFactory.CameraBackendType.AUTO);
```

## Usage

### For Library Users

The library remains fully backward compatible. No changes are required for existing code.

To enable Camera2:

```java
// Initialize with context (required for Camera2)
CameraInstance.getInstance().initializeWithContext(context);

// Enable Camera2 backend
CameraInstance.setCameraBackendType(CameraBackendFactory.CameraBackendType.CAMERA2);

// Use camera normally
CameraInstance.getInstance().tryOpenCamera(callback);
```

### For Demo Application

The demo application now includes a configuration panel in MainActivity:

1. **Checkbox**: "Use Camera2 API" - toggles between Camera backends
2. **Status Display**: Shows current backend status and device compatibility
3. **Info Button**: Displays detailed runtime information

Settings are automatically saved and restored between app sessions.

## Implementation Details

### CameraBackend Interface

The `CameraBackend` abstract class defines a unified interface for camera operations:

- `tryOpenCamera()` - Open camera with specified facing
- `stopCamera()` - Close camera and cleanup
- `startPreview()` / `stopPreview()` - Control preview
- `setPictureSize()` / `setFocusMode()` - Configuration
- `focusAtPoint()` - Touch-to-focus functionality

### Legacy Compatibility

The `CameraBackendLegacy` class wraps the existing Camera API implementation, ensuring:

- All existing functionality is preserved
- No breaking changes to the API
- Same behavior as before the refactoring

### Camera2 Implementation

The `CameraBackend2` class provides:

- Modern Camera2 API usage with CameraDevice and CameraCaptureSession
- Background thread handling for camera operations
- Proper lifecycle management
- Permission checking
- Size selection and configuration

### Context Initialization

Camera2 requires a Context for CameraManager access. The library now supports context initialization:

```java
// Initialize once, typically in Application or Activity
CameraInstance.getInstance().initializeWithContext(context);
```

This is automatically handled in CameraGLSurfaceView constructor.

## Error Handling

The implementation includes comprehensive error handling:

- **Permission Errors**: Camera2 checks for camera permissions
- **Device Compatibility**: Automatic fallback when Camera2 is not supported
- **Camera Access Errors**: Proper cleanup on camera open failures
- **Lifecycle Errors**: Safe handling of Activity/Context lifecycle

## Testing

Use `CameraBackendTest` class for validation:

```java
// Run comprehensive backend tests
CameraBackendTest.testCameraBackends(context);

// Get runtime information
String info = CameraBackendTest.getRuntimeInfo(context);
```

## Benefits

1. **Future-Proof**: Ready for modern Android camera capabilities
2. **Better Performance**: Camera2 API provides better control and performance
3. **Enhanced Features**: Access to Camera2-specific features like manual controls
4. **Maintained Compatibility**: Existing applications continue to work unchanged
5. **User Choice**: Developers and users can choose the best backend for their needs

## Requirements

- **Minimum SDK**: API 21 (Android 5.0) for Camera2 features
- **Permissions**: CAMERA permission required (same as before)
- **Context**: Application context needed for Camera2 initialization

## Migration Guide

### For Existing Applications

No changes required - applications continue to work with legacy Camera API by default.

### To Enable Camera2

1. Ensure `initializeWithContext()` is called (automatic in CameraGLSurfaceView)
2. Set Camera2 backend type
3. Test on target devices
4. Consider providing user choice in settings

## Future Enhancements

The architecture supports future enhancements:

- Camera2 advanced features (manual controls, RAW capture, etc.)
- CameraX backend implementation
- Device-specific optimizations
- Feature detection and capabilities reporting
49 changes: 49 additions & 0 deletions UI_CHANGES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# UI Changes Visualization

## MainActivity UI Enhancement

The MainActivity now includes a Camera Configuration panel at the top:

```
┌─ MainActivity ──────────────────────────────┐
│ │
│ Camera Configuration │
│ ┌──────────────────────────────────────────┐ │
│ │ □ Use Camera2 API (requires API 21+) │ │
│ │ │ │
│ │ Camera2 availability: ✓ Supported │ │
│ │ (API XX) │ │
│ │ │ │
│ │ [Show Camera Backend Info] │ │
│ └──────────────────────────────────────────┘ │
│ │
│ Basic Image Filter Demo │
│ Advanced Image Filter Demo │
│ Image Deform Demo │
│ Camera Filter Demo │
│ Multi Input Demo │
│ Simple Player Demo │
│ Video Player Demo │
│ Face Tracking Demo │
│ Image Demo WithMatrix │
│ Test Cases │
│ │
└──────────────────────────────────────────────┘
```

## User Interaction Flow

1. **Checkbox Interaction**: Users can toggle between Camera APIs
2. **Status Display**: Real-time feedback on Camera2 availability
3. **Persistent Settings**: Preferences saved between app sessions
4. **Info Button**: Displays detailed backend information
5. **Toast Messages**: Feedback when settings change

## Technical Implementation

- Settings are stored in SharedPreferences
- Camera backend is automatically applied
- Graceful fallback for unsupported devices
- Comprehensive logging for debugging

This implementation provides a user-friendly way to test and compare both camera backends while maintaining full backward compatibility.
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_camera_demo);
PermissionUtil.verifyStoragePermissions(this);

// Log which camera backend is being used
Log.i(LOG_TAG, "=== CameraDemoActivity ===");
Log.i(LOG_TAG, "Camera backend configuration: " + CameraInstance.getCameraBackendInfo());
Log.i(LOG_TAG, "Camera2 supported: " + CameraInstance.isCamera2Supported());
Log.i(LOG_TAG, "Selected backend type: " + CameraInstance.getCameraBackendType());

// lastVideoPathFileName = FileUtil.getPathInPackage(CameraDemoActivity.this, true) + "/lastVideoPath.txt";
Button takePicBtn = (Button) findViewById(R.id.takePicBtn);
Button takeShotBtn = (Button) findViewById(R.id.takeShotBtn);
Expand Down
84 changes: 84 additions & 0 deletions cgeDemo/src/main/java/org/wysaid/cgeDemo/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
Expand All @@ -12,13 +13,18 @@
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import java.io.IOException;
import java.io.InputStream;

import org.wysaid.camera.CameraBackendFactory;
import org.wysaid.camera.CameraBackendTest;
import org.wysaid.camera.CameraInstance;
import org.wysaid.common.Common;
import org.wysaid.myUtils.MsgUtil;
import org.wysaid.myUtils.PermissionUtil;
Expand All @@ -28,6 +34,11 @@
public class MainActivity extends AppCompatActivity {

public static final String LOG_TAG = "wysaid";
private static final String PREFS_NAME = "CameraSettings";
private static final String CAMERA2_ENABLED_KEY = "camera2_enabled";

private CheckBox mCamera2CheckBox;
private TextView mCamera2StatusText;

public static final String EFFECT_CONFIGS[] = {
"",
Expand Down Expand Up @@ -242,6 +253,8 @@ protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

setupCameraConfiguration();

LinearLayout mLayout = (LinearLayout) findViewById(R.id.buttonLayout);

for (DemoClassDescription demo : mDemos) {
Expand All @@ -256,6 +269,77 @@ protected void onCreate(Bundle savedInstanceState) {
PermissionUtil.verifyStoragePermissions(this);
}

private void setupCameraConfiguration() {
mCamera2CheckBox = findViewById(R.id.camera2CheckBox);
mCamera2StatusText = findViewById(R.id.camera2StatusText);
Button showInfoButton = findViewById(R.id.showInfoButton);

// Check Camera2 availability
boolean isCamera2Supported = CameraInstance.isCamera2Supported();
String statusMessage = "Camera2 availability: " +
(isCamera2Supported ? "✓ Supported (API " + android.os.Build.VERSION.SDK_INT + ")" :
"✗ Not supported (API " + android.os.Build.VERSION.SDK_INT + " < 21)");
mCamera2StatusText.setText(statusMessage);

if (!isCamera2Supported) {
mCamera2CheckBox.setEnabled(false);
mCamera2CheckBox.setText("Use Camera2 API (not available on this device)");
}

// Load saved preference
SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
boolean camera2Enabled = prefs.getBoolean(CAMERA2_ENABLED_KEY, false);
mCamera2CheckBox.setChecked(camera2Enabled && isCamera2Supported);

// Apply saved setting
applyCameraSetting(camera2Enabled && isCamera2Supported);

// Set up checkbox listener
mCamera2CheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
applyCameraSetting(isChecked);
saveCameraSetting(isChecked);

// Show a toast message about the change
String message = isChecked ?
"Camera2 API enabled. Restart camera activities to take effect." :
"Legacy Camera API enabled. Restart camera activities to take effect.";
MsgUtil.toastMsg(MainActivity.this, message);
});

// Set up info button
showInfoButton.setOnClickListener(v -> {
String info = CameraBackendTest.getRuntimeInfo(MainActivity.this);
Log.i(LOG_TAG, "Camera Backend Info:\n" + info);
MsgUtil.toastMsg(MainActivity.this, "Camera backend info logged - check logcat");

// Also run the test
CameraBackendTest.testCameraBackends(MainActivity.this);
});
}

private void applyCameraSetting(boolean useCamera2) {
CameraBackendFactory.CameraBackendType backendType = useCamera2 ?
CameraBackendFactory.CameraBackendType.CAMERA2 :
CameraBackendFactory.CameraBackendType.LEGACY;

CameraInstance.setCameraBackendType(backendType);

String statusText = useCamera2 ?
"✓ Using Camera2 API backend" :
"Using Legacy Camera API backend";
Log.i(LOG_TAG, statusText);

// Update status text with more details
String detailedStatus = statusText + " | Backend info: " + CameraInstance.getCameraBackendInfo();
mCamera2StatusText.setText(detailedStatus);
}

private void saveCameraSetting(boolean useCamera2) {
SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
prefs.edit().putBoolean(CAMERA2_ENABLED_KEY, useCamera2).apply();
Log.i(LOG_TAG, "Camera setting saved: " + (useCamera2 ? "Camera2" : "Legacy"));
}

// @Override
// public void onDestroy() {
// super.onDestroy();
Expand Down
Loading