Pengantar ARCore Recording and Playback API

1. Pengantar

Fitur yang dapat menyimpan pengalaman AR ke file MP4 dan memutarnya dari file MP4 mungkin berguna bagi developer aplikasi maupun pengguna akhir.

Men-debug dan menguji fitur baru dari desktop Anda

ARCore Record & Playback API tentunya sangat berguna bagi developer. Anda tidak perlu lagi mem-build dan menjalankan aplikasi di perangkat uji, melepas kabel USB, dan berjalan berkeliling hanya untuk menguji perubahan kecil pada kode. Sekarang, Anda hanya perlu merekam MP4 di lingkungan pengujian dengan pergerakan ponsel yang diperkirakan, dan melakukan pengujian langsung dari desktop Anda.

Merekam dan memutar dari perangkat yang berbeda

Dengan Recording and Playback API, satu pengguna dapat merekam sesi menggunakan satu perangkat, sedangkan pengguna lainnya dapat memutar sesi yang sama pada perangkat lain. Anda dapat membagikan pengalaman AR dengan pengguna lain. Ada banyak kemungkinannya.

Apakah ini pertama kalinya Anda membuat aplikasi ARCore?

Tidak. Ya.

Bagaimana Anda akan menggunakan codelab ini?

Hanya membacanya Membacanya dan menyelesaikan latihan

Yang akan Anda build

Dalam codelab ini, Anda akan menggunakan Recording & Playback API untuk membuat aplikasi yang merekam pengalaman AR ke file MP4 dan memutar pengalaman tersebut dari file yang sama. Anda akan mempelajari:

  • Cara menggunakan Recording API untuk menyimpan sesi AR ke file MP4.
  • Cara menggunakan Playback API untuk memutar sesi AR dari file MP4.
  • Cara merekam sesi AR di satu perangkat dan memutarnya di perangkat lain.

Yang Anda butuhkan

Dalam codelab ini, Anda akan memodifikasi aplikasi Hello AR Java, yang di-build dengan ARCore Android SDK. Anda akan membutuhkan hardware dan software tertentu untuk mengikutinya.

Persyaratan hardware

Persyaratan software

Anda juga harus memiliki pemahaman dasar tentang ARCore untuk memperoleh hasil terbaik.

2. Menyiapkan lingkungan pengembangan

Mulai dengan menyiapkan lingkungan pengembangan Anda.

Mendownload ARCore Android SDK

Klik untuk mendownload SDK.

Mengekstrak file zip ARCore Android SDK

Setelah mendownload Android SDK ke mesin Anda, ekstrak file zip tersebut lalu buka direktori arcore-android-sdk-1.24/samples/hello_ar_java. Ini adalah direktori utama aplikasi yang akan digunakan.

hello-ar-java-extracted

Memuat Hello AR Java ke Android Studio

Luncurkan Android Studio dan klik Open an existing Android Studio project.

android-studio-open-projects

Pada jendela dialog yang muncul, pilih arcore-android-sdk-1.24/samples/hello_ar_java, lalu klik Open.

Tunggu sampai Android Studio menyelesaikan sinkronisasi project. Jika ada komponen yang hilang, pengimporan project mungkin akan gagal dengan pesan error. Perbaiki masalah ini sebelum melanjutkan.

Menjalankan aplikasi contoh

  1. Hubungkan perangkat yang mendukung ARCore ke mesin pengembangan Anda.
  2. Jika perangkat dikenali dengan benar, Anda akan melihat nama perangkat muncul di Android Studio. android-studio-pixel-5.png
  3. Klik tombol Run atau pilih Run > Run 'app' agar Android Studio menginstal dan meluncurkan aplikasi di perangkat. android-studio-run-button.png
  4. Anda akan melihat perintah yang meminta izin untuk mengambil gambar dan merekam video. Pilih While using this app, untuk memberikan izin Kamera kepada aplikasi. Lalu Anda akan melihat lingkungan dunia nyata di layar perangkat. hello-ar-java-permission
  5. Gerakkan perangkat secara horizontal untuk memindai bidang.
  6. Petak putih akan muncul saat aplikasi mendeteksi bidang. Ketuk petak untuk menempatkan penanda di bidang tersebut. Penempatan Hello AR

Yang telah Anda lakukan pada langkah ini

  • Menyiapkan project Hello AR Java
  • Mem-build dan menjalankan aplikasi contoh di perangkat yang mendukung ARCore

Selanjutnya, Anda akan merekam sesi AR ke file MP4.

3. Merekam sesi ARCore ke file MP4

Kami akan menambahkan fitur perekaman di langkah ini. Fitur tersebut terdiri dari:

  • Tombol untuk memulai atau menghentikan perekaman.
  • Fungsi penyimpanan untuk menyimpan file MP4 pada perangkat.
  • Panggilan untuk memulai atau menghentikan perekaman sesi ARCore.

Menambahkan UI untuk tombol Record

Sebelum Anda mengimplementasikan perekaman, tambahkan tombol di UI sehingga pengguna dapat memberi tahu ARCore kapan harus memulai atau menghentikan perekaman.

Di panel Project, buka file app/res/layout/activity_main.xml.

activity_main-xml-location-in-project

Secara default, Android Studio akan menggunakan tampilan desain setelah Anda membuka file app/res/layout/activity_main.xml. Klik tombol Code di pojok kanan atas tab untuk beralih ke tampilan kode.

swith-to-the-code-view.png

Di activity_main.xml, tambahkan kode berikut sebelum tag penutup untuk membuat tombol Record baru dan setel penangan peristiwanya ke metode yang disebut onClickRecord():

  <!--
    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" />

Setelah Anda menambahkan kode di atas, sebuah error mungkin akan ditampilkan untuk sementara: Corresponding method handler 'public void onClickRecord(android.view.View)' not found". Hal ini sudah diperkirakan. Anda akan mengatasi error tersebut dengan membuat fungsi onClickRecord() dalam beberapa langkah berikutnya.

Mengubah teks di tombol berdasarkan status

Tombol Record sebenarnya menangani perekaman maupun penghentian perekaman. Saat tidak merekam data, aplikasi akan menampilkan kata "Record". Saat aplikasi merekam data, tombol akan berubah dengan menampilkan kata "Stop".

Untuk memberikan fungsi ini pada tombol, aplikasi harus mengetahui statusnya saat ini. Kode berikut membuat enum baru yang disebut AppState untuk mewakili status kerja aplikasi dan melacak perubahan status tertentu melalui variabel anggota pribadi yang disebut appState. Tambahkan ke HelloArActivity.java, di awal class HelloArActivity.

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

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

Setelah Anda dapat melacak status internal aplikasi, buat fungsi yang disebut updateRecordButton(), yang mengubah teks tombol berdasarkan status aplikasi saat ini. Tambahkan kode berikut di dalam class HelloArActivity di HelloArActivity.java.

// 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;
    }
  }

Selanjutnya, buat metode onClickRecord() yang memeriksa status aplikasi, mengubahnya ke status berikutnya, dan memanggil updateRecordButton() untuk mengubah UI tombol. Tambahkan kode berikut di dalam class HelloArActivity di HelloArActivity.java.

  // 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();
  }

Mengaktifkan aplikasi untuk mulai merekam

Anda hanya perlu melakukan dua hal untuk mulai merekam di ARCore:

  1. Tentukan URI file rekaman di objek RecordingConfig.
  2. Panggil session.startRecording dengan objek RecordingConfig

Sisanya hanyalah kode boilerplate: konfigurasi, logging, dan pemeriksaan ketepatan.

Buat fungsi baru bernama startRecording() yang merekam data dan menyimpannya ke URI MP4. Tambahkan kode berikut di dalam class HelloArActivity di HelloArActivity.java.

// 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;
  }

Untuk menjeda dan melanjutkan sesi ARCore dengan aman, buat pauseARCoreSession() dan resumeARCoreSession() di 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;
  }

Mengaktifkan aplikasi untuk berhenti merekam

Buat fungsi yang disebut stopRecording() di HelloArActivity.java untuk menghentikan aplikasi agar tidak merekam data baru. Fungsi ini memanggil session.stopRecording() dan mengirim error ke log konsol jika aplikasi tidak dapat berhenti merekam.

  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;
  }

Mendesain penyimpanan file menggunakan penyimpanan terbatas Android 11

Fungsi terkait penyimpanan dalam codelab ini dirancang sesuai dengan persyaratan penyimpanan terbatas Android 11 yang baru.

Buat beberapa perubahan kecil di file app/build.gradle untuk menargetkan Android 11. Di panel Project Android Studio, file ini berada di bawah node Gradle Scripts, yang terkait dengan modul aplikasi.

app-build.gradle.png

Ubah compileSdkVersion dan targetSdkVersion menjadi 30.

    compileSdkVersion 30
    defaultConfig {
      targetSdkVersion 30
    }

Untuk Perekaman, gunakan Android MediaStore API untuk membuat file MP4 di direktori Movie yang dibagikan.

Buat fungsi yang disebut createMp4File() di HelloArActivity.java:

// 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;
  }

Menangani izin penyimpanan

Jika menggunakan perangkat Android 11, Anda dapat mulai menguji kode. Untuk mendukung perangkat Android 10 atau yang lebih rendah, Anda perlu memberikan izin penyimpanan aplikasi guna menyimpan data ke sistem file perangkat target.

Di AndroidManifest.xml, deklarasikan bahwa aplikasi memerlukan izin baca dan tulis penyimpanan sebelum Android 11 (API level 30).

  <!-- 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" />

Tambahkan fungsi bantuan yang disebut checkAndRequestStoragePermission() di HelloArActivity.java untuk meminta izin WRITE_EXTERNAL_STORAGE selama runtime.

// 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;
  }

Jika Anda menggunakan API level 29 atau yang lebih lama, tambahkan tanda centang untuk izin penyimpanan di bagian atas createMp4File() dan keluar dari fungsi lebih awal jika aplikasi tidak memiliki izin yang tepat. API level 30 (Android 11) tidak memerlukan izin penyimpanan untuk mengakses file di 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 ...
  }

Merekam dari perangkat target

Saatnya melihat yang telah Anda build sejauh ini. Hubungkan perangkat seluler ke mesin pengembangan, lalu klik Run di Android Studio.

Anda akan melihat tombol Record berwarna merah di sisi kiri bawah layar. Mengetuknya akan mengubah teks menjadi Stop. Gerakkan perangkat Anda untuk merekam sesi, dan klik tombol Stop saat Anda ingin menyelesaikan rekaman. Tindakan ini akan menyimpan file baru bernama arcore-xxxxxx_xxxxxx.mp4 di penyimpanan eksternal perangkat Anda.

record-button.png

Sekarang, Anda akan memiliki file arcore-xxxxxx_xxxxxx.mp4 baru di penyimpanan eksternal perangkat. Pada perangkat Pixel 5, jalurnya adalah /storage/emulated/0/Movies/. Jalur ini dapat ditemukan di jendela Logcat setelah memulai rekaman.

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

Melihat rekaman

Anda dapat menggunakan aplikasi sistem file seperti Files by Google untuk melihat rekaman, atau menyalinnya ke mesin pengembangan. Di bawah ini adalah dua perintah adb untuk mencantumkan dan mengambil file dari perangkat Android:

  • adb shell ls '$EXTERNAL_STORAGE/Movies/*' untuk menampilkan file dalam direktori Movies dalam penyimpanan eksternal pada perangkat
  • adb pull /absolute_path_from_previous_adb_shell_ls/arcore-xxxxxxxx_xxxxxx.mp4 untuk menyalin file dari perangkat ke mesin pengembangan

Ini adalah contoh output setelah menggunakan dua perintah ini (dari 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

Yang telah Anda lakukan pada langkah ini

  • Menambahkan tombol untuk memulai dan menghentikan perekaman
  • Mengimplementasikan fungsi untuk memulai dan menghentikan perekaman
  • Menguji aplikasi di perangkat
  • Menyalin MP4 yang direkam ke mesin Anda dan memverifikasinya

Berikutnya, Anda akan memutar sesi AR dari file MP4.

4. Memutar sesi ARCore dari file MP4

Anda sekarang memiliki tombol Record dan beberapa file MP4 yang berisi sesi yang direkam. Sekarang, Anda akan memutarnya menggunakan ARCore Playback API.

Menambahkan UI untuk tombol Playback

Sebelum Anda mengimplementasikan pemutaran, tambahkan tombol di UI sehingga pengguna dapat memberi tahu ARCore kapan harus memulai dan menghentikan pemutaran sesi.

Di panel Project, buka file app/res/layout/activity_main.xml.

activity_main-xml-location-in-project

Di activity_main.xml, tambahkan kode di bawah sebelum tag penutup untuk membuat tombol Playback baru dan setel penangan peristiwanya ke metode yang disebut onClickPlayback(). Tombol ini akan mirip dengan tombol Record, dan akan ditampilkan di sisi kanan layar.

  <!--
    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" />

Mengupdate Tombol selama Pemutaran

Aplikasi kini memiliki status baru yang disebut Playingback. Update enum AppState dan semua fungsi yang ada yang menggunakan appState sebagai argumen untuk menangani ini.

Tambahkan Playingback ke enum AppState di HelloArActivity.java:

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

Jika tombol Record masih ada di layar selama pemutaran, pengguna kemungkinan tidak sengaja mengkliknya. Untuk menghindari hal ini, sembunyikan tombol Record selama Pemutaran. Dengan cara ini, Anda tidak perlu menangani status Playingback di onClickRecord().

Ubah fungsi updateRecordButton() di HelloArActivity.java untuk menyembunyikan tombol Record saat aplikasi dalam status Playingback.

  // 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;
    }
  }

Demikian pula, sembunyikan tombol Playback saat pengguna merekam sesi, dan ubah menjadi "Stop" saat pengguna secara aktif memutar sesi. Dengan cara ini, mereka dapat menghentikan pemutaran tanpa harus menunggu hingga selesai dengan sendirinya.

Tambahkan fungsi updatePlaybackButton() di HelloArActivity.java:

  // 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;
    }
  }

Terakhir, update onClickRecord() untuk memanggil updatePlaybackButton(). Tambahkan baris berikut ke HelloArActivity.java:

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

Memilih file dengan tombol Playback

Saat diketuk, tombol Playback akan membuat pengguna dapat memilih file untuk diputar. Di Android, pemilihan file ditangani di alat pilih file sistem di Aktivitas lain. Hal ini dilakukan melalui Storage Access Framework (SAF). Setelah pengguna memilih file, aplikasi akan menerima callback yang disebut onActivityResult(). Anda akan memulai pemutaran sebenarnya di dalam fungsi callback ini.

Di HelloArActivity.java, buat fungsi onClickPlayback() untuk melakukan pemilihan file dan berhenti memutar.

  // 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();
  }

Di HelloArActivity.java, buat fungsi selectFileToPlayback() yang memilih file dari perangkat. Untuk memilih file dari Sistem File Android, gunakan Intent ACTION_OPEN_DOCUMENT.

// 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 adalah konstanta untuk mengidentifikasi permintaan ini. Anda dapat menentukannya menggunakan nilai placeholder apa pun di dalam HelloArActivity di HelloArActivity.java:

  private int REQUEST_MP4_SELECTOR = 1;

Ganti fungsi onActivityResult() di HelloArActivity.java untuk menangani callback dari alat pilih file.

  // 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);
  }

Mengaktifkan aplikasi untuk memulai pemutaran

Sesi ARCore memerlukan tiga panggilan API untuk memutar file MP4:

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

Di HelloArActivity.java, buat fungsi 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;
  }

Mengaktifkan aplikasi untuk menghentikan pemutaran

Buat fungsi yang disebut stopPlayingback() di HelloArActivity.java untuk menangani perubahan status aplikasi setelah:

  1. Pemutaran MP4 dihentikan oleh pengguna
  2. Pemutaran MP4 selesai dengan sendirinya

Jika pengguna menghentikan pemutaran, aplikasi harus kembali ke status yang sama seperti saat pengguna pertama kali meluncurkannya.

  // 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;
  }

Pemutaran juga dapat berhenti secara alami setelah pemutar mencapai akhir file MP4. Saat ini terjadi, stopPlayingback() harus mengembalikan status aplikasi ke Idle. Di onDrawFrame(), periksa PlaybackStatus. Jika FINISHED, panggil fungsi stopPlayingback() pada UI thread.

  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 ...
  }

Memutar dari perangkat target

Saatnya melihat yang telah Anda build sejauh ini. Hubungkan perangkat seluler ke mesin pengembangan, lalu klik Run di Android Studio.

Saat aplikasi diluncurkan, Anda akan melihat layar dengan tombol Record berwarna merah di sebelah kiri dan tombol Playback berwarna hijau di sebelah kanan.

playback-button.png

Ketuk tombol Playback, lalu pilih salah satu file MP4 yang baru saja Anda rekam. Jika Anda tidak melihat nama file yang dimulai dengan arcore-, mungkin perangkat tidak menampilkan folder Movies. Dalam kasus ini, buka folder Phone model > Movies menggunakan menu di pojok kiri atas. Anda mungkin juga perlu mengaktifkan opsi Show internal storage untuk menampilkan folder model ponsel.

show-internal-storage-button.png

nativate-to-movies-file-picker.jpg

Ketuk nama file pada layar untuk memilih file MP4. Aplikasi akan memutar file MP4.

playback-stop-button.png

Perbedaan antara memutar sesi dan memutar video biasa adalah Anda dapat berinteraksi dengan sesi yang direkam. Ketuk bidang yang terdeteksi untuk menempatkan penanda di layar.

playback-placement

Yang telah Anda lakukan pada langkah ini

  • Menambahkan tombol untuk memulai dan menghentikan pemutaran
  • Mengimplementasikan fungsi untuk membuat aplikasi memulai dan menghentikan perekaman
  • Memutar sesi ARCore yang telah direkam sebelumnya pada perangkat

5. Merekam data tambahan dalam MP4

Dengan ARCore 1.24, Anda dapat merekam informasi tambahan dalam file MP4. Anda dapat merekam Pose penempatan objek AR, lalu membuat objek AR di lokasi yang sama selama pemutaran.

Mengonfigurasi trek baru untuk merekam

Tentukan trek baru dengan UUID dan tag MIME di 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 ...
  }

Update kode keluar untuk membuat objek RecordingConfig dengan panggilan ke addTrack().

  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 ...
  }

Menyimpan pose anchor selama perekaman

Setiap kali pengguna mengetuk bidang yang terdeteksi, penanda AR akan ditempatkan di Anchor, yang posenya akan diupdate oleh ARCore.

Rekam pose Anchor pada frame yang dibuatnya, jika Anda masih merekam sesi ARCore.

Ubah fungsi handleTap() di 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 ...
  }

Alasan kami mempertahankan Pose relatif kamera dan bukannya Pose relatif dunia adalah karena asal dunia dari sebuah sesi rekaman dan asal dunia dari sebuah sesi pemutaran tidaklah sama. Asal dunia dari sebuah sesi rekaman dimulai saat sesi pertama kali dilanjutkan, saat Session.resume() pertama kali dipanggil. Asal dunia dari sebuah sesi pemutaran dimulai saat frame pertama direkam, saat Session.resume() pertama kali dipanggil setelah Session.startRecording().

Memutar pembuatan anchor

Membuat ulang Anchor sangatlah mudah. Tambahkan fungsi createRecordedAnchors() di HelloArActivity.java.

// 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);
    }
  }

Panggil createRecordedAnchors() di fungsi onDrawFrame() di HelloArActivity.java.

  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 ...
  }

Menguji di perangkat target

Hubungkan perangkat seluler ke mesin pengembangan, lalu klik Run di Android Studio.

Pertama, ketuk tombol Record untuk merekam sesi. Selama perekaman, ketuk bidang yang terdeteksi untuk menempatkan beberapa penanda AR.

Setelah perekaman berhenti, ketuk tombol Playback dan pilih file yang baru saja Anda rekam. Pemutaran akan dimulai. Perhatikan bagaimana penempatan penanda AR sebelumnya muncul saat Anda mengetuk aplikasi.

Itulah semua coding yang harus Anda lakukan untuk codelab ini.

6. Selamat

Selamat, Anda telah berhasil mencapai bagian akhir codelab ini. Mari lihat kembali apa yang telah Anda lakukan dalam codelab ini:

  • Mem-build dan menjalankan contoh Hello AR Java ARCore.
  • Menambahkan tombol Record ke aplikasi untuk menyimpan sesi AR ke file MP4
  • Menambahkan tombol Playback ke aplikasi untuk memutar sesi AR dari file MP4
  • Menambahkan fitur baru untuk menyimpan anchor yang dibuat oleh pengguna di MP4 untuk diputar

Apakah Anda senang ketika mengerjakan codelab ini?

Ya Tidak

Apakah Anda mempelajari sesuatu yang berguna ketika mengerjakan codelab ini?

Ya Tidak

Apakah Anda sudah selesai membuat aplikasi dalam codelab ini?

Ya Tidak