1. Giriş
AR deneyimini MP4 dosyasına kaydedip MP4 dosyasından oynatabilmek hem uygulama geliştiriciler hem de son kullanıcılar için faydalı olabilir.
Masanızdan yeni özellikleri hata ayıklama ve test etme
ARCore Record & Playback API'nin en basit kullanımı geliştiriciler içindir. Artık küçük bir kod değişikliğini test etmek için uygulamayı bir test cihazında oluşturup çalıştırmanız, USB kablosunu çıkarmanız ve etrafta dolaşmanız gerekmiyor. Artık test ortamında beklenen telefon hareketiyle yalnızca bir MP4 kaydetmeniz ve doğrudan masanızdan test yapmanız yeterli.
Farklı cihazlarda kayıt yapma ve oynatma
Kayıt ve Oynatma API'leri sayesinde bir kullanıcı bir cihazı kullanarak oturum kaydedebilir ve başka bir kullanıcı aynı oturumu farklı bir cihazda oynatabilir. Artırılmış gerçeklik deneyimlerini başka bir kullanıcıyla paylaşabilirsiniz. Birçok olasılık vardır.
İlk ARCore uygulamanızı mı oluşturuyorsunuz?
Bu codelab'i nasıl kullanacaksınız?
Ne oluşturacaksınız?
Bu codelab'de, hem bir AR deneyimini MP4 dosyasına kaydeden hem de aynı dosyadan deneyimi oynatan bir uygulama oluşturmak için Kayıt ve Oynatma API'sini kullanacaksınız. Öğrenecekleriniz:
- AR oturumunu MP4 dosyasına kaydetmek için Recording API'yi kullanma
- Bir AR oturumunu MP4 dosyasından yeniden oynatmak için Playback API'yi kullanma.
- Bir cihazda AR oturumu kaydetme ve başka bir cihazda oynatma
İhtiyacınız olanlar
Bu codelab'de, ARCore Android SDK ile oluşturulan Hello AR Java uygulamasını değiştireceksiniz. Bu adımları takip etmek için belirli donanım ve yazılımlara ihtiyacınız vardır.
Donanım gereksinimleri
- Geliştirici Seçenekleri etkinleştirilmiş ve USB üzerinden hata ayıklama özelliği açık olan, USB kablosuyla geliştirme makinenize bağlı bir ARCore destekli cihaz.
- Android Studio'yu çalıştırdığınız bir geliştirme makinesi.
- Geliştirme sırasında kitaplıkları indirmek için internet erişimi.
Yazılım gereksinimleri
- Geliştirme için kullandığınız ARCore cihazınızda AR için Google Play Hizmetleri (ARCore) 1.24 veya sonraki bir sürümün yüklü olması gerekir. Bu hizmet normalde Play Store üzerinden cihaza otomatik olarak yüklenir. Ayrıca ARCore destekli cihazlara manuel olarak da yükleyebilirsiniz.
- Geliştirme makinesinde Android Studio (3.1 veya sonraki bir sürüm) yüklü olmalıdır.
En iyi sonuçları elde etmek için ARCore hakkında temel bilgilere de sahip olmanız gerekir.
2. Geliştirme ortamınızı kurma
Geliştirme ortamınızı kurarak başlayın.
ARCore Android SDK'sını indirin
SDK'yı indirmek için seçeneğini tıklayın.
ARCore Android SDK'sını açın
Android SDK'yı makinenize indirdikten sonra dosyayı çıkartın ve arcore-android-sdk-1.24/samples/hello_ar_java dizinine gidin. Bu, üzerinde çalışacağınız uygulamanın kök dizinidir.

Hello AR Java'yı Android Studio'ya yükleme
Android Studio'yu başlatın ve Open an existing Android Studio project'i (Mevcut bir Android Studio projesini aç) tıklayın.

Açılan iletişim kutusunda arcore-android-sdk-1.24/samples/hello_ar_java simgesini seçin ve Aç'ı tıklayın.
Android Studio'nun projeyi senkronize etmesini bekleyin. Eksik bileşenler varsa proje, hata mesajlarıyla birlikte içe aktarılamayabilir. Devam etmeden önce bu sorunları düzeltin.
Örnek uygulamayı çalıştırma
- ARCore'un desteklendiği bir cihazı geliştirme makinenize bağlayın.
- Cihaz düzgün şekilde tanınırsa cihaz adının Android Studio'da gösterilmesi gerekir.

- Android Studio'nun uygulamayı cihaza yükleyip başlatması için Çalıştır düğmesini tıklayın veya Çalıştır > "app" uygulamasını çalıştır'ı seçin.

- Fotoğraf çekme ve video kaydetme izni isteyen bir istem görürsünüz. Uygulamaya kamera izni vermek için Bu uygulamayı kullanırken'i seçin. Ardından, cihazın ekranında gerçek dünyadaki ortamınızı görürsünüz.

- Uçakları taramak için cihazı yatay olarak hareket ettirin.
- Uygulama bir düzlem algıladığında beyaz bir ızgara görünür. İlgili düzleme işaretçi yerleştirmek için düzleme dokunun.

Bu adımda yaptıklarınız
- Hello AR Java projesini oluşturma
- Örnek uygulamayı ARCore destekli bir cihazda oluşturup çalıştırma
Ardından, bir AR oturumunu MP4 dosyasına kaydedeceksiniz.
3. ARCore oturumunu MP4 dosyasına kaydetme
Bu adımda kayıt özelliğini ekleyeceğiz. Şunlardan oluşur:
- Kaydı başlatmak veya durdurmak için bir düğme.
- MP4 dosyasını cihaza kaydetmek için depolama işlevleri.
- ARCore oturumu kaydını başlatmak veya durdurmak için yapılan aramalar.
Kayıt düğmesi için kullanıcı arayüzü ekleme
Kaydı uygulamadan önce kullanıcı arayüzüne bir düğme ekleyin. Böylece kullanıcı, ARCore'a kaydı ne zaman başlatacağını veya durduracağını bildirebilir.
Proje panelinde app/res/layout/activity_main.xml dosyasını açın.

Android Studio, app/res/layout/activity_main.xml dosyasını açtıktan sonra varsayılan olarak tasarım görünümünü kullanır. Kod görünümüne geçmek için sekmenin sağ üst köşesindeki Kod düğmesini tıklayın.

activity_main.xml içinde, yeni Record (Kaydet) düğmesini oluşturmak ve etkinlik işleyicisini onClickRecord() adlı bir yönteme ayarlamak için kapanış etiketinden önce aşağıdaki kodu ekleyin:
<!--
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" />
Yukarıdaki kodu ekledikten sonra geçici olarak bir hata gösterilebilir: Corresponding method handler 'public void onClickRecord(android.view.View)' not found". Bu beklenen bir durumdur. Birkaç adım sonra onClickRecord() işlevini oluşturarak hatayı çözeceksiniz.
Duruma göre düğmedeki metni değiştirme
Kaydet düğmesi hem kaydı hem de durdurmayı yönetir. Uygulama veri kaydetmediğinde "Kaydet" kelimesini göstermelidir. Uygulama veri kaydederken düğme, "Durdur" kelimesini gösterecek şekilde değişmelidir.
Uygulamanın, düğmeye bu işlevi verebilmesi için mevcut durumunu bilmesi gerekir. Aşağıdaki kod, uygulamanın çalışma durumunu temsil etmek için AppState adlı yeni bir enum oluşturur ve appState adlı özel bir üye değişkeni aracılığıyla belirli durum değişikliklerini izler. HelloArActivity.java sınıfının başına, HelloArActivity sınıfına ekleyin.
// Represents the app's working state.
public enum AppState {
Idle,
Recording
}
// Tracks app's specific state changes.
private AppState appState = AppState.Idle;
Artık uygulamanın dahili durumunu izleyebildiğinize göre, düğmenin metnini uygulamanın mevcut durumuna göre değiştiren updateRecordButton() adlı bir işlev oluşturun. HelloArActivity.java dosyasındaki HelloArActivity sınıfının içine aşağıdaki kodu ekleyin.
// 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;
}
}
Ardından, uygulamanın durumunu kontrol eden, bir sonraki duruma değiştiren ve düğmenin kullanıcı arayüzünü değiştirmek için updateRecordButton() işlevini çağıran onClickRecord() yöntemini oluşturun. HelloArActivity.java dosyasındaki HelloArActivity sınıfının içine aşağıdaki kodu ekleyin.
// 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();
}
Uygulamanın kayda başlamasını etkinleştirin
ARCore'da kayıt yapmaya başlamak için yalnızca iki şey yapmanız gerekir:
- Kayıt dosyası URI'sini bir
RecordingConfignesnesinde belirtin. RecordingConfignesnesiylesession.startRecordingadlı kişiyi arama
Geriye kalanlar ise yalnızca ortak metin kodudur: yapılandırma, günlük kaydı ve doğruluğu kontrol etme.
Verileri kaydedip MP4 URI'sine kaydeden startRecording() adlı yeni bir işlev oluşturun. HelloArActivity.java dosyasındaki HelloArActivity sınıfının içine aşağıdaki kodu ekleyin.
// 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;
}
Bir ARCore oturumunu güvenli bir şekilde duraklatmak ve devam ettirmek için HelloArActivity.java içinde pauseARCoreSession() ve resumeARCoreSession() oluşturun.
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;
}
Uygulamanın kaydı durdurmasını sağlama
Uygulamanızın yeni veri kaydetmesini durdurmak için HelloArActivity.java içinde stopRecording() adlı bir işlev oluşturun. Bu işlev session.stopRecording() işlevini çağırır ve uygulama kaydı durduramazsa konsol günlüğüne bir hata gönderir.
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;
}
Android 11'de kapsamlı depolama alanını kullanarak tasarım dosyalarını depolama
Bu codelab'deki depolamayla ilgili işlevler, yeni Android 11 kapsamlı depolama gereksinimlerine uygun olarak tasarlanmıştır.
Android 11'i hedeflemek için app/build.gradle dosyasında küçük değişiklikler yapın. Android Studio Proje panelinde bu dosya, Gradle Komut Dosyaları düğümünün altında, app modülüyle ilişkilendirilmiş olarak bulunur.

compileSdkVersion ve targetSdkVersion değerlerini 30 olarak değiştirin.
compileSdkVersion 30
defaultConfig {
targetSdkVersion 30
}
Kayıt için, paylaşılan Movie dizininde MP4 dosyası oluşturmak üzere Android MediaStore API'yi kullanın.
HelloArActivity.java içinde createMp4File() adlı bir işlev oluşturun:
// 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;
}
Depolama alanı izinlerini yönetme
Android 11 cihaz kullanıyorsanız kodu test etmeye başlayabilirsiniz. Android 10 veya önceki sürümlerin yüklü olduğu cihazları desteklemek için uygulamanın, verileri hedef cihazın dosya sistemine kaydetmesine izin vermeniz gerekir.
AndroidManifest.xml içinde, uygulamanın Android 11'den (API düzeyi 30) önce depolama okuma ve yazma izinlerine ihtiyacı olduğunu beyan edin.
<!-- 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" />
Çalışma zamanında WRITE_EXTERNAL_STORAGE izinlerini istemek için HelloArActivity.java içinde checkAndRequestStoragePermission() adlı bir yardımcı işlev ekleyin.
// 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;
}
API düzeyi 29 veya önceki bir sürümü kullanıyorsanız createMp4File() öğesinin en üstüne depolama izinleriyle ilgili bir kontrol ekleyin ve uygulama doğru izinlere sahip değilse işlevden erken çıkın. API düzeyi 30 (Android 11), MediaStore'daki dosyalara erişmek için depolama izni gerektirmez.
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 ...
}
Hedef cihazdan kayıt yapma
Şimdiye kadar neler geliştirdiğinizi görme zamanı. Mobil cihazınızı geliştirme makinenize bağlayın ve Android Studio'da Run'ı (Çalıştır) tıklayın.
Ekranın sol alt tarafında kırmızı bir Kaydet düğmesi görürsünüz. Bu düğmeye dokunduğunuzda metin Durdur olarak değişmelidir. Bir oturumu kaydetmek için cihazınızı hareket ettirin ve kaydı tamamlamak istediğinizde Durdur düğmesini tıklayın. Bu işlem, cihazınızın harici depolama alanına arcore-xxxxxx_xxxxxx.mp4 adlı yeni bir dosya kaydeder.

Cihazınızın harici depolama alanında yeni bir arcore-xxxxxx_xxxxxx.mp4 dosyası olmalıdır. Pixel 5 cihazlarda yol /storage/emulated/0/Movies/ şeklindedir. Yol, kayıt başlatıldıktan sonra Logcat penceresinde bulunabilir.
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
Kaydı görüntüleme
Kaydı görüntülemek için Google Files gibi bir dosya sistemi uygulaması kullanabilir veya kaydı geliştirme makinenize kopyalayabilirsiniz. Android cihazdaki dosyaları listelemek ve getirmek için kullanılan iki adb komutu aşağıda verilmiştir:
adb shell ls '$EXTERNAL_STORAGE/Movies/*'simgesine dokunarak cihazdaki harici depolama alanında bulunan Filmler dizinindeki dosyaları göstermeadb pull /absolute_path_from_previous_adb_shell_ls/arcore-xxxxxxxx_xxxxxx.mp4dosyayı cihazdan geliştirme makinesine kopyalamak için
Bu, bu iki komut kullanıldıktan sonraki örnek bir çıkıştır (macOS'ten alınmıştır):
$ 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
Bu adımda yaptıklarınız
- Kaydı başlatma ve durdurma düğmesi eklendi
- Kaydı başlatma ve durdurma işlevleri uygulandı.
- Uygulamayı cihazda test etme
- Kaydedilen MP4'ü makinenize kopyalayıp doğruladıysanız
Ardından, MP4 dosyasından bir AR oturumunu oynatacaksınız.
4. MP4 dosyasından ARCore oturumu oynatma
Artık Kaydet düğmesi ve kaydedilmiş oturumları içeren bazı MP4 dosyalarınız var. Şimdi bunları ARCore Playback API'yi kullanarak oynatacaksınız.
Oynatma düğmesi için kullanıcı arayüzü ekleme
Oynatma özelliğini uygulamadan önce kullanıcı arayüzüne bir düğme ekleyin. Böylece kullanıcı, oturumun oynatılmaya ne zaman başlanacağını ve ne zaman durdurulacağını ARCore'a bildirebilir.
Proje panelinde app/res/layout/activity_main.xml dosyasını açın.

activity_main.xml bölümünde, yeni Oynatma düğmesini oluşturmak ve etkinlik işleyicisini onClickPlayback() adlı bir yönteme ayarlamak için kapanış etiketinden önce aşağıdaki kodu ekleyin. Bu düğme, Kaydet düğmesine benzer ve ekranın sağ tarafında gösterilir.
<!--
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" />
Oynatma Sırasında Güncelleme Düğmeleri
Uygulama artık Playingback adlı yeni bir duruma sahip. Bunu işlemek için AppState enum'unu ve appState öğesini bağımsız değişken olarak alan tüm mevcut işlevleri güncelleyin.
Playingback değerini HelloArActivity.java içindeki AppState enum'ına ekleyin:
public enum AppState {
Idle,
Recording,
Playingback // New enum value.
}
Oynatma sırasında Kaydet düğmesi ekranda kalırsa kullanıcı yanlışlıkla bu düğmeyi tıklayabilir. Bunu önlemek için oynatma sırasında Kaydet düğmesini gizleyin. Bu sayede, Playingback durumunu onClickRecord() içinde işlemeniz gerekmez.
Uygulama Playingback durumundayken Kaydet düğmesini gizlemek için HelloArActivity.java içindeki updateRecordButton() işlevini değiştirin.
// 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;
}
}
Benzer şekilde, kullanıcı bir oturumu kaydederken Oynatma düğmesini gizleyin ve kullanıcı bir oturumu etkin bir şekilde oynatırken düğmeyi "Durdur" olarak değiştirin. Böylece, oynatmanın kendi kendine tamamlanmasını beklemeden durdurabilirler.
HelloArActivity.java'e updatePlaybackButton() işlevi ekleme:
// 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;
}
}
Son olarak, onClickRecord() işlevini updatePlaybackButton() işlevini çağıracak şekilde güncelleyin. HelloArActivity.java alanına aşağıdaki satırı ekleyin:
public void onClickRecord(View view) {
// ... omitted code ...
updatePlaybackButton(); // Add this line to the end of the function.
}
Oynatma düğmesini kullanarak dosya seçme
Oynatma düğmesine dokunulduğunda kullanıcının oynatılacak bir dosya seçmesine izin verilmelidir. Android'de dosya seçimi, başka bir etkinlikteki sistem dosyası seçicide yapılır. Bu işlem, Depolama Erişimi Çerçevesi (SAF) üzerinden yapılır. Kullanıcı bir dosya seçtiğinde uygulama, onActivityResult() adlı bir geri çağırma alır. Gerçek oynatmayı bu geri çağırma işlevinin içinde başlatırsınız.
HelloArActivity.java içinde, dosya seçimini yapacak ve oynatmayı durduracak bir onClickPlayback() işlevi oluşturun.
// 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();
}
HelloArActivity.java içinde, cihazdan dosya seçen bir selectFileToPlayback() işlevi oluşturun. Android dosya sisteminden bir dosya seçmek için ACTION_OPEN_DOCUMENT Intent kullanın.
// 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, bu isteği tanımlamak için kullanılan bir sabittir. HelloArActivity.java içindeki HelloArActivity öğesinde herhangi bir yer tutucu değeri kullanarak tanımlayabilirsiniz:
private int REQUEST_MP4_SELECTOR = 1;
Dosya seçiciden gelen geri çağırmayı işlemek için HelloArActivity.java içinde onActivityResult() işlevini geçersiz kılın.
// 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);
}
Çalmayı başlatmak için uygulamayı etkinleştirin
ARCore oturumunda MP4 dosyasını oynatmak için üç API çağrısı gerekir:
session.pause()session.setPlaybackDataset()session.resume()
HelloArActivity.java içinde startPlayingback() işlevini oluşturun.
// 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;
}
Uygulamanın oynatmayı durdurmasını etkinleştirme
Aşağıdaki işlemlerden sonra uygulama durumu değişikliklerini işlemek için HelloArActivity.java içinde stopPlayingback() adlı bir işlev oluşturun:
- MP4 oynatma, kullanıcı tarafından durduruldu
- MP4 oynatma kendi kendine tamamlandı
Kullanıcı oynatmayı durdurduysa uygulama, kullanıcı ilk başlattığında bulunduğu duruma geri dönmelidir.
// 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;
}
Oynatma, oynatıcı MP4 dosyasının sonuna ulaştıktan sonra da doğal olarak durabilir. Bu durumda stopPlayingback(), uygulamanın durumunu tekrar Idle olarak değiştirmelidir. onDrawFrame() bölümünde PlaybackStatus seçeneğini işaretleyin. Bu durumda FINISHED, kullanıcı arayüzü iş parçacığında stopPlayingback() işlevini çağırın.
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 ...
}
Hedef cihazdan oynatma
Şimdiye kadar neler geliştirdiğinizi görme zamanı. Mobil cihazınızı geliştirme makinenize bağlayın ve Android Studio'da Run'ı (Çalıştır) tıklayın.
Uygulama başlatıldığında solda kırmızı bir Kaydet düğmesi, sağda ise yeşil bir Oynat düğmesi içeren bir ekran görmeniz gerekir.

Oynatma düğmesine dokunun ve yeni kaydettiğiniz MP4 dosyalarından birini seçin. arcore- ile başlayan dosya adları görmüyorsanız cihazınız Filmler klasörünü göstermiyor olabilir. Bu durumda, sol üst köşedeki menüyü kullanarak Telefon modeli > Filmler klasörüne gidin. Telefon modeli klasörünü göstermek için Dahili depolamayı göster seçeneğini de etkinleştirmeniz gerekebilir.


MP4 dosyasını seçmek için ekranda bir dosya adına dokunun. Uygulama, MP4 dosyasını oynatmalıdır.

Bir oturumu oynatma ile normal bir videoyu oynatma arasındaki fark, kaydedilen oturumla etkileşimde bulunabilmenizdir. Ekrana işaretçi yerleştirmek için algılanan bir düzleme dokunun.

Bu adımda yaptıklarınız
- Oynatmayı başlatma ve durdurma düğmesi eklendi.
- Uygulamanın kaydı başlatıp durdurmasını sağlayan bir işlev uygulandı.
- Cihazda daha önce kaydedilmiş bir ARCore oturumunu oynatma
5. MP4 dosyasına ek veriler kaydetme
ARCore 1.24 ile MP4 dosyasına ek bilgiler kaydedebilirsiniz. AR nesne yerleşimlerinin Pose kaydedebilir, ardından oynatma sırasında AR nesnelerini aynı konumda oluşturabilirsiniz.
Yeni parçayı kaydetmek üzere yapılandırma
HelloArActivity.java içinde UUID ve MIME etiketiyle yeni bir parça tanımlayın.
// 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 ...
}
Mevcut kodu, addTrack() çağrısıyla RecordingConfig nesnesini oluşturacak şekilde güncelleyin.
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 ...
}
Kayıt sırasında sabit duruşu kaydetme
Kullanıcı, algılanan bir düzleme her dokunduğunda ARCore tarafından pozu güncellenen bir Anchor üzerine AR işaretçisi yerleştirilir.
ARCore oturumunu kaydetmeye devam ediyorsanız Anchor öğesinin oluşturulduğu karedeki pozunu kaydedin.
HelloArActivity.java içinde handleTap() işlevini değiştirin.
// 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 ...
}
Dünyaya göre Pose yerine kameraya göre Pose değerini kalıcı hale getirmemizin nedeni, bir kayıt oturumunun dünya başlangıcı ile bir oynatma oturumunun dünya başlangıcının aynı olmamasıdır. Bir kayıt oturumunun dünya kökeni, oturum ilk kez devam ettirildiğinde, yani Session.resume() ilk kez çağrıldığında başlar. Bir oynatma oturumunun dünya başlangıcı, ilk kare kaydedildiğinde, Session.resume() ilk kez sonra Session.startRecording() çağrıldığında başlar.
Oynatma bağlantısı oluşturma
Anchor yeniden oluşturmak kolaydır. HelloArActivity.java içinde createRecordedAnchors() adlı bir işlev ekleyin.
// 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);
}
}
HelloArActivity.java içindeki onDrawFrame() işlevinde createRecordedAnchors() işlevini çağırın.
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 ...
}
Hedef cihazda test etme
Mobil cihazınızı geliştirme makinenize bağlayın ve Android Studio'da Run'ı (Çalıştır) tıklayın.
Öncelikle bir oturum kaydetmek için Kaydet düğmesine dokunun. Kayıt sırasında, algılanan düzlemlere dokunarak birkaç AR işaretçisi yerleştirin.
Kayıt durduktan sonra Oynatma düğmesine dokunun ve yeni kaydettiğiniz dosyayı seçin. Çalma işlemi başlar. Önceki AR işaretleyici yerleşimlerinizin, uygulamaya dokunduğunuz sırada nasıl göründüğüne dikkat edin.
Bu codelab için yapmanız gereken tüm kodlama işlemi bu kadardır.
6. Tebrikler
Tebrikler, bu codelab'in sonuna geldiniz. Bu codelab'de yaptıklarınıza göz atalım:
- ARCore Hello AR Java örneğini oluşturup çalıştırın.
- Uygulamaya, AR oturumunu MP4 dosyasına kaydetmek için Kaydet düğmesi eklendi.
- MP4 dosyasındaki bir AR oturumunu oynatmak için uygulamaya oynatma düğmesi eklendi.
- Kullanıcı tarafından oluşturulan bağlantı noktalarını oynatma için MP4'e kaydetmeye yönelik yeni bir özellik eklendi.