1. Introduzione
La possibilità di salvare un'esperienza AR in un file MP4 e riprodurla dal file MP4 può essere utile sia agli sviluppatori di app sia agli utenti finali.
Esegui il debug e testa le nuove funzionalità dalla tua scrivania
L'utilizzo più semplice dell'API ARCore Record & Playback è per gli sviluppatori. Sono finiti i tempi in cui dovevi creare ed eseguire l'app su un dispositivo di test, scollegare il cavo USB e spostarti per testare una piccola modifica al codice. Ora devi solo registrare un MP4 nell'ambiente di test con il movimento previsto dello smartphone ed eseguire il test direttamente dalla tua scrivania.
Registrare e riprodurre da dispositivi diversi
Con le API Recording and Playback, un utente può registrare una sessione utilizzando un dispositivo e un altro può riprodurre la stessa sessione su un dispositivo diverso. È possibile condividere un'esperienza AR con un altro utente. Le possibilità sono molte.
È la prima volta che crei un'app ARCore?
Come utilizzerai questo codelab?
Cosa creerai
In questo codelab utilizzerai l'API Recording & Playback per creare un'app che registri un'esperienza AR in un file MP4 e la riproduca dallo stesso file. Scoprirai:
- Come utilizzare l'API Recording per salvare una sessione AR in un file MP4.
- Come utilizzare l'API Playback per riprodurre una sessione AR da un file MP4.
- Come registrare una sessione di AR su un dispositivo e riprodurla su un altro.
Che cosa ti serve
In questo codelab, modificherai l'app Hello AR Java, creata con l'SDK ARCore per Android. Per seguire le istruzioni, avrai bisogno di hardware e software specifici.
Requisiti hardware
- Un dispositivo supportato da ARCore con le opzioni sviluppatore attive e il debug USB abilitato, collegato tramite cavo USB alla macchina di sviluppo.
- Una macchina di sviluppo su cui esegui Android Studio.
- Accesso a internet per scaricare le librerie durante lo sviluppo.
Requisiti software
- Google Play Services per AR (ARCore) 1.24 o versioni successive sul dispositivo ARCore di sviluppo. Questo servizio viene normalmente installato automaticamente sul dispositivo tramite il Play Store. Puoi anche installarlo manualmente su un dispositivo supportato da ARCore.
- Android Studio (v3.1 o versioni successive) sulla macchina di sviluppo.
Per ottenere risultati ottimali, dovresti anche avere una conoscenza di base di ARCore.
2. Configurazione dell'ambiente di sviluppo
Inizia configurando l'ambiente di sviluppo.
Scarica l'SDK ARCore per Android
Fai clic su per scaricare l'SDK.
Decomprimere l'SDK ARCore per Android
Una volta scaricato l'SDK Android sul tuo computer, decomprimi il file e vai alla directory arcore-android-sdk-1.24/samples/hello_ar_java. Questa è la directory principale dell'app con cui lavorerai.

Carica Hello AR Java in Android Studio
Avvia Android Studio e fai clic su Apri un progetto Android Studio esistente.

Nella finestra di dialogo risultante, seleziona arcore-android-sdk-1.24/samples/hello_ar_java e fai clic su Apri.
Attendi il completamento della sincronizzazione del progetto da parte di Android Studio. Se mancano componenti, l'importazione del progetto potrebbe non riuscire e potrebbero essere visualizzati messaggi di errore. Risolvi questi problemi prima di continuare.
Esegui l'app di esempio
- Collega un dispositivo supportato da ARCore alla macchina di sviluppo.
- Se il dispositivo viene riconosciuto correttamente, dovresti visualizzare il nome del dispositivo in Android Studio.

- Fai clic sul pulsante Esegui o seleziona Esegui > Esegui "app" per fare in modo che Android Studio installi e avvii l'app sul dispositivo.

- Vedrai un messaggio che chiede l'autorizzazione per scattare foto e registrare video. Seleziona Mentre usi questa app per concedere all'app le autorizzazioni della fotocamera. Dopodiché, vedrai l'ambiente reale sullo schermo del dispositivo.

- Sposta il dispositivo orizzontalmente per cercare gli aerei.
- Quando l'app rileva un aereo, viene visualizzata una griglia bianca. Tocca per posizionare un indicatore sul piano.

Cosa hai fatto in questo passaggio
- Configura il progetto Java Hello AR
- Crea ed esegui l'app di esempio su un dispositivo supportato da ARCore
Successivamente, registrerai una sessione AR in un file MP4.
3. Registrare una sessione ARCore in un file MP4
In questo passaggio aggiungeremo la funzionalità di registrazione. È composto da:
- Un pulsante per avviare o interrompere la registrazione.
- Funzioni di archiviazione per salvare il file MP4 sul dispositivo.
- Chiamate per avviare o interrompere la registrazione della sessione ARCore.
Aggiungere l'UI per il pulsante Registra
Prima di implementare la registrazione, aggiungi un pulsante all'interfaccia utente in modo che l'utente possa comunicare ad ARCore quando iniziare o interrompere la registrazione.
Nel riquadro Progetto, apri il file app/res/layout/activity_main.xml.

Per impostazione predefinita, Android Studio utilizza la visualizzazione Progettazione dopo l'apertura del file app/res/layout/activity_main.xml. Fai clic sul pulsante Codice nell'angolo in alto a destra della scheda per passare alla visualizzazione del codice.

In activity_main.xml, aggiungi il seguente codice prima del tag di chiusura per creare il nuovo pulsante Record e imposta il relativo gestore di eventi su un metodo denominato 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" />
Dopo aver aggiunto il codice precedente, potrebbe essere visualizzato temporaneamente un errore: Corresponding method handler 'public void onClickRecord(android.view.View)' not found". È previsto. Risolvi l'errore creando la funzione onClickRecord() nei prossimi passaggi.
Modificare il testo sul pulsante in base allo stato
Il pulsante Registra gestisce sia l'avvio che l'interruzione della registrazione. Quando l'app non registra dati, deve visualizzare la parola "Registra". Quando l'app registra i dati, il pulsante deve cambiare e visualizzare la parola "Interrompi".
Per assegnare questa funzionalità al pulsante, l'app deve conoscere il suo stato attuale. Il seguente codice crea una nuova enumerazione denominata AppState per rappresentare lo stato di funzionamento dell'app e tiene traccia di modifiche di stato specifiche tramite una variabile membro privata denominata appState. Aggiungilo a HelloArActivity.java, all'inizio della classe HelloArActivity.
// Represents the app's working state.
public enum AppState {
Idle,
Recording
}
// Tracks app's specific state changes.
private AppState appState = AppState.Idle;
Ora che puoi monitorare lo stato interno dell'app, crea una funzione chiamata updateRecordButton() che cambia il testo del pulsante in base allo stato attuale dell'app. Aggiungi il seguente codice all'interno della classe HelloArActivity in 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;
}
}
Successivamente, crea il metodo onClickRecord() che controlla lo stato dell'app, lo modifica in quello successivo e chiama updateRecordButton() per modificare la UI del pulsante. Aggiungi il seguente codice all'interno della classe HelloArActivity in 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();
}
Consenti all'app di avviare la registrazione
Per iniziare a registrare in ARCore, devi fare solo due cose:
- Specifica l'URI del file di registrazione in un oggetto
RecordingConfig. - Chiama
session.startRecordingcon l'oggettoRecordingConfig
Il resto è solo codice boilerplate: configurazione, logging e controllo della correttezza.
Crea una nuova funzione denominata startRecording() che registra i dati e li salva in un URI MP4. Aggiungi il seguente codice all'interno della classe HelloArActivity in 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;
}
Per mettere in pausa e riprendere in modo sicuro una sessione ARCore, crea pauseARCoreSession() e 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;
}
Consenti all'app di interrompere la registrazione
Crea una funzione denominata stopRecording() in HelloArActivity.java per impedire all'app di registrare nuovi dati. Questa funzione chiama session.stopRecording() e invia un errore al log della console se l'app non riesce a interrompere la registrazione.
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;
}
Progettare l'archiviazione dei file utilizzando l'archiviazione isolata di Android 11
Le funzioni relative all'archiviazione in questo codelab sono progettate in base ai nuovi requisiti di archiviazione con ambito di Android 11.
Apporta alcune piccole modifiche al file app/build.gradle per avere come target Android 11. Nel riquadro Progetto di Android Studio, questo file si trova sotto il nodo Gradle Scripts, associato al modulo app.

Modifica compileSdkVersion e targetSdkVersion in 30.
compileSdkVersion 30
defaultConfig {
targetSdkVersion 30
}
Per la registrazione, utilizza l'API Android MediaStore per creare il file MP4 nella directory condivisa Film.
Crea una funzione chiamata createMp4File() in 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;
}
Gestire le autorizzazioni di archiviazione
Se utilizzi un dispositivo Android 11, puoi iniziare a testare il codice. Per supportare i dispositivi Android 10 o versioni precedenti, devi concedere all'app le autorizzazioni di archiviazione per salvare i dati nel file system del dispositivo di destinazione.
In AndroidManifest.xml, dichiara che l'app ha bisogno delle autorizzazioni di lettura e scrittura dell'archiviazione prima di Android 11 (livello API 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" />
Aggiungi una funzione helper denominata checkAndRequestStoragePermission() in HelloArActivity.java per richiedere le autorizzazioni WRITE_EXTERNAL_STORAGE durante il 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;
}
Se utilizzi il livello API 29 o versioni precedenti, aggiungi un controllo delle autorizzazioni di archiviazione nella parte superiore di createMp4File() ed esci dalla funzione in anticipo se l'app non dispone delle autorizzazioni corrette. Il livello API 30 (Android 11) non richiede l'autorizzazione di archiviazione per accedere ai file in 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 ...
}
Registrare dal dispositivo di destinazione
È il momento di vedere cosa hai creato finora. Collega il dispositivo mobile alla macchina di sviluppo e fai clic su Esegui in Android Studio.
Dovresti vedere un pulsante rosso Registra in basso a sinistra dello schermo. Se lo tocchi, il testo dovrebbe cambiare in Interrompi. Sposta il dispositivo per registrare una sessione e fai clic sul pulsante Interrompi quando vuoi completare la registrazione. Verrà salvato un nuovo file chiamato arcore-xxxxxx_xxxxxx.mp4 nella memoria esterna del dispositivo.

Ora dovresti avere un nuovo file arcore-xxxxxx_xxxxxx.mp4 nella memoria esterna del dispositivo. Sui Pixel 5, il percorso è /storage/emulated/0/Movies/. Il percorso è disponibile nella finestra Logcat dopo l'avvio di una registrazione.
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
Visualizzare la registrazione
Puoi utilizzare un'app di file system come Files by Google per visualizzare la registrazione o copiarla sul computer di sviluppo. Di seguito sono riportati i due comandi adb per elencare e recuperare i file dal dispositivo Android:
adb shell ls '$EXTERNAL_STORAGE/Movies/*'per mostrare i file nella directory Filmati nell'archivio esterno del dispositivoadb pull /absolute_path_from_previous_adb_shell_ls/arcore-xxxxxxxx_xxxxxx.mp4per copiare il file dal dispositivo al computer di sviluppo
Di seguito è riportato un esempio di output dopo l'utilizzo di questi due comandi (da 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
Cosa hai fatto in questo passaggio
- Aggiunto un pulsante per avviare e interrompere la registrazione
- Implementate funzioni per avviare e interrompere la registrazione
- Testato l'app sul dispositivo
- Copia il file MP4 registrato sul tuo computer e verificalo
Successivamente, riprodurrai una sessione AR da un file MP4.
4. Riprodurre una sessione ARCore da un file MP4
Ora hai un pulsante Registra e alcuni file MP4 contenenti le sessioni registrate. Ora li riprodurrai utilizzando l'API ARCore Playback.
Aggiungere l'UI per il pulsante Riproduzione
Prima di implementare la riproduzione, aggiungi un pulsante all'interfaccia utente in modo che l'utente possa comunicare ad ARCore quando deve iniziare e interrompere la riproduzione della sessione.
Nel riquadro Progetto, apri il file app/res/layout/activity_main.xml.

In activity_main.xml, aggiungi il codice riportato di seguito prima del tag di chiusura per creare il nuovo pulsante Riproduzione e imposta il relativo gestore eventi su un metodo denominato onClickPlayback(). Questo pulsante sarà simile al pulsante Registra e verrà visualizzato sul lato destro dello schermo.
<!--
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" />
Pulsanti di aggiornamento durante la riproduzione
L'app ora ha un nuovo stato chiamato Playingback. Aggiorna l'enumerazione AppState e tutte le funzioni esistenti che accettano appState come argomento per gestire questo problema.
Aggiungi Playingback all'enumerazione AppState in HelloArActivity.java:
public enum AppState {
Idle,
Recording,
Playingback // New enum value.
}
Se il pulsante Registra è ancora sullo schermo durante la riproduzione, l'utente potrebbe farci clic per errore. Per evitare questo problema, nascondi il pulsante Registra durante la riproduzione. In questo modo, non è necessario gestire lo stato di Playingback in onClickRecord().
Modifica la funzione updateRecordButton() in HelloArActivity.java per nascondere il pulsante Registra quando l'app è nello stato 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;
}
}
Allo stesso modo, nascondi il pulsante Riproduzione quando l'utente sta registrando una sessione e modificalo in "Interrompi" quando l'utente sta riproducendo attivamente una sessione. In questo modo, possono interrompere la riproduzione senza dover attendere il completamento automatico.
Aggiungi una funzione updatePlaybackButton() in 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;
}
}
Infine, aggiorna onClickRecord() per chiamare updatePlaybackButton(). Aggiungi la seguente riga a HelloArActivity.java:
public void onClickRecord(View view) {
// ... omitted code ...
updatePlaybackButton(); // Add this line to the end of the function.
}
Selezionare un file con il pulsante Riproduci
Quando viene toccato, il pulsante Riproduzione deve consentire all'utente di selezionare un file da riprodurre. Su Android, la selezione dei file viene gestita nel selettore di file di sistema in un'altra attività. Ciò avviene tramite Storage Access Framework (SAF). Una volta che l'utente seleziona un file, l'app riceve un callback chiamato onActivityResult(). Avvierai la riproduzione effettiva all'interno di questa funzione di callback.
In HelloArActivity.java, crea una funzione onClickPlayback() per selezionare il file e interrompere la riproduzione.
// 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();
}
In HelloArActivity.java, crea una funzione selectFileToPlayback() che seleziona un file dal dispositivo. Per selezionare un file dal file system Android, utilizza un 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 è una costante per identificare questa richiesta. Puoi definirlo utilizzando qualsiasi valore segnaposto all'interno di HelloArActivity in HelloArActivity.java:
private int REQUEST_MP4_SELECTOR = 1;
Esegui l'override della funzione onActivityResult() in HelloArActivity.java per gestire il callback dal selettore di 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);
}
Consenti all'app di avviare la riproduzione
Una sessione ARCore richiede tre chiamate API per riprodurre un file MP4:
session.pause()session.setPlaybackDataset()session.resume()
In HelloArActivity.java, crea la funzione 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;
}
Consenti all'app di interrompere la riproduzione
Crea una funzione denominata stopPlayingback() in HelloArActivity.java per gestire le modifiche allo stato dell'app dopo:
- La riproduzione del file MP4 è stata interrotta dall'utente
- La riproduzione del file MP4 è stata completata automaticamente
Se l'utente ha interrotto la riproduzione, l'app deve tornare allo stato in cui si trovava al primo avvio.
// 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;
}
La riproduzione può anche interrompersi naturalmente dopo che il lettore ha raggiunto la fine del file MP4. In questo caso, stopPlayingback() dovrebbe riportare lo stato dell'app su Idle. In onDrawFrame(), controlla PlaybackStatus. Se è FINISHED, chiama la funzione stopPlayingback() sul thread UI.
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 ...
}
Riproduzione dal dispositivo di destinazione
È il momento di vedere cosa hai creato finora. Collega il dispositivo mobile alla macchina di sviluppo e fai clic su Esegui in Android Studio.
Quando l'app viene avviata, dovresti vedere una schermata con un pulsante rosso Registra a sinistra e un pulsante verde Riproduci a destra.

Tocca il pulsante Riproduzione e seleziona uno dei file MP4 che hai appena registrato. Se non vedi nomi di file che iniziano con arcore-, è possibile che il tuo dispositivo non mostri la cartella Film. In questo caso, vai alla cartella Modello di smartphone > Film utilizzando il menu nell'angolo in alto a sinistra. Potrebbe anche essere necessario attivare l'opzione Mostra memoria interna per visualizzare la cartella del modello di smartphone.


Tocca un nome file sullo schermo per selezionare il file MP4. L'app dovrebbe riprodurre il file MP4.

Una differenza tra la riproduzione di una sessione e la riproduzione di un video normale è che puoi interagire con la sessione registrata. Tocca un piano rilevato per posizionare i marcatori sullo schermo.

Cosa hai fatto in questo passaggio
- Aggiunto un pulsante per avviare e interrompere la riproduzione
- È stata implementata una funzione per avviare e interrompere la registrazione dell'app
- Riproduzione di una sessione ARCore registrata in precedenza sul dispositivo
5. Registrare dati aggiuntivi nel file MP4
Con ARCore 1.24, è possibile registrare informazioni aggiuntive nel file MP4. Puoi registrare il Pose dei posizionamenti degli oggetti AR, quindi, durante la riproduzione, creare gli oggetti AR nella stessa posizione.
Configura la nuova traccia da registrare
Definisci una nuova traccia con un UUID e un tag MIME 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 ...
}
Aggiorna il codice esistente per creare l'oggetto RecordingConfig con una chiamata a 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 ...
}
Salvare la posa di ancoraggio durante la registrazione
Ogni volta che l'utente tocca un piano rilevato, viene posizionato un marcatore AR su un Anchor, la cui posa viene aggiornata da ARCore.
Registra la posa di un Anchor nel frame in cui viene creato, se stai ancora registrando la sessione ARCore.
Modifica la funzione handleTap() 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 ...
}
Il motivo per cui manteniamo Pose relativo alla videocamera anziché Pose relativo al mondo è che l'origine del mondo di una sessione di registrazione e l'origine del mondo di una sessione di riproduzione non sono le stesse. L'origine del mondo di una sessione di registrazione inizia la prima volta che la sessione viene ripresa, quando viene chiamato per la prima volta Session.resume(). L'origine del mondo di una sessione di riproduzione inizia quando viene registrato il primo frame, quando Session.resume() viene chiamato per la prima volta dopo Session.startRecording().
Creazione di ancore di riproduzione
Ricreare un Anchor è semplice. Aggiungi una funzione denominata createRecordedAnchors() in 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);
}
}
Chiama createRecordedAnchors() nella funzione onDrawFrame() in 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 ...
}
Esegui il test sul dispositivo di destinazione
Collega il dispositivo mobile alla macchina di sviluppo e fai clic su Esegui in Android Studio.
Per prima cosa, tocca il pulsante Registra per registrare una sessione. Durante la registrazione, tocca gli aerei rilevati per posizionare alcuni marker AR.
Al termine della registrazione, tocca il pulsante Riproduzione e seleziona il file appena registrato. La riproduzione dovrebbe iniziare. Nota come i posizionamenti precedenti dei marker AR vengono visualizzati proprio mentre tocchi l'app.
Questo è tutto il codice che dovrai scrivere per questo codelab.
6. Complimenti
Congratulazioni, hai raggiunto la fine di questo codelab. Rivediamo cosa hai fatto in questo codelab:
- Crea ed esegui l'esempio ARCore Hello AR Java.
- È stato aggiunto un pulsante Registra all'app per salvare una sessione AR in un file MP4
- È stato aggiunto un pulsante di riproduzione all'app per riprodurre una sessione AR da un file MP4
- È stata aggiunta una nuova funzionalità per salvare gli ancoraggi creati dall'utente nel file MP4 per la riproduzione.