גישה משותפת למצלמה באמצעות ARCore

במדריך הזה למפתחים מוסבר איך להעניק גישה לאפליקציה אחרת בצורה חלקה בין שליטה בלעדית במצלמה דרך ממשק API של Android Camera2 ושיתוף הגישה למצלמה עם ARCore.

נושא זה מניח שאתם:

יצירה והפעלה של אפליקציה לדוגמה

כשאתם בונים ומריצים את האפליקציה לדוגמה של Shared Camera Java, היא יוצרת סשן ARCore שתומך בגישה למצלמה משותפת. האפליקציה מופעלת מחוץ ל-AR כש-ARCore מושהה.

כשהאפליקציה פועלת במצב שאינו AR, מציג המצלמה מציג צבע חום-ספיה ! כשעוברים למצב AR, אפקט הסלפיה מושבת בתור האפליקציה החזרת בקרת המצלמה ל-ARCore על ידי המשך הסשן שהושהה.

אפשר להשתמש במתג ה-AR באפליקציה כדי לשנות מצבים. במהלך התצוגה המקדימה, שני המצבים הצגת מספר הפריימים הרציפים שצולמו על ידי Camera2.

כדי ליצור ולהפעיל את האפליקציה לדוגמה של 'מצלמה משותפת' ב-Java:

  1. מורידים ומחלצים את Google ARCore SDK ל-Android.

  2. פותחים את פרויקט samples/shared_camera_java.

  3. צריך לוודא שמכשיר ה-Android מחובר למכונת הפיתוח באמצעות USB. לצפייה במכשירים נתמכים של ARCore לקבלת מידע מפורט.

  4. ב-Android Studio, לוחצים על Run .

  5. בוחרים את המכשיר כיעד הפריסה ולוחצים על OK כדי להפעיל את אפליקציה לדוגמה במכשיר.

  6. במכשיר, מאשרים שאתם רוצים לאפשר לאפליקציה לצלם תמונות להקליט סרטון.

  7. אם צריך לעשות זאת, צריך לעדכן או להתקין את הגרסה האחרונה של ARCore.

  8. אפשר להשתמש במתג AR כדי לעבור בין מצבים שאינם AR ו-AR.

סקירה כללית של הפעלת אפליקציה לשיתוף גישה למצלמה עם ARCore

כדי להטמיע גישה משותפת למצלמה באמצעות ARCore באפליקציה, צריך לפעול לפי השלבים הבאים. כל קטעי הקוד זמינים SharedCameraActivity.java בתוך shared_camera_java לדוגמה.

בקשת הרשאה ל-CAMERA

כדי להשתמש במצלמה של המכשיר, המשתמש חייבת להעניק לאפליקציה את ההרשאה CAMERA. דוגמאות ה-ARCore כוללות CameraPermissionHelper, שמספק כלי שירות כדי לבקש את ההרשאה המתאימה לאפליקציה.

Java

protected void onResume() {
  // Request the camera permission, if necessary.
  if (!CameraPermissionHelper.hasCameraPermission(this)) {
      CameraPermissionHelper.requestCameraPermission(this);
  }
}

Kotlin

override fun onResume() {
  // Request the camera permission, if necessary.
  if (!CameraPermissionHelper.hasCameraPermission(this)) {
    CameraPermissionHelper.requestCameraPermission(this)
  }
}

צריך לוודא ש-ARCore מותקן ועדכני

כדי להשתמש ב-ARCore צריכים להיות מותקנים ומעודכנים. בקטע הקוד הבא מוסבר איך לבקש התקנה של ARCore אם היא עדיין לא הותקנה במכשיר.

Java

boolean isARCoreSupportedAndUpToDate() {
  // Make sure that ARCore is installed and supported on this device.
  ArCoreApk.Availability availability = ArCoreApk.getInstance().checkAvailability(this);
  switch (availability) {
    case SUPPORTED_INSTALLED:
      return true;

    case SUPPORTED_APK_TOO_OLD:
    case SUPPORTED_NOT_INSTALLED:
        // Requests an ARCore installation or updates ARCore if needed.
        ArCoreApk.InstallStatus installStatus = ArCoreApk.getInstance().requestInstall(this, userRequestedInstall);
        switch (installStatus) {
          case INSTALL_REQUESTED:
            return false;
          case INSTALLED:
            return true;
        }
      return false;

    default:
      // Handle the error. For example, show the user a snackbar that tells them
      // ARCore is not supported on their device.
      return false;
  }
}

Kotlin

// Determine ARCore installation status.
// Requests an ARCore installation or updates ARCore if needed.
fun isARCoreSupportedAndUpToDate(): Boolean {
  when (ArCoreApk.getInstance().checkAvailability(this)) {
    Availability.SUPPORTED_INSTALLED -> return true

    Availability.SUPPORTED_APK_TOO_OLD,
    Availability.SUPPORTED_NOT_INSTALLED -> {
      when(ArCoreApk.getInstance().requestInstall(this, userRequestedInstall)) {
        InstallStatus.INSTALLED -> return true
        else -> return false
      }
    }

    else -> {
      // Handle the error. For example, show the user a snackbar that tells them
      // ARCore is not supported on their device.
      return false
    }
  }
}

יצירת סשן ARCore שתומך בשיתוף מצלמה

במסגרת התהליך הזה צריך ליצור את הסשן ולאחסן את קובץ העזר והמזהה של ARCore מצלמה משותפת:

Java

// Create an ARCore session that supports camera sharing.
sharedSession = new Session(this, EnumSet.of(Session.Feature.SHARED_CAMERA))

// Store the ARCore shared camera reference.
sharedCamera = sharedSession.getSharedCamera();

// Store the ID of the camera that ARCore uses.
cameraId = sharedSession.getCameraConfig().getCameraId();

Kotlin

// Create an ARCore session that supports camera sharing.
sharedSession = Session(this, EnumSet.of(Session.Feature.SHARED_CAMERA))

// Store the ARCore shared camera reference.
sharedCamera = sharedSession.sharedCamera

// Store the ID of the camera that ARCore uses.
cameraId = sharedSession.cameraConfig.cameraId

(אופציונלי) מידע על ARCore לגבי פלטפורמות בהתאמה אישית

בקשה לפלטפורמות נוספות בהתאמה אישית מגדילה את דרישות הביצועים של במכשיר. כדי להבטיח ביצועים טובים, יש לבדוק את האפליקציה במכשירים שבהם המשתמשים ישתמשו.

כברירת מחדל, תישלח בקשה מ-ARCore לשני שידורים:

  1. סטרימינג אחד של מעבד (CPU) מסוג YUV, כרגע תמיד 640x480.
    ב-ARCore נעשה שימוש בשידור הזה למעקב אחר תנועה.
  2. סטרימינג אחד של GPU, בדרך כלל 1920x1080
    שימוש ב-Session#getCameraConfig() כדי לקבוע את רזולוציית הסטרימינג הנוכחית ב-GPU.

אפשר לשנות את הרזולוציה של הסטרימינג ב-GPU במכשירים נתמכים באמצעות getSupportedCameraConfigs() וגם setCameraConfig()

כמדד כללי, אפשר לצפות ל:

סוג המכשיר תמיכה בשידורים חיים בו-זמנית
טלפונים מתקדמים
  • 2x שידורים חיים במעבד (CPU) מסוג YUV, למשל. 640x480 ו1920x1080
  • סטרימינג אחד של GPU, למשל. 1920x1080
  • תמונה אחת לפעמים ברזולוציה גבוהה ברזולוציה גבוהה (JPEG), למשל 12MP
טלפונים ברמה בינונית
  • 2x שידורים חיים במעבד (CPU) מסוג YUV, למשל. 640x480 ו1920x1080
  • סטרימינג אחד של GPU, למשל. 1920x1080
–או–
  • 1x שידורים של YUV CPU, למשל. 640x480 –או- 1920x1080
  • סטרימינג אחד של GPU, למשל. 1920x1080
  • תמונה אחת לפעמים ברזולוציה גבוהה ברזולוציה גבוהה (JPEG), למשל 12MP

כדי להשתמש בפלטפורמות מותאמות אישית, כמו קורא תמונות של המעבד (CPU), חשוב להוסיף אותו לרשימת הפלטפורמות שצריך לעדכן (לדוגמה, ImageReader).

Java

sharedCamera.setAppSurfaces(this.cameraId, Arrays.asList(imageReader.getSurface()));

Kotlin

sharedCamera.setAppSurfaces(this.cameraId, listOf(imageReader.surface))

פתיחת המצלמה

פותחים את המצלמה באמצעות קריאה חוזרת (callback) שעטופה ב-ARCore:

Java

// Wrap the callback in a shared camera callback.
CameraDevice.StateCallback wrappedCallback =
    sharedCamera.createARDeviceStateCallback(cameraDeviceCallback, backgroundHandler);

// Store a reference to the camera system service.
cameraManager = (CameraManager) this.getSystemService(Context.CAMERA_SERVICE);

// Open the camera device using the ARCore wrapped callback.
cameraManager.openCamera(cameraId, wrappedCallback, backgroundHandler);

Kotlin

// Wrap the callback in a shared camera callback.
val wrappedCallback = sharedCamera.createARDeviceStateCallback(cameraDeviceCallback, backgroundHandler)

// Store a reference to the camera system service.
val cameraManager = this.getSystemService(Context.CAMERA_SERVICE) as CameraManager

// Open the camera device using the ARCore wrapped callback.
cameraManager.openCamera(cameraId, wrappedCallback, backgroundHandler)

שימוש בקריאה החוזרת (callback) של מצב המצלמה

במצב 'קריאה חוזרת' במצב המצלמה, מאוחסנים הפניות למכשיר המצלמה. להתחיל סשן צילום חדש.

Java

public void onOpened(@NonNull CameraDevice cameraDevice) {
    Log.d(TAG, "Camera device ID " + cameraDevice.getId() + " opened.");
    SharedCameraActivity.this.cameraDevice = cameraDevice;
    createCameraPreviewSession();
}

Kotlin

fun onOpened(cameraDevice: CameraDevice) {
  Log.d(TAG, "Camera device ID " + cameraDevice.id + " opened.")
  this.cameraDevice = cameraDevice
  createCameraPreviewSession()
}

יצירת סשן חדש של צילום

יצירה של בקשת תיעוד חדשה. שימוש ב-TEMPLATE_RECORD כדי להבטיח שבקשת הלכידה תואמת ל-ARCore ולאפשר מעבר בין מצב AR למצב AR בזמן ריצה.

Java

void createCameraPreviewSession() {
  try {
    // Create an ARCore-compatible capture request using `TEMPLATE_RECORD`.
    previewCaptureRequestBuilder =
        cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);

    // Build a list of surfaces, starting with ARCore provided surfaces.
    List<Surface> surfaceList = sharedCamera.getArCoreSurfaces();

    // (Optional) Add a CPU image reader surface.
    surfaceList.add(cpuImageReader.getSurface());

    // The list should now contain three surfaces:
    // 0. sharedCamera.getSurfaceTexture()
    // 1. …
    // 2. cpuImageReader.getSurface()

    // Add ARCore surfaces and CPU image surface targets.
    for (Surface surface : surfaceList) {
      previewCaptureRequestBuilder.addTarget(surface);
    }

    // Wrap our callback in a shared camera callback.
    CameraCaptureSession.StateCallback wrappedCallback =
        sharedCamera.createARSessionStateCallback(cameraSessionStateCallback, backgroundHandler);

    // Create a camera capture session for camera preview using an ARCore wrapped callback.
    cameraDevice.createCaptureSession(surfaceList, wrappedCallback, backgroundHandler);
  } catch (CameraAccessException e) {
    Log.e(TAG, "CameraAccessException", e);
  }
}

Kotlin

fun createCameraPreviewSession() {
  try {
    // Create an ARCore-compatible capture request using `TEMPLATE_RECORD`.
    previewCaptureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD)

    // Build a list of surfaces, starting with ARCore provided surfaces.
    val surfaceList: MutableList<Surface> = sharedCamera.arCoreSurfaces

    // (Optional) Add a CPU image reader surface.
    surfaceList.add(cpuImageReader.getSurface())

    // The list should now contain three surfaces:
    // 0. sharedCamera.getSurfaceTexture()
    // 1. …
    // 2. cpuImageReader.getSurface()

    // Add ARCore surfaces and CPU image surface targets.
    for (surface in surfaceList) {
      previewCaptureRequestBuilder.addTarget(surface)
    }

    // Wrap the callback in a shared camera callback.
    val wrappedCallback = sharedCamera.createARSessionStateCallback(cameraSessionStateCallback, backgroundHandler)

    // Create a camera capture session for camera preview using an ARCore wrapped callback.
    cameraDevice.createCaptureSession(surfaceList, wrappedCallback, backgroundHandler)
  } catch (e: CameraAccessException) {
    Log.e(TAG, "CameraAccessException", e)
  }
}

התחלה במצב ללא AR או AR

כדי להתחיל לצלם פריימים, צריך להתקשר למספר captureSession.setRepeatingRequest() מפעילות צילום המצלמה onConfigured(). כדי להתחיל במצב AR, צריך להמשיך את הסשן של ARCore בתוך הקריאה החוזרת של onActive().

Java

// Repeating camera capture session state callback.
CameraCaptureSession.StateCallback cameraSessionStateCallback =
    new CameraCaptureSession.StateCallback() {

      // Called when ARCore first configures the camera capture session after
      // initializing the app, and again each time the activity resumes.
      @Override
      public void onConfigured(@NonNull CameraCaptureSession session) {
        captureSession = session;
        setRepeatingCaptureRequest();
      }

      @Override
      public void onActive(@NonNull CameraCaptureSession session) {
        if (arMode && !arcoreActive) {
          resumeARCore();
        }
      }
    };

// A repeating camera capture session capture callback.
CameraCaptureSession.CaptureCallback cameraCaptureCallback =
    new CameraCaptureSession.CaptureCallback() {
      @Override
      public void onCaptureCompleted(…) {
        shouldUpdateSurfaceTexture.set(true);
      }
    };

void setRepeatingCaptureRequest() {
    captureSession.setRepeatingRequest(
        previewCaptureRequestBuilder.build(), cameraCaptureCallback, backgroundHandler);
}

void resumeARCore() {
    // Resume ARCore.
    sharedSession.resume();
    arcoreActive = true;

    // Set the capture session callback while in AR mode.
    sharedCamera.setCaptureCallback(cameraCaptureCallback, backgroundHandler);
}

Kotlin

val cameraSessionStateCallback = object : CameraCaptureSession.StateCallback() {
      // Called when ARCore first configures the camera capture session after
      // initializing the app, and again each time the activity resumes.
  override fun onConfigured(session: CameraCaptureSession) {
    captureSession = session
    setRepeatingCaptureRequest()
  }

  override fun onActive(session: CameraCaptureSession) {
    if (arMode && !arcoreActive) {
      resumeARCore()
    }
  }
}

val cameraCaptureCallback = object : CameraCaptureSession.CaptureCallback() {
  override fun onCaptureCompleted(
    session: CameraCaptureSession,
    request: CaptureRequest,
    result: TotalCaptureResult
  ) {
    shouldUpdateSurfaceTexture.set(true);
  }
}

fun setRepeatingCaptureRequest() {
  captureSession.setRepeatingRequest(
    previewCaptureRequestBuilder.build(), cameraCaptureCallback, backgroundHandler
  )
}

fun resumeARCore() {
    // Resume ARCore.
    sharedSession.resume()
    arcoreActive = true

    // Set the capture session callback while in AR mode.
    sharedCamera.setCaptureCallback(cameraCaptureCallback, backgroundHandler)
}

מעבר חלק בין מצבים ללא AR או AR בזמן הריצה

כדי לעבור ממצב 'ללא AR' למצב AR ולהמשיך סשן ARCore מושהה:

Java

// Resume the ARCore session.
resumeARCore();

Kotlin

// Resume the ARCore session.
resumeARCore()

כדי לעבור ממצב AR למצב לא-AR:

Java

// Pause ARCore.
sharedSession.pause();

// Create the Camera2 repeating capture request.
setRepeatingCaptureRequest();

Kotlin

// Pause ARCore.
sharedSession.pause()

// Create the Camera2 repeating capture request.
setRepeatingCaptureRequest()