1. Wprowadzenie
Możliwość zapisania doświadczenia AR w pliku MP4 i odtwarzania go z tego pliku może być przydatna zarówno dla deweloperów aplikacji, jak i dla użytkowników.
Debugowanie i testowanie nowych funkcji bez wychodzenia z biura
Najprostszym sposobem korzystania z interfejsu ARCore Record & Playback API jest używanie go przez deweloperów. Nie musisz już tworzyć i uruchamiać aplikacji na urządzeniu testowym, odłączać kabla USB i chodzić po pomieszczeniu, aby przetestować niewielką zmianę w kodzie. Teraz wystarczy nagrać plik MP4 w środowisku testowym z oczekiwanym ruchem telefonu i przeprowadzić test bezpośrednio przy biurku.
Nagrywanie i odtwarzanie na różnych urządzeniach
Za pomocą interfejsów Recording API i Playback API jeden użytkownik może nagrać sesję na jednym urządzeniu, a inny może odtworzyć tę samą sesję na innym urządzeniu. Możesz udostępnić doświadczenie AR innemu użytkownikowi. Możliwości jest wiele.
Czy to Twoja pierwsza aplikacja ARCore?
Jak zamierzasz wykorzystać to ćwiczenie?
Co utworzysz
W tym module dowiesz się, jak za pomocą interfejsu Recording & Playback API utworzyć aplikację, która nagrywa funkcje AR do pliku MP4 i odtwarza je z tego samego pliku. Dowiesz się:
- Jak używać interfejsu Recording API do zapisywania sesji AR w pliku MP4.
- Jak używać interfejsu Playback API do ponownego odtwarzania sesji AR z pliku MP4.
- Jak nagrać sesję AR na jednym urządzeniu i odtwarzać ją na innym.
Czego potrzebujesz
W tym laboratorium kodowania zmodyfikujesz aplikację Hello AR Java, która została utworzona za pomocą pakietu ARCore Android SDK. Aby wykonać te instrukcje, musisz mieć odpowiedni sprzęt i oprogramowanie.
Wymagania sprzętowe
- Urządzenie obsługujące ARCore z włączonymi opcjami programisty i włączonym debugowaniem USB, podłączone kablem USB do komputera używanego do programowania.
- komputer używany do programowania, na którym uruchamiasz Android Studio.
- Dostęp do internetu, aby pobierać biblioteki podczas programowania.
Wymagania dotyczące oprogramowania
- Usługi Google Play dla AR (ARCore) w wersji 1.24 lub nowszej na urządzeniu deweloperskim z ARCore. Ta usługa jest zwykle instalowana automatycznie na urządzeniu za pomocą Sklepu Play. Możesz też zainstalować ją ręcznie na urządzeniu obsługującym ARCore.
- Android Studio (wersja 3.1 lub nowsza) na komputerze używanym do programowania.
Aby uzyskać najlepsze wyniki, warto też znać podstawy ARCore.
2. Konfigurowanie środowiska programistycznego
Zacznij od skonfigurowania środowiska programistycznego.
Pobieranie pakietu ARCore Android SDK
Kliknij , aby pobrać pakiet SDK.
Rozpakuj pakiet ARCore Android SDK
Po pobraniu pakietu Android SDK na komputer rozpakuj plik i przejdź do katalogu arcore-android-sdk-1.24/samples/hello_ar_java. Jest to katalog główny aplikacji, z którą będziesz pracować.

Wczytywanie Hello AR Java do Android Studio
Uruchom Android Studio i kliknij Open an existing Android Studio project (Otwórz istniejący projekt Android Studio).

W wyświetlonym oknie dialogowym wybierz arcore-android-sdk-1.24/samples/hello_ar_java i kliknij Otwórz.
Poczekaj, aż Android Studio zakończy synchronizację projektu. Jeśli brakuje komponentu, importowanie projektu może się nie powieść i wyświetlić komunikaty o błędach. Rozwiąż te problemy, zanim przejdziesz dalej.
Uruchamianie przykładowej aplikacji
- Podłącz urządzenie obsługujące ARCore do komputera używanego do programowania.
- Jeśli urządzenie zostanie prawidłowo rozpoznane, jego nazwa powinna pojawić się w Androidzie Studio.

- Kliknij przycisk Uruchom lub wybierz Uruchom > Uruchom „aplikację”, aby Android Studio zainstalowało i uruchomiło aplikację na urządzeniu.

- Zobaczysz komunikat z prośbą o uprawnienia do robienia zdjęć i nagrywania filmów. Aby przyznać aplikacji uprawnienia do korzystania z kamery, wybierz Podczas korzystania z tej aplikacji. Na ekranie urządzenia zobaczysz wtedy rzeczywiste otoczenie.

- Przesuwaj urządzenie poziomo, aby wyszukać samoloty.
- Gdy aplikacja wykryje płaszczyznę, pojawi się biała siatka. Kliknij go, aby umieścić znacznik na tej płaszczyźnie.

Co zostało zrobione na tym etapie
- Konfigurowanie projektu Hello AR w Javie
- Kompilowanie i uruchamianie przykładowej aplikacji na urządzeniu obsługującym ARCore
Następnie nagrasz sesję AR do pliku MP4.
3. Nagrywanie sesji ARCore do pliku MP4
W tym kroku dodamy funkcję nagrywania. Składa się z:
- przycisk rozpoczynania i zatrzymywania nagrywania;
- Funkcje pamięci masowej umożliwiające zapisanie pliku MP4 na urządzeniu.
- Wywołania rozpoczynające i zatrzymujące nagrywanie sesji ARCore.
Dodawanie interfejsu przycisku nagrywania
Zanim wdrożysz nagrywanie, dodaj w interfejsie przycisk, aby użytkownik mógł informować ARCore, kiedy rozpocząć lub zakończyć nagrywanie.
W panelu Projekt otwórz plik app/res/layout/activity_main.xml.

Domyślnie po otwarciu pliku app/res/layout/activity_main.xml Android Studio użyje widoku projektu. Aby przejść do widoku kodu, w prawym górnym rogu karty kliknij przycisk Kod.

W pliku activity_main.xml dodaj ten kod przed tagiem zamykającym, aby utworzyć nowy przycisk Record i ustawić jego moduł obsługi zdarzeń na metodę o nazwie 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" />
Po dodaniu powyższego kodu może się tymczasowo wyświetlić błąd: Corresponding method handler 'public void onClickRecord(android.view.View)' not found". To normalne. W kolejnych krokach rozwiążesz ten błąd, tworząc funkcję onClickRecord().
Zmiana tekstu na przycisku w zależności od stanu
Przycisk Nagrywaj służy zarówno do nagrywania, jak i do zatrzymywania nagrywania. Gdy aplikacja nie rejestruje danych, powinna wyświetlać słowo „Nagrywaj”. Gdy aplikacja rejestruje dane, przycisk powinien zmienić się tak, aby wyświetlać słowo „Stop”.
Aby przycisk działał w ten sposób, aplikacja musi znać jego bieżący stan. Poniższy kod tworzy nowy wyliczenie o nazwie AppState, które reprezentuje stan działania aplikacji, i śledzi konkretne zmiany stanu za pomocą prywatnej zmiennej składowej o nazwie appState. Dodaj go do HelloArActivity.java na początku klasy HelloArActivity.
// Represents the app's working state.
public enum AppState {
Idle,
Recording
}
// Tracks app's specific state changes.
private AppState appState = AppState.Idle;
Teraz, gdy możesz śledzić stan wewnętrzny aplikacji, utwórz funkcję o nazwie updateRecordButton(), która zmienia tekst przycisku w zależności od bieżącego stanu aplikacji. Dodaj ten kod w klasie HelloArActivity w pliku 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;
}
}
Następnie utwórz metodę onClickRecord(), która sprawdza stan aplikacji, zmienia go na następny i wywołuje metodę updateRecordButton(), aby zmienić interfejs przycisku. Dodaj ten kod w klasie HelloArActivity w pliku 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();
}
Włącz aplikację, aby rozpocząć nagrywanie
Aby rozpocząć nagrywanie w ARCore, wystarczy wykonać 2 czynności:
- Określ identyfikator URI pliku nagrania w obiekcie
RecordingConfig. - Wywołaj
session.startRecordingza pomocą obiektuRecordingConfig.
Reszta to tylko powtarzalny kod: konfiguracja, logowanie i sprawdzanie poprawności.
Utwórz nową funkcję o nazwie startRecording(), która rejestruje dane i zapisuje je w formacie MP4 URI. Dodaj ten kod w klasie HelloArActivity w pliku 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;
}
Aby bezpiecznie wstrzymać i wznowić sesję ARCore, utwórz pauseARCoreSession() i resumeARCoreSession() w 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;
}
Włącz możliwość zatrzymania nagrywania przez aplikację
Utwórz funkcję o nazwie stopRecording() w HelloArActivity.java, aby zatrzymać rejestrowanie nowych danych przez aplikację. Ta funkcja wywołuje session.stopRecording() i wysyła błąd do dziennika konsoli, jeśli aplikacja nie może zatrzymać nagrywania.
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;
}
Projektowanie przechowywania plików z użyciem ograniczonego dostępu do miejsca na dane w Androidzie 11
Funkcje związane z pamięcią w tym Codelabs są zaprojektowane zgodnie z nowymi wymaganiami dotyczącymi ograniczonego dostępu do miejsca na dane w Androidzie 11.
Wprowadź niewielkie zmiany w pliku app/build.gradle, aby kierować reklamy na Androida 11. W panelu Projekt w Android Studio ten plik znajduje się w węźle Skrypty Gradle powiązanym z modułem aplikacji.

Zmień wartości compileSdkVersion i targetSdkVersion na 30.
compileSdkVersion 30
defaultConfig {
targetSdkVersion 30
}
Do nagrywania używaj interfejsu Android MediaStore API, aby utworzyć plik MP4 we wspólnym katalogu Movie.
Utwórz funkcję o nazwie createMp4File() w 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;
}
Obsługa uprawnień dostępu do pamięci
Jeśli używasz urządzenia z Androidem 11, możesz rozpocząć testowanie kodu. Aby obsługiwać urządzenia z Androidem 10 lub starszym, musisz przyznać aplikacji uprawnienia do przechowywania danych, aby zapisywać dane w systemie plików urządzenia docelowego.
W AndroidManifest.xml zadeklaruj, że aplikacja wymaga uprawnień do odczytu i zapisu danych przed Androidem 11 (poziom 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" />
Dodaj w HelloArActivity.java funkcję pomocniczą o nazwie checkAndRequestStoragePermission(), która będzie wysyłać żądanie uprawnień WRITE_EXTERNAL_STORAGE w czasie działania programu.
// 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;
}
Jeśli używasz poziomu API 29 lub starszego, dodaj na początku funkcji createMp4File() sprawdzenie uprawnień do pamięci i wcześniej zakończ działanie funkcji, jeśli aplikacja nie ma odpowiednich uprawnień. Poziom API 30 (Android 11) nie wymaga uprawnień do pamięci, aby uzyskać dostęp do plików w 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 ...
}
Nagrywanie z urządzenia docelowego
Czas sprawdzić, co udało Ci się do tej pory stworzyć. Podłącz urządzenie mobilne do komputera używanego do programowania i w Androidzie Studio kliknij Uruchom.
W lewym dolnym rogu ekranu powinien pojawić się czerwony przycisk Nagrywaj. Po kliknięciu tego przycisku tekst powinien zmienić się na Stop. Poruszaj urządzeniem, aby nagrać sesję, a gdy chcesz zakończyć nagrywanie, kliknij przycisk Zatrzymaj. Spowoduje to zapisanie nowego pliku o nazwie arcore-xxxxxx_xxxxxx.mp4 w pamięci zewnętrznej urządzenia.

Teraz na urządzeniu zewnętrznej pamięci masowej powinien znajdować się nowy plik arcore-xxxxxx_xxxxxx.mp4. Na urządzeniach Pixel 5 ścieżka to /storage/emulated/0/Movies/. Ścieżkę można znaleźć w oknie Logcat po rozpoczęciu nagrywania.
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
Wyświetlanie nagrania
Aby wyświetlić nagranie, możesz użyć aplikacji do obsługi systemu plików, np. Files by Google, lub skopiować je na komputer używany do programowania. Oto 2 polecenia adb, które umożliwiają wyświetlanie listy plików na urządzeniu z Androidem i pobieranie ich:
adb shell ls '$EXTERNAL_STORAGE/Movies/*', aby wyświetlić pliki w katalogu Filmy w pamięci zewnętrznej urządzenia.adb pull /absolute_path_from_previous_adb_shell_ls/arcore-xxxxxxxx_xxxxxx.mp4– skopiuj plik z urządzenia na komputer używany do programowania.
Oto przykładowe dane wyjściowe po użyciu tych 2 poleceń (w systemie 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
Co zostało zrobione na tym etapie
- Dodano przycisk rozpoczynania i zatrzymywania nagrywania.
- Wdrożono funkcje rozpoczynania i zatrzymywania nagrywania.
- Przetestowano aplikację na urządzeniu
- skopiowano nagrany plik MP4 na komputer i sprawdzono go;
Następnie odtworzysz sesję AR z pliku MP4.
4. Odtwarzanie sesji ARCore z pliku MP4
Masz teraz przycisk Nagrywaj i kilka plików MP4 zawierających nagrane sesje. Teraz odtwórz je za pomocą interfejsu ARCore Playback API.
Dodawanie interfejsu przycisku odtwarzania
Zanim wdrożysz odtwarzanie, dodaj w interfejsie przycisk, aby użytkownik mógł informować ARCore, kiedy ma rozpocząć i zakończyć odtwarzanie sesji.
W panelu Projekt otwórz plik app/res/layout/activity_main.xml.

W activity_main.xml dodaj poniższy kod przed tagiem zamykającym, aby utworzyć nowy przycisk Odtwórz i ustawić jego procedurę obsługi zdarzeń na metodę o nazwie onClickPlayback(). Będzie on podobny do przycisku Nagrywaj i wyświetli się po prawej stronie ekranu.
<!--
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" />
Aktualizowanie przycisków podczas odtwarzania
Aplikacja ma teraz nowy stan o nazwie Playingback. Zaktualizuj wyliczenie AppState i wszystkie istniejące funkcje, które przyjmują appState jako argument, aby obsługiwać tę zmianę.
Dodaj Playingback do wyliczenia AppState w pliku HelloArActivity.java:
public enum AppState {
Idle,
Recording,
Playingback // New enum value.
}
Jeśli przycisk Nagrywaj jest nadal widoczny na ekranie podczas odtwarzania, użytkownik może go przypadkowo kliknąć. Aby tego uniknąć, ukryj przycisk Nagrywaj podczas odtwarzania. Dzięki temu nie musisz obsługiwać stanu Playingback w onClickRecord().
Zmodyfikuj funkcję updateRecordButton() w HelloArActivity.java, aby ukryć przycisk Nagraj, gdy aplikacja jest w stanie 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;
}
}
Podobnie ukryj przycisk Odtwarzanie, gdy użytkownik nagrywa sesję, i zmień go na „Stop”, gdy użytkownik aktywnie odtwarza sesję. Dzięki temu mogą zatrzymać odtwarzanie bez czekania na jego zakończenie.
Dodaj funkcję updatePlaybackButton() w 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;
}
}
Na koniec zaktualizuj funkcję onClickRecord(), aby wywoływała funkcję updatePlaybackButton(). Dodaj do pliku HelloArActivity.java ten wiersz:
public void onClickRecord(View view) {
// ... omitted code ...
updatePlaybackButton(); // Add this line to the end of the function.
}
Wybieranie pliku za pomocą przycisku Odtwórz
Po kliknięciu przycisku Odtwarzanie użytkownik powinien mieć możliwość wybrania pliku do odtworzenia. Na Androidzie wybór plików odbywa się w oknie wyboru plików systemowych w innym działaniu. Odbywa się to za pomocą platformy Storage Access Framework (SAF). Gdy użytkownik wybierze plik, aplikacja otrzyma wywołanie zwrotne o nazwie onActivityResult(). W tej funkcji zwrotnej rozpoczniesz odtwarzanie.
W pliku HelloArActivity.java utwórz funkcję onClickPlayback(), która będzie wybierać plik i zatrzymywać odtwarzanie.
// 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();
}
W HelloArActivity.java utwórz funkcję selectFileToPlayback(), która wybiera plik z urządzenia. Aby wybrać plik z systemu plików Androida, użyj ACTION_OPEN_DOCUMENTintencji.
// 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 to stała identyfikująca tę prośbę. Możesz ją zdefiniować za pomocą dowolnej wartości zmiennej w elemencie HelloArActivity w aplikacji HelloArActivity.java:
private int REQUEST_MP4_SELECTOR = 1;
Zastąp funkcję onActivityResult() w HelloArActivity.java, aby obsługiwać wywołanie zwrotne z okna wyboru plików.
// 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);
}
Włącz aplikację, aby rozpocząć odtwarzanie
Odtwarzanie pliku MP4 w sesji ARCore wymaga 3 wywołań interfejsu API:
session.pause()session.setPlaybackDataset()session.resume()
W HelloArActivity.java utwórz funkcję 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;
}
Włączanie aplikacji, aby zatrzymać odtwarzanie
Utwórz w pliku HelloArActivity.java funkcję o nazwie stopPlayingback(), która będzie obsługiwać zmiany stanu aplikacji po:
- Odtwarzanie pliku MP4 zostało zatrzymane przez użytkownika
- Odtwarzanie pliku MP4 zakończyło się samoistnie
Jeśli użytkownik zatrzyma odtwarzanie, aplikacja powinna wrócić do stanu, w jakim była, gdy użytkownik ją uruchomił.
// 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;
}
Odtwarzanie może się też naturalnie zakończyć, gdy odtwarzacz dotrze do końca pliku MP4. W takiej sytuacji stopPlayingback() powinna przywrócić stan aplikacji do Idle. W polu onDrawFrame() sprawdź PlaybackStatus. Jeśli jest to FINISHED, wywołaj funkcję stopPlayingback() w wątku 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 ...
}
Odtwarzanie na urządzeniu docelowym
Czas sprawdzić, co udało Ci się do tej pory stworzyć. Podłącz urządzenie mobilne do komputera używanego do programowania i w Androidzie Studio kliknij Uruchom.
Po uruchomieniu aplikacji powinien pojawić się ekran z czerwonym przyciskiem Nagrywaj po lewej stronie i zielonym przyciskiem Odtwarzaj po prawej stronie.

Kliknij przycisk Odtwórz i wybierz jeden z nagranych przed chwilą plików MP4. Jeśli nie widzisz żadnych nazw plików zaczynających się od arcore-, być może urządzenie nie wyświetla folderu Filmy. W tym przypadku otwórz folder Model telefonu > Filmy, korzystając z menu w lewym górnym rogu. Aby wyświetlić folder modelu telefonu, może być też konieczne włączenie opcji Pokaż pamięć wewnętrzną.


Kliknij nazwę pliku na ekranie, aby wybrać plik MP4. Aplikacja powinna odtworzyć plik MP4.

Różnica między odtwarzaniem sesji a odtwarzaniem zwykłego filmu polega na tym, że możesz wchodzić w interakcje z nagraną sesją. Kliknij wykrytą płaszczyznę, aby umieścić znaczniki na ekranie.

Co zostało zrobione na tym etapie
- Dodaliśmy przycisk rozpoczynania i zatrzymywania odtwarzania.
- Wprowadziliśmy funkcję rozpoczynania i zatrzymywania nagrywania w aplikacji.
- odtwarzanie na urządzeniu wcześniej zarejestrowanej sesji ARCore;
5. Zapisywanie dodatkowych danych w pliku MP4
W ARCore 1.24 można nagrywać dodatkowe informacje w pliku MP4. Możesz nagrywać Pose umiejscowienia obiektów AR, a potem podczas odtwarzania tworzyć obiekty AR w tych samych miejscach.
Skonfiguruj nowy ślad do nagrywania
Zdefiniuj nowy ścieżkę za pomocą identyfikatora UUID i tagu MIME w 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 ...
}
Zaktualizuj istniejący kod, aby utworzyć obiekt RecordingConfig za pomocą wywołania 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 ...
}
Zapisywanie pozycji kotwicy podczas nagrywania
Za każdym razem, gdy użytkownik dotknie wykrytej płaszczyzny, na Anchor zostanie umieszczony marker AR, którego położenie będzie aktualizowane przez ARCore.
Zarejestruj pozę Anchor w klatce, w której została utworzona, jeśli nadal nagrywasz sesję ARCore.
Zmodyfikuj funkcję handleTap() w 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 ...
}
Zapisujemy względne położenie kamery Pose, a nie względne położenie świata Pose, ponieważ początek układu współrzędnych świata w sesji nagrywania i w sesji odtwarzania nie jest taki sam. Światowy punkt początkowy sesji nagrywania jest ustawiany przy pierwszym wznowieniu sesji, gdy po raz pierwszy zostanie wywołana funkcja Session.resume(). Początek sesji odtwarzania w przestrzeni świata następuje, gdy zostanie zarejestrowana pierwsza klatka, czyli gdy po raz pierwszy zostanie wywołana funkcja Session.resume() po Session.startRecording().
Tworzenie punktu odtwarzania
Odtworzenie Anchor jest proste. Dodaj funkcję o nazwie createRecordedAnchors() w 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);
}
}
Wywołaj funkcję createRecordedAnchors() w funkcji onDrawFrame() w języku 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 ...
}
Testowanie na urządzeniu docelowym
Podłącz urządzenie mobilne do komputera używanego do programowania i w Androidzie Studio kliknij Uruchom.
Najpierw kliknij przycisk Nagraj, aby nagrać sesję. Podczas nagrywania kliknij wykryte samoloty, aby umieścić kilka markerów AR.
Po zakończeniu nagrywania kliknij przycisk Odtwórz i wybierz nagrany plik. Odtwarzanie powinno się rozpocząć. Zwróć uwagę, że poprzednie miejsca docelowe markerów AR pojawiają się dokładnie w momencie, gdy klikasz aplikację.
To wszystko, co musisz zrobić w tym ćwiczeniu.
6. Gratulacje
Gratulacje! To już koniec tego ćwiczenia w Codelabs. Podsumujmy, co udało Ci się zrobić w tym ćwiczeniu:
- Zbuduj i uruchom przykładową aplikację ARCore Hello AR Java.
- Dodaliśmy do aplikacji przycisk nagrywania, który umożliwia zapisanie sesji AR w pliku MP4.
- Dodaliśmy do aplikacji przycisk odtwarzania, który umożliwia odtwarzanie sesji AR z pliku MP4.
- Dodaliśmy nową funkcję zapisywania kotwic utworzonych przez użytkownika w pliku MP4 w celu odtwarzania.