Einführung in die ARCore Recording and Playback API

1. Einführung

Die Möglichkeit, ein AR-Erlebnis in einer MP4-Datei zu speichern und aus der MP4-Datei wiederzugeben, kann sowohl für App-Entwickler als auch für Endnutzer nützlich sein.

Von Ihrem Desktop aus Fehler beheben und neue Funktionen testen

Die einfachste Nutzung von ARCore Record Die Playback API ist für Entwickler gedacht. Die Zeiten, in denen Sie die App auf einem Testgerät erstellen und ausführen, das USB-Kabel trennen und herumgehen müssen, um nur eine kleine Codeänderung zu testen, sind vorbei. Jetzt müssen Sie nur noch eine MP4-Datei in der Testumgebung mit erwarteten Bewegungen des Smartphones aufnehmen und direkt von Ihrem Schreibtisch aus testen.

Auf verschiedenen Geräten aufzeichnen und wiedergeben

Mit den Aufzeichnungs- und Wiedergabe-APIs kann ein Nutzer eine Sitzung auf einem Gerät aufzeichnen und ein anderer dieselbe Sitzung auf einem anderen Gerät wiedergeben. Es ist möglich, ein AR-Erlebnis mit einem anderen Nutzer zu teilen. Es gibt viele Möglichkeiten!

Entwickeln Sie zum ersten Mal eine ARCore-App?

<ph type="x-smartling-placeholder"></ph> Nein. Ja.

Wie möchten Sie dieses Codelab nutzen?

<ph type="x-smartling-placeholder"></ph> Lesen Sie sie nur durch. Lies sie dir durch und absolviere die Übungen

Aufgaben

In diesem Codelab nutzen Sie die Wiedergabe-API zur Erstellung einer App, die sowohl ein AR-Erlebnis als MP4-Datei aufzeichnet als auch die Inhalte in derselben Datei wiedergibt. Informationen in diesem Dokument:

  • Hier erfahren Sie, wie Sie mit der Recording API eine AR-Sitzung in einer MP4-Datei speichern.
  • Hier erfährst du, wie du mithilfe der Playback API eine AR-Sitzung aus einer MP4-Datei abspielst.
  • Wie Sie eine AR-Sitzung auf einem Gerät aufzeichnen und auf einem anderen wiedergeben

Voraussetzungen

In diesem Codelab modifizieren Sie die Hello AR Java-App, die mit dem ARCore Android SDK erstellt wurde. Dafür brauchst du bestimmte Hardware und Software.

Hardwareanforderungen

Softwareanforderungen

Außerdem sollten Sie über Grundkenntnisse in ARCore verfügen, um optimale Ergebnisse zu erzielen.

2. Entwicklungsumgebung einrichten

Richten Sie zuerst Ihre Entwicklungsumgebung ein.

ARCore Android SDK herunterladen

Klicke auf , um das SDK herunterzuladen.

ARCore Android SDK entpacken

Nachdem Sie das Android SDK auf Ihren Computer heruntergeladen haben, entpacken Sie die Datei und rufen Sie das Verzeichnis arcore-android-sdk-1.24/samples/hello_ar_java auf. Dies ist das Stammverzeichnis der Anwendung, mit der Sie arbeiten werden.

hello-ar-java-extracted

Hello AR-Java in Android Studio laden

Starten Sie Android Studio und klicken Sie auf Open an bestehendes Android Studio project (Vorhandenes Android Studio-Projekt öffnen).

android-studio-open-projects

Wählen Sie im daraufhin angezeigten Dialogfeld arcore-android-sdk-1.24/samples/hello_ar_java aus und klicken Sie auf Öffnen.

Warten Sie, bis Android Studio die Synchronisierung des Projekts abgeschlossen hat. Wenn Komponenten fehlen, können beim Importieren des Projekts Fehlermeldungen angezeigt werden. Beheben Sie diese Probleme, bevor Sie fortfahren.

Beispiel-App ausführen

  1. Verbinden Sie ein von ARCore unterstütztes Gerät mit Ihrem Entwicklungscomputer.
  2. Wenn das Gerät ordnungsgemäß erkannt wird, sollte der Gerätename in Android Studio angezeigt werden. android-studio-pixel-5.png
  3. Klicken Sie auf die Schaltfläche Ausführen oder wählen Sie Ausführen > Führen Sie „app“ aus, um Android Studio zu installieren und die App auf dem Gerät zu starten. android-studio-run-button.png
  4. Sie werden gefragt, ob Sie Bilder und Videos aufnehmen möchten. Wählen Sie Bei Nutzung dieser App aus, um der App Kameraberechtigungen zu gewähren. Dann sehen Sie Ihre reale Umgebung auf dem Bildschirm des Geräts. hello-ar-java-permission
  5. Bewegen Sie das Gerät horizontal, um nach Flugzeugen zu suchen.
  6. Wenn die App ein Flugzeug erkennt, wird ein weißes Raster angezeigt. Tippe darauf, um eine Markierung auf dem Flugzeug zu setzen. Placement für Hello AR

In diesem Schritt durchgeführte Schritte

  • Hello AR Java-Projekt einrichten
  • Beispiel-App auf einem mit ARCore unterstützten Gerät erstellen und ausführen

Als Nächstes zeichnen Sie eine AR-Sitzung in einer MP4-Datei auf.

3. ARCore-Sitzung als MP4-Datei aufzeichnen

In diesem Schritt fügen wir die Aufzeichnungsfunktion hinzu. Sie besteht aus folgenden Komponenten:

  • Eine Schaltfläche zum Starten oder Stoppen der Aufzeichnung.
  • Speicherfunktionen zum Speichern der MP4-Datei auf dem Gerät
  • Anrufe, um die Aufzeichnung von ARCore-Sitzungen zu starten oder zu beenden.

Benutzeroberfläche für die Schaltfläche „Eintrag hinzufügen“

Bevor du die Aufzeichnung implementierst, füge auf der Benutzeroberfläche eine Schaltfläche hinzu, damit der Nutzer ARCore darüber informieren kann, wann die Aufzeichnung gestartet oder beendet werden soll.

Öffnen Sie im Projektbereich die Datei app/res/layout/activity_main.xml.

activity_main-xml-location-in-project

Standardmäßig verwendet Android Studio die Designansicht nach dem Öffnen der Datei app/res/layout/activity_main.xml. Klicken Sie rechts oben auf dem Tab auf die Schaltfläche Code, um zur Codeansicht zu wechseln.

swith-to-the-code-view.png

Fügen Sie in activity_main.xml den folgenden Code vor dem schließenden Tag ein, um die neue Schaltfläche Record zu erstellen und deren Event-Handler auf eine Methode namens onClickRecord() festzulegen:

  <!--
    Add a new "Record" button with those attributes:
        text is "Record",
        onClick event handler is "onClickRecord",
        text color is "red".
  -->
  <Button
      android:id="@+id/record_button"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_alignLeft="@id/surfaceview"
      android:layout_alignBottom="@id/surfaceview"
      android:layout_marginBottom="100dp"
      android:onClick="onClickRecord"
      android:text="Record"
      android:textColor="@android:color/holo_red_light" />

Nachdem Sie den obigen Code hinzugefügt haben, wird möglicherweise vorübergehend ein Fehler angezeigt: Corresponding method handler 'public void onClickRecord(android.view.View)' not found". Dies ist zu erwarten. Sie beheben den Fehler, indem Sie in den nächsten Schritten die Funktion onClickRecord() erstellen.

Text auf Schaltfläche basierend auf dem Status ändern

Über die Schaltfläche Aufzeichnen wird sowohl die Aufzeichnung als auch das Beenden der Aufzeichnung ausgeführt. Wenn die App keine Daten aufzeichnet, sollte das Wort „Aufzeichnen“ angezeigt werden. Wenn die App Daten aufzeichnet, sollte sich die Schaltfläche ändern und das Wort „Stopp“ anzeigen.

Um der Schaltfläche diese Funktion zur Verfügung zu stellen, muss die App ihren aktuellen Status kennen. Mit dem folgenden Code wird eine neue Aufzählung namens AppState erstellt, die den Arbeitsstatus der App darstellt. Außerdem werden bestimmte Statusänderungen über eine private Member-Variable namens appState verfolgt. Füge sie zu HelloArActivity.java am Anfang der Klasse HelloArActivity hinzu.

  // Represents the app's working state.
  public enum AppState {
    Idle,
    Recording
  }

  // Tracks app's specific state changes.
  private AppState appState = AppState.Idle;

Sie können jetzt den internen Status der App erfassen. Erstellen Sie dazu eine Funktion namens updateRecordButton(), die den Text der Schaltfläche entsprechend dem aktuellen Status der App ändert. Fügen Sie den folgenden Code in die Klasse HelloArActivity in HelloArActivity.java ein.

// Add imports to the beginning of the file.
import android.widget.Button;

  // Update the "Record" button based on app's internal state.
  private void updateRecordButton() {
    View buttonView = findViewById(R.id.record_button);
    Button button = (Button) buttonView;

    switch (appState) {
      case Idle:
        button.setText("Record");
        break;
      case Recording:
        button.setText("Stop");
        break;
    }
  }

Erstellen Sie als Nächstes die Methode onClickRecord(), die den Status der App prüft, zum nächsten Status wechselt und updateRecordButton() aufruft, um die Benutzeroberfläche der Schaltfläche zu ändern. Fügen Sie den folgenden Code in die Klasse HelloArActivity in HelloArActivity.java ein.

  // Handle the "Record" button click event.
  public void onClickRecord(View view) {
    Log.d(TAG, "onClickRecord");

    // Check the app's internal state and switch to the new state if needed.
    switch (appState) {
        // If the app is not recording, begin recording.
      case Idle: {
        boolean hasStarted = startRecording();
        Log.d(TAG, String.format("onClickRecord start: hasStarted %b", hasStarted));

        if (hasStarted)
          appState = AppState.Recording;

        break;
      }

      // If the app is recording, stop recording.
      case Recording: {
        boolean hasStopped = stopRecording();
        Log.d(TAG, String.format("onClickRecord stop: hasStopped %b", hasStopped));

        if (hasStopped)
          appState = AppState.Idle;

        break;
      }

      default:
        // Do nothing.
        break;
    }

    updateRecordButton();
  }

Aktiviere die App, um die Aufzeichnung zu starten

Sie müssen nur zwei Dinge tun, um mit der Aufzeichnung in ARCore zu beginnen:

  1. Geben Sie den URI der Aufnahmedatei in einem RecordingConfig-Objekt an.
  2. session.startRecording mit dem RecordingConfig-Objekt aufrufen

Der Rest ist nur Boilerplate-Code: Konfiguration, Protokollierung und Prüfung auf Richtigkeit.

Erstellen Sie eine neue Funktion namens startRecording(), die Daten aufzeichnet und in einem MP4-URI speichert. Fügen Sie den folgenden Code in die Klasse HelloArActivity in HelloArActivity.java ein.

// Add imports to the beginning of the file.
import android.net.Uri;
import com.google.ar.core.RecordingConfig;
import com.google.ar.core.RecordingStatus;
import com.google.ar.core.exceptions.RecordingFailedException;

  private boolean startRecording() {
    Uri mp4FileUri = createMp4File();
    if (mp4FileUri == null)
      return false;

    Log.d(TAG, "startRecording at: " + mp4FileUri);

    pauseARCoreSession();

    // Configure the ARCore session to start recording.
    RecordingConfig recordingConfig = new RecordingConfig(session)
        .setMp4DatasetUri(mp4FileUri)
        .setAutoStopOnPause(true);

    try {
      // Prepare the session for recording, but do not start recording yet.
      session.startRecording(recordingConfig);
    } catch (RecordingFailedException e) {
      Log.e(TAG, "startRecording - Failed to prepare to start recording", e);
      return false;
    }

    boolean canResume = resumeARCoreSession();
    if (!canResume)
      return false;

    // Correctness checking: check the ARCore session's RecordingState.
    RecordingStatus recordingStatus = session.getRecordingStatus();
    Log.d(TAG, String.format("startRecording - recordingStatus %s", recordingStatus));
    return recordingStatus == RecordingStatus.OK;
  }

Wenn du eine ARCore-Sitzung sicher pausieren und fortsetzen möchtest, erstelle pauseARCoreSession() und resumeARCoreSession() in HelloArActivity.java.

  private void pauseARCoreSession() {
    // Pause the GLSurfaceView so that it doesn't update the ARCore session.
    // Pause the ARCore session so that we can update its configuration.
    // If the GLSurfaceView is not paused,
    //   onDrawFrame() will try to update the ARCore session
    //   while it's paused, resulting in a crash.
    surfaceView.onPause();
    session.pause();
  }

  private boolean resumeARCoreSession() {
    // We must resume the ARCore session before the GLSurfaceView.
    // Otherwise, the GLSurfaceView will try to update the ARCore session.
    try {
      session.resume();
    } catch (CameraNotAvailableException e) {
      Log.e(TAG, "CameraNotAvailableException in resumeARCoreSession", e);
      return false;
    }

    surfaceView.onResume();
    return true;
  }

Zum Beenden der Aufnahme App aktivieren

Erstelle in HelloArActivity.java eine Funktion namens „stopRecording()“, um zu verhindern, dass deine App neue Daten aufzeichnet. Diese Funktion ruft session.stopRecording() auf und sendet einen Fehler an das Konsolenprotokoll, wenn die App die Aufzeichnung nicht beenden kann.

  private boolean stopRecording() {
    try {
      session.stopRecording();
    } catch (RecordingFailedException e) {
      Log.e(TAG, "stopRecording - Failed to stop recording", e);
      return false;
    }

    // Correctness checking: check if the session stopped recording.
    return session.getRecordingStatus() == RecordingStatus.NONE;
  }

Dateispeicher mit Android 11-Speicher entwerfen

Die speicherbezogenen Funktionen in diesem Codelab entsprechen den neuen Android 11-Speicheranforderungen.

Nimm ein paar kleine Änderungen in der Datei app/build.gradle vor, um sie auf Android 11 auszurichten. Im Projektbereich von Android Studio befindet sich diese Datei im Knoten Gradle Scripts, der mit dem Modul app verknüpft ist.

app-build.gradle.png

Ändern Sie compileSdkVersion und targetSdkVersion in 30.

    compileSdkVersion 30
    defaultConfig {
      targetSdkVersion 30
    }

Verwende für die Aufnahme die Android MediaStore API, um die MP4-Datei im freigegebenen Filmverzeichnis zu erstellen.

Erstellen Sie in HelloArActivity.java eine Funktion namens createMp4File():

// Add imports to the beginning of the file.
import java.text.SimpleDateFormat;
import android.content.ContentResolver;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore;
import android.content.ContentValues;
import java.io.File;
import android.content.CursorLoader;
import android.database.Cursor;
import java.util.Date;


  private final String MP4_VIDEO_MIME_TYPE = "video/mp4";

  private Uri createMp4File() {
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
    String mp4FileName = "arcore-" + dateFormat.format(new Date()) + ".mp4";

    ContentResolver resolver = this.getContentResolver();

    Uri videoCollection = null;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
      videoCollection = MediaStore.Video.Media.getContentUri(
          MediaStore.VOLUME_EXTERNAL_PRIMARY);
    } else {
      videoCollection = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
    }

    // Create a new Media file record.
    ContentValues newMp4FileDetails = new ContentValues();
    newMp4FileDetails.put(MediaStore.Video.Media.DISPLAY_NAME, mp4FileName);
    newMp4FileDetails.put(MediaStore.Video.Media.MIME_TYPE, MP4_VIDEO_MIME_TYPE);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
      // The Relative_Path column is only available since API Level 29.
      newMp4FileDetails.put(MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_MOVIES);
    } else {
      // Use the Data column to set path for API Level <= 28.
      File mp4FileDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES);
      String absoluteMp4FilePath = new File(mp4FileDir, mp4FileName).getAbsolutePath();
      newMp4FileDetails.put(MediaStore.Video.Media.DATA, absoluteMp4FilePath);
    }

    Uri newMp4FileUri = resolver.insert(videoCollection, newMp4FileDetails);

    // Ensure that this file exists and can be written.
    if (newMp4FileUri == null) {
      Log.e(TAG, String.format("Failed to insert Video entity in MediaStore. API Level = %d", Build.VERSION.SDK_INT));
      return null;
    }

    // This call ensures the file exist before we pass it to the ARCore API.
    if (!testFileWriteAccess(newMp4FileUri)) {
      return null;
    }

    Log.d(TAG, String.format("createMp4File = %s, API Level = %d", newMp4FileUri, Build.VERSION.SDK_INT));

    return newMp4FileUri;
  }

  // Test if the file represented by the content Uri can be open with write access.
  private boolean testFileWriteAccess(Uri contentUri) {
    try (java.io.OutputStream mp4File = this.getContentResolver().openOutputStream(contentUri)) {
      Log.d(TAG, String.format("Success in testFileWriteAccess %s", contentUri.toString()));
      return true;
    } catch (java.io.FileNotFoundException e) {
      Log.e(TAG, String.format("FileNotFoundException in testFileWriteAccess %s", contentUri.toString()), e);
    } catch (java.io.IOException e) {
      Log.e(TAG, String.format("IOException in testFileWriteAccess %s", contentUri.toString()), e);
    }

    return false;
  }

Speicherberechtigungen verwalten

Wenn du ein Android 11-Gerät verwendest, kannst du mit dem Testen des Codes beginnen. Wenn Sie Geräte mit Android 10 oder niedriger unterstützen möchten, müssen Sie dem App-Speicher Berechtigungen erteilen, damit Daten im Dateisystem des Zielgeräts gespeichert werden können.

Deklariere in AndroidManifest.xml, dass die App vor Android 11 (API-Level 30) Lese- und Schreibberechtigungen für Speicher benötigt.

  <!-- Inside the <manifest> tag, below the existing Camera permission -->
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
      android:maxSdkVersion="29" />

  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
      android:maxSdkVersion="29" />

Fügen Sie eine Hilfsfunktion namens checkAndRequestStoragePermission() in HelloArActivity.java hinzu, um die WRITE_EXTERNAL_STORAGE-Berechtigungen während der Laufzeit anzufordern.

// Add imports to the beginning of the file.
import android.Manifest;
import android.content.pm.PackageManager;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

  private final int REQUEST_WRITE_EXTERNAL_STORAGE = 1;
  public boolean checkAndRequestStoragePermission() {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
        != PackageManager.PERMISSION_GRANTED) {
      ActivityCompat.requestPermissions(this,
          new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
          REQUEST_WRITE_EXTERNAL_STORAGE);
      return false;
    }

    return true;
  }

Wenn Sie API-Level 29 oder niedriger verwenden, fügen Sie oben in createMp4File() eine Prüfung auf Speicherberechtigungen hinzu und beenden Sie die Funktion vorzeitig, wenn die App nicht die richtigen Berechtigungen hat. API-Level 30 (Android 11) erfordert keine Speicherberechtigung für den Zugriff auf Dateien im MediaStore.

  private Uri createMp4File() {
    // Since we use legacy external storage for Android 10,
    // we still need to request for storage permission on Android 10.
    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
      if (!checkAndRequestStoragePermission()) {
        Log.i(TAG, String.format(
            "Didn't createMp4File. No storage permission, API Level = %d",
            Build.VERSION.SDK_INT));
        return null;
      }
    }
    // ... omitted code ...
  }

Vom Zielgerät aufzeichnen

Sehen wir uns nun an, was Sie bisher entwickelt haben. Verbinden Sie Ihr Mobilgerät mit dem Entwicklungscomputer und klicken Sie in Android Studio auf Ausführen.

Links unten auf dem Bildschirm sollte eine rote Schaltfläche Aufzeichnen zu sehen sein. Wenn Sie darauf tippen, sollte sich der Text in Stopp ändern. Bewegen Sie Ihr Gerät, um eine Sitzung aufzuzeichnen, und klicken Sie auf die Schaltfläche Beenden, wenn Sie die Aufzeichnung abschließen möchten. Dadurch sollte eine neue Datei mit dem Namen arcore-xxxxxx_xxxxxx.mp4 im externen Speicher deines Geräts gespeichert werden.

record-button.png

Jetzt sollte sich eine neue arcore-xxxxxx_xxxxxx.mp4-Datei im externen Speicher deines Geräts befinden. Auf Pixel 5-Geräten ist der Pfad /storage/emulated/0/Movies/. Der Pfad wird nach dem Start einer Aufzeichnung im Logcat-Fenster angezeigt.

com.google.ar.core.examples.java.helloar D/HelloArActivity: startRecording at:/storage/emulated/0/Movies/arcore-xxxxxxxx_xxxxxx.mp4
com.google.ar.core.examples.java.helloar D/HelloArActivity: startRecording - RecordingStatus OK

Aufzeichnung ansehen

Sie können eine Dateisystem-App wie Files by Google verwenden, um sich die Aufzeichnung anzusehen oder sie auf Ihren Entwicklungscomputer zu kopieren. Nachfolgend sind die beiden ADB-Befehle zum Auflisten und Abrufen von Dateien auf dem Android-Gerät aufgeführt:

  • adb shell ls '$EXTERNAL_STORAGE/Movies/*', um die Dateien im Verzeichnis „Filme“ im externen Speicher auf dem Gerät anzuzeigen
  • adb pull /absolute_path_from_previous_adb_shell_ls/arcore-xxxxxxxx_xxxxxx.mp4, um die Datei vom Gerät auf den Entwicklungscomputer zu kopieren

Dies ist eine Beispielausgabe nach Verwendung dieser beiden Befehle (von macOS):

$ adb shell ls '$EXTERNAL_STORAGE/Movies/*'
/sdcard/Movies/arcore-xxxxxxxx_xxxxxx.mp4


$ adb pull /sdcard/Movies/arcore-xxxxxxxx_xxxxxx.mp4
/sdcard/Movies/arcore-xxxxxxxx_xxxxxx.mp4: ... pulled

In diesem Schritt durchgeführte Schritte

  • Schaltfläche zum Starten und Beenden der Aufnahme hinzugefügt
  • Implementierung von Funktionen zum Starten und Beenden der Aufzeichnung
  • Die App wurde auf dem Gerät getestet.
  • Die MP4-Datei wurde auf deinen Computer kopiert und verifiziert.

Als Nächstes spielen Sie eine AR-Sitzung aus einer MP4-Datei ab.

4. ARCore-Sitzung aus einer MP4-Datei wiedergeben

Sie haben jetzt die Schaltfläche Aufzeichnen und einige MP4-Dateien mit aufgezeichneten Sitzungen. Jetzt kannst du sie mithilfe der ARCore Playback API wiedergeben.

Benutzeroberfläche für Wiedergabeschaltfläche hinzufügen

Bevor du die Wiedergabe implementierst, füge der Benutzeroberfläche eine Schaltfläche hinzu, damit der Nutzer ARCore darüber informieren kann, wann die Wiedergabe beginnen und die Wiedergabe beendet werden soll.

Öffnen Sie im Bereich Projekt die Datei app/res/layout/activity_main.xml.

activity_main-xml-location-in-project

Fügen Sie in activity_main.xml den unten stehenden Code vor dem schließenden Tag ein, um die neue Wiedergabe-Schaltfläche zu erstellen und deren Event-Handler auf eine Methode namens onClickPlayback() festzulegen. Diese Schaltfläche ähnelt der Schaltfläche Aufzeichnen und erscheint rechts auf dem Bildschirm.

  <!--
    Add a new "Playback" button with those attributes:
        text is "Playback",
        onClick event handler is "onClickPlayback",
        text color is "green".
  -->
  <Button
      android:id="@+id/playback_button"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_alignEnd="@id/surfaceview"
      android:layout_alignBottom="@id/surfaceview"
      android:layout_marginBottom="100dp"
      android:onClick="onClickPlayback"
      android:text="Playback"
      android:textColor="@android:color/holo_green_light" />

Schaltflächen während der Wiedergabe aktualisieren

Die App hat jetzt einen neuen Status namens Playingback. Aktualisieren Sie dazu die AppState-Enum und alle vorhandenen Funktionen, die appState als Argument verwenden.

Fügen Sie Playingback zur AppState-Enum in HelloArActivity.java hinzu:

  public enum AppState {
    Idle,
    Recording,
    Playingback // New enum value.
  }

Wenn die Schaltfläche Aufnehmen während der Wiedergabe noch immer auf dem Bildschirm zu sehen ist, kann es passieren, dass der Nutzer versehentlich darauf klickt. Blende die Schaltfläche Aufzeichnen während der Wiedergabe aus, um dies zu vermeiden. Auf diese Weise müssen Sie den Status Playingback in onClickRecord() nicht verarbeiten.

Verändere die Funktion updateRecordButton() in HelloArActivity.java so, dass die Schaltfläche Aufzeichnen ausgeblendet wird, wenn die App den Status Playingback hat.

  // Update the "Record" button based on app's internal state.
  private void updateRecordButton() {
    View buttonView = findViewById(R.id.record_button);
    Button button = (Button)buttonView;

    switch (appState) {

      // The app is neither recording nor playing back. The "Record" button is visible.
      case Idle:
        button.setText("Record");
        button.setVisibility(View.VISIBLE);
        break;

      // While recording, the "Record" button is visible and says "Stop".
      case Recording:
        button.setText("Stop");
        button.setVisibility(View.VISIBLE);
        break;

      // During playback, the "Record" button is not visible.
      case Playingback:
        button.setVisibility(View.INVISIBLE);
        break;
    }
  }

Ebenso können Sie die Schaltfläche Wiedergabe ausblenden, wenn der Nutzer eine Sitzung aufzeichnet. Ändern Sie sie in „Stopp“. Der Nutzer spielt eine Sitzung aktiv ab. Auf diese Weise können sie die Wiedergabe anhalten, ohne warten zu müssen, bis sie von selbst abgeschlossen wird.

Fügen Sie eine updatePlaybackButton()-Funktion in HelloArActivity.java hinzu:

  // Update the "Playback" button based on app's internal state.
  private void updatePlaybackButton() {
    View buttonView = findViewById(R.id.playback_button);
    Button button = (Button)buttonView;

    switch (appState) {

      // The app is neither recording nor playing back. The "Playback" button is visible.
      case Idle:
        button.setText("Playback");
        button.setVisibility(View.VISIBLE);
        break;

      // While playing back, the "Playback" button is visible and says "Stop".
      case Playingback:
        button.setText("Stop");
        button.setVisibility(View.VISIBLE);
        break;

      // During recording, the "Playback" button is not visible.
      case Recording:
        button.setVisibility(View.INVISIBLE);
        break;
    }
  }

Aktualisieren Sie onClickRecord(), um updatePlaybackButton() anzurufen. Fügen Sie HelloArActivity.java die folgende Zeile hinzu:

  public void onClickRecord(View view) {
    // ... omitted code ...
    updatePlaybackButton(); // Add this line to the end of the function.
  }

Datei über die Wiedergabeschaltfläche auswählen

Wenn der Nutzer auf die Schaltfläche Wiedergabe tippt, sollte er eine Datei für die Wiedergabe auswählen können. Unter Android erfolgt die Dateiauswahl in der Systemdateiauswahl in einer anderen Aktivität. Dies geschieht über das Storage Access Framework (SAF). Sobald der Nutzer eine Datei ausgewählt hat, erhält die App einen Callback namens onActivityResult(). Sie starten die eigentliche Wiedergabe innerhalb dieser Callback-Funktion.

Erstellen Sie in HelloArActivity.java eine onClickPlayback()-Funktion, um die Datei auszuwählen und die Wiedergabe zu beenden.

  // Handle the click event of the "Playback" button.
  public void onClickPlayback(View view) {
    Log.d(TAG, "onClickPlayback");

    switch (appState) {

      // If the app is not playing back, open the file picker.
      case Idle: {
        boolean hasStarted = selectFileToPlayback();
        Log.d(TAG, String.format("onClickPlayback start: selectFileToPlayback %b", hasStarted));
        break;
      }

      // If the app is playing back, stop playing back.
      case Playingback: {
        boolean hasStopped = stopPlayingback();
        Log.d(TAG, String.format("onClickPlayback stop: hasStopped %b", hasStopped));
        break;
      }

      default:
        // Recording - do nothing.
        break;
    }

    // Update the UI for the "Record" and "Playback" buttons.
    updateRecordButton();
    updatePlaybackButton();
  }

Erstellen Sie in HelloArActivity.java eine selectFileToPlayback()-Funktion, die eine Datei auf dem Gerät auswählt. Verwenden Sie einen ACTION_OPEN_DOCUMENT-Intent, um eine Datei aus dem Android-Dateisystem auszuwählen.

// Add imports to the beginning of the file.
import android.content.Intent;
import android.provider.DocumentsContract;

  private boolean selectFileToPlayback() {
    // Start file selection from Movies directory.
    // Android 10 and above requires VOLUME_EXTERNAL_PRIMARY to write to MediaStore.
    Uri videoCollection;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
      videoCollection = MediaStore.Video.Media.getContentUri(
          MediaStore.VOLUME_EXTERNAL_PRIMARY);
    } else {
      videoCollection = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
    }

    // Create an Intent to select a file.
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);

    // Add file filters such as the MIME type, the default directory and the file category.
    intent.setType(MP4_VIDEO_MIME_TYPE); // Only select *.mp4 files
    intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, videoCollection); // Set default directory
    intent.addCategory(Intent.CATEGORY_OPENABLE); // Must be files that can be opened

    this.startActivityForResult(intent, REQUEST_MP4_SELECTOR);

    return true;
  }

REQUEST_MP4_SELECTOR ist eine Konstante zur Identifizierung dieser Anfrage. Sie können ihn mithilfe eines beliebigen Platzhalterwerts innerhalb von HelloArActivity in HelloArActivity.java definieren:

  private int REQUEST_MP4_SELECTOR = 1;

Überschreiben Sie die onActivityResult()-Funktion in HelloArActivity.java, um den Callback aus der Dateiauswahl zu verarbeiten.

  // Begin playback once the user has selected the file.
  @Override
  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // Check request status. Log an error if the selection fails.
    if (resultCode != android.app.Activity.RESULT_OK || requestCode != REQUEST_MP4_SELECTOR) {
      Log.e(TAG, "onActivityResult select file failed");
      return;
    }

    Uri mp4FileUri = data.getData();
    Log.d(TAG, String.format("onActivityResult result is %s", mp4FileUri));

    // Begin playback.
    startPlayingback(mp4FileUri);
  }

Aktiviere die App, um die Wiedergabe zu starten

Für eine ARCore-Sitzung sind drei API-Aufrufe zur Wiedergabe einer MP4-Datei erforderlich:

  1. session.pause()
  2. session.setPlaybackDataset()
  3. session.resume()

Erstellen Sie in HelloArActivity.java die Funktion startPlayingback().

// Add imports to the beginning of the file.
import com.google.ar.core.PlaybackStatus;
import com.google.ar.core.exceptions.PlaybackFailedException;

  private boolean startPlayingback(Uri mp4FileUri) {
    if (mp4FileUri == null)
      return false;

    Log.d(TAG, "startPlayingback at:" + mp4FileUri);

    pauseARCoreSession();

    try {
      session.setPlaybackDatasetUri(mp4FileUri);
    } catch (PlaybackFailedException e) {
      Log.e(TAG, "startPlayingback - setPlaybackDataset failed", e);
    }

    // The session's camera texture name becomes invalid when the
    // ARCore session is set to play back.
    // Workaround: Reset the Texture to start Playback
    // so it doesn't crashes with AR_ERROR_TEXTURE_NOT_SET.
    hasSetTextureNames = false;

    boolean canResume = resumeARCoreSession();
    if (!canResume)
      return false;

    PlaybackStatus playbackStatus = session.getPlaybackStatus();
    Log.d(TAG, String.format("startPlayingback - playbackStatus %s", playbackStatus));


    if (playbackStatus != PlaybackStatus.OK) { // Correctness check
      return false;
    }

    appState = AppState.Playingback;
    updateRecordButton();
    updatePlaybackButton();

    return true;
  }

App aktivieren, um die Wiedergabe zu beenden

Erstellen Sie eine Funktion mit dem Namen stopPlayingback() in HelloArActivity.java, um die App-Statusänderungen nach folgendem Datum zu verarbeiten:

  1. Die MP4-Wiedergabe wurde vom Nutzer gestoppt.
  2. Die MP4-Wiedergabe wird von alleine abgeschlossen.

Wenn der Nutzer die Wiedergabe angehalten hat, sollte die App in den Zustand zurückversetzt werden, in dem sie sich beim ersten Start befand.

  // Stop the current playback, and restore app status to Idle.
  private boolean stopPlayingback() {
    // Correctness check, only stop playing back when the app is playing back.
    if (appState != AppState.Playingback)
      return false;

    pauseARCoreSession();

    // Close the current session and create a new session.
    session.close();
    try {
      session = new Session(this);
    } catch (UnavailableArcoreNotInstalledException
        |UnavailableApkTooOldException
        |UnavailableSdkTooOldException
        |UnavailableDeviceNotCompatibleException e) {
      Log.e(TAG, "Error in return to Idle state. Cannot create new ARCore session", e);
      return false;
    }
    configureSession();

    boolean canResume = resumeARCoreSession();
    if (!canResume)
      return false;

    // A new session will not have a camera texture name.
    // Manually set hasSetTextureNames to false to trigger a reset.
    hasSetTextureNames = false;

    // Reset appState to Idle, and update the "Record" and "Playback" buttons.
    appState = AppState.Idle;
    updateRecordButton();
    updatePlaybackButton();

    return true;
  }

Eine Wiedergabe kann auch gestoppt werden, wenn der Player das Ende der MP4-Datei erreicht hat. In diesem Fall sollte der App-Status von stopPlayingback() wieder auf Idle zurückgesetzt werden. Sehen Sie in onDrawFrame() nach: PlaybackStatus. Wenn es sich um FINISHED handelt, rufen Sie die Funktion stopPlayingback() im UI-Thread auf.

  public void onDrawFrame(SampleRender render) {
      // ... omitted code ...

      // Insert before this line:
      // frame = session.update();

      // Check the playback status and return early if playback reaches the end.
      if (appState == AppState.Playingback
          && session.getPlaybackStatus() == PlaybackStatus.FINISHED) {
        this.runOnUiThread(this::stopPlayingback);
        return;
      }

      // ... omitted code ...
  }

Ab dem Zielgerät wiedergeben

Sehen wir uns nun an, was Sie bisher entwickelt haben. Verbinden Sie Ihr Mobilgerät mit dem Entwicklungscomputer und klicken Sie in Android Studio auf Ausführen.

Nach dem Starten der App sollten Sie einen Bildschirm mit einer roten Aufnahme-Schaltfläche auf der linken Seite und einer grünen Wiedergabe-Schaltfläche auf der rechten Seite sehen.

playback-button.png

Tippe auf die Schaltfläche Wiedergabe und wähle eine der MP4-Dateien aus, die du gerade aufgenommen hast. Wenn keine Dateinamen mit arcore- angezeigt werden, wird der Ordner Filme auf Ihrem Gerät möglicherweise nicht angezeigt. Navigieren Sie in diesem Fall zum Phone model > Filme über das Menü oben links. Möglicherweise müssen Sie auch die Option Internen Speicher anzeigen aktivieren, damit der Ordner mit den Telefonmodellen angezeigt wird.

show-internal-storage-button.png

nativate-to-movies-file-picker.jpg

Tippen Sie auf dem Bildschirm auf einen Dateinamen, um die MP4-Datei auszuwählen. Die App sollte die MP4-Datei wiedergeben.

playback-stop-button.png

Der Unterschied zwischen der Wiedergabe einer Sitzung und der Wiedergabe eines gewöhnlichen Videos besteht darin, dass Sie mit der aufgezeichneten Sitzung interagieren können. Tippe auf ein erkanntes Flugzeug, um Markierungen auf dem Bildschirm zu platzieren.

Wiedergabeplatzierung

In diesem Schritt durchgeführte Schritte

  • Schaltfläche zum Starten und Stoppen der Wiedergabe hinzugefügt
  • Implementierung einer Funktion zum Starten und Beenden der App-Aufzeichnung
  • Eine zuvor aufgezeichnete ARCore-Sitzung auf dem Gerät abgespielt

5. Zusätzliche Daten in der MP4-Datei aufzeichnen

Mit ARCore 1.24 ist es möglich, zusätzliche Informationen in der MP4-Datei aufzuzeichnen. Sie können die Pose von AR-Objekt-Placements aufzeichnen und dann während der Wiedergabe die AR-Objekte an demselben Ort erstellen.

Neuen Track für die Aufzeichnung konfigurieren

Definieren Sie einen neuen Track mit einer UUID und einem MIME-Tag in HelloArActivity.java.

// Add imports to the beginning of the file.
import java.util.UUID;
import com.google.ar.core.Track;

  // Inside the HelloArActiity class.
  private static final UUID ANCHOR_TRACK_ID = UUID.fromString("53069eb5-21ef-4946-b71c-6ac4979216a6");;
  private static final String ANCHOR_TRACK_MIME_TYPE = "application/recording-playback-anchor";

  private boolean startRecording() {
    // ... omitted code ...

    // Insert after line:
    //   pauseARCoreSession();

    // Create a new Track, with an ID and MIME tag.
    Track anchorTrack = new Track(session)
        .setId(ANCHOR_TRACK_ID).
        .setMimeType(ANCHOR_TRACK_MIME_TYPE);
    // ... omitted code ...
  }

Aktualisieren Sie den Exit-Code, um das RecordingConfig-Objekt mit einem Aufruf von addTrack() zu erstellen.

  private boolean startRecording() {
    // ... omitted code ...

    // Update the lines below with a call to the addTrack() function:
    //   RecordingConfig recordingConfig = new RecordingConfig(session)
    //    .setMp4DatasetUri(mp4FileUri)
    //    .setAutoStopOnPause(true);

    RecordingConfig recordingConfig = new RecordingConfig(session)
        .setMp4DatasetUri(mp4FileUri)
        .setAutoStopOnPause(true)
        .addTrack(anchorTrack); // add the new track onto the recordingConfig

    // ... omitted code ...
  }

Ankerposition während der Aufnahme speichern

Jedes Mal, wenn der Nutzer auf ein erkanntes Flugzeug tippt, wird ein AR-Marker auf einem Gerät (Anchor) platziert, dessen Position von ARCore aktualisiert wird.

Wenn die ARCore-Sitzung noch läuft, zeichne die Position eines Anchor in dem Frame auf, an dem es erstellt wurde.

Ändern Sie die handleTap()-Funktion in HelloArActivity.java.

// Add imports to the beginning of the file.
import com.google.ar.core.Pose;
import java.nio.FloatBuffer;

  private void handleTap(Frame frame, Camera camera) {
          // ... omitted code ...

          // Insert after line:
          // anchors.add(hit.createAnchor());

          // If the app is recording a session,
          // save the new Anchor pose (relative to the camera)
          // into the ANCHOR_TRACK_ID track.
          if (appState == AppState.Recording) {
            // Get the pose relative to the camera pose.
            Pose cameraRelativePose = camera.getPose().inverse().compose(hit.getHitPose());
            float[] translation = cameraRelativePose.getTranslation();
            float[] quaternion = cameraRelativePose.getRotationQuaternion();
            ByteBuffer payload = ByteBuffer.allocate(4 * (translation.length + quaternion.length));
            FloatBuffer floatBuffer = payload.asFloatBuffer();
            floatBuffer.put(translation);
            floatBuffer.put(quaternion);

            try {
              frame.recordTrackData(ANCHOR_TRACK_ID, payload);
            } catch (IllegalStateException e) {
              Log.e(TAG, "Error in recording anchor into external data track.", e);
            }
          }
          // ... omitted code ...
  }

Der Grund, warum wir die relative Kamera-URL Pose und nicht die Welt-relative Pose beibehalten, liegt daran, dass der Weltursprung einer Aufnahmesitzung und der Weltursprung einer Wiedergabesitzung nicht identisch sind. Der Weltursprung einer Aufzeichnungssitzung beginnt, wenn die Sitzung zum ersten Mal fortgesetzt wird und Session.resume() zum ersten Mal aufgerufen wird. Der Ursprung einer Wiedergabesitzung beginnt, wenn der erste Frame aufgezeichnet wird, wenn Session.resume() erstmals nach Session.startRecording() aufgerufen wird.

Wiedergabeanker erstellen

Ein Anchor lässt sich ganz einfach neu erstellen. Fügen Sie eine Funktion namens createRecordedAnchors() in HelloArActivity.java hinzu.

// Add imports to the beginning of the file.
import com.google.ar.core.TrackData;

  // Extract poses from the ANCHOR_TRACK_ID track, and create new anchors.
  private void createRecordedAnchors(Frame frame, Camera camera) {
    // Get all `ANCHOR_TRACK_ID` TrackData from the frame.
    for (TrackData trackData : frame.getUpdatedTrackData(ANCHOR_TRACK_ID)) {
      ByteBuffer payload = trackData.getData();
      FloatBuffer floatBuffer = payload.asFloatBuffer();

      // Extract translation and quaternion from TrackData payload.
      float[] translation = new float[3];
      float[] quaternion = new float[4];

      floatBuffer.get(translation);
      floatBuffer.get(quaternion);

      // Transform the recorded anchor pose
      // from the camera coordinate
      // into world coordinates.
      Pose worldPose = camera.getPose().compose(new Pose(translation, quaternion));

      // Re-create an anchor at the recorded pose.
      Anchor recordedAnchor = session.createAnchor(worldPose);

      // Add the new anchor into the list of anchors so that
      // the AR marker can be displayed on top.
      anchors.add(recordedAnchor);
    }
  }

Rufen Sie createRecordedAnchors() in der Funktion onDrawFrame() in HelloArActivity.java auf.

  public void onDrawFrame(SampleRender render) {
    // ... omitted code ...

    // Insert after this line:
    // handleTap(frame, camera);

    // If the app is currently playing back a session, create recorded anchors.
    if (appState == AppState.Playingback) {
      createRecordedAnchors(frame, camera);
    }
    // ... omitted code ...
  }

Auf dem Zielgerät testen

Verbinden Sie Ihr Mobilgerät mit dem Entwicklungscomputer und klicken Sie in Android Studio auf Ausführen.

Tippe zuerst auf die Schaltfläche Aufzeichnen, um eine Sitzung aufzunehmen. Tippe während der Aufnahme auf erkannte Flugzeuge, um ein paar AR-Markierungen zu setzen.

Tippen Sie nach dem Ende der Aufnahme auf die Schaltfläche Wiedergabe und wählen Sie die Datei aus, die Sie gerade aufgenommen haben. Die Wiedergabe sollte beginnen. Sie sehen, wie Ihre vorherigen AR-Markierungs-Placements angezeigt werden, als Sie auf die App getippt haben.

Das war die gesamte Programmierung, die Sie für dieses Codelab benötigen.

6. Glückwunsch

Glückwunsch, du hast das Ende dieses Codelabs erreicht. Sehen wir uns an, was du in diesem Codelab geschafft hast:

  • Sie haben das ARCore-Beispiel Hello AR Java erstellt und ausgeführt.
  • Der App wurde eine Aufnahmeschaltfläche hinzugefügt, mit der eine AR-Sitzung als MP4-Datei gespeichert werden kann
  • Der App wurde eine Wiedergabeschaltfläche hinzugefügt, um eine AR-Sitzung aus einer MP4-Datei wiederzugeben
  • Eine neue Funktion zum Speichern der vom Nutzer erstellten Anker in der MP4-Datei für die Wiedergabe wurde hinzugefügt.

Hat Ihnen dieses Codelab Spaß gemacht?

<ph type="x-smartling-placeholder"></ph> Ja Nein

Haben Sie bei diesem Codelab etwas Nützliches gelernt?

<ph type="x-smartling-placeholder"></ph> Ja Nein

Haben Sie die App in diesem Codelab erstellt?

<ph type="x-smartling-placeholder"></ph> Ja Nein