1. Giriş
AR deneyimini MP4 dosyasına kaydedip MP4 dosyasından oynatma imkanı, hem uygulama geliştiriciler hem de son kullanıcılar için faydalı olabilir.
Masanızda hata ayıklayın ve yeni özellikleri test edin
ARCore Kaydı ve Oynatma API'si geliştiriciler içindir. Uygulamayı bir test cihazında oluşturup çalıştırmanız, USB kablosunu çıkarmanız ve yalnızca küçük bir kod değişikliğini test etmek için etrafta dolaşmanız gereken günler geride kaldı. Artık bir MP4 dosyasını test ortamında beklenen telefon hareketiyle kaydedip doğrudan masanızdan test etmeniz gerekiyor.
İçerikleri farklı cihazlardan kaydedip oynatın
Kayıt ve Oynatma API'leriyle, bir kullanıcı bir oturumu bir cihazı kullanarak kaydedebilir ve başka bir kullanıcı aynı oturumu farklı bir cihazda oynatabilir. Artırılmış gerçeklik (AR) deneyimini başka bir kullanıcıyla paylaşmak mümkündür. Çok sayıda olasılık var.
İlk kez bir ARCore uygulaması mı geliştiriyorsunuz?
Bu codelab'i nasıl kullanacaksınız?
Neler oluşturacaksınız?
Bu codelab'de Kayıt ve Hem AR deneyimini MP4 dosyasına kaydeden hem de deneyimi aynı dosyadan oynatan bir uygulama oluşturmak için Playback API'sini kullanın. Öğrenecekleriniz:
- AR oturumunu MP4 dosyasına kaydetmek için kayıt API'sini kullanma.
- Bir MP4 dosyasından AR oturumunu tekrar oynatmak için Playback API'sini kullanma.
- AR oturumunu bir cihazda kaydedip başka bir cihazda tekrar oynatma.
Gerekenler
Bu codelab'de, ARCore Android SDK ile oluşturulan Hello AR Java uygulamasında değişiklik yapacaksınız. Çalışmak için belirli donanım ve yazılımlara ihtiyacınız olacaktır.
Donanım gereksinimleri
- Geliştirici Seçenekleri'nin etkin olduğu, ARCore destekli cihaz USB kablosuyla geliştirme makinenize bağlanmış ve USB üzerinden hata ayıklama etkin.
- Android Studio'yu çalıştırdığınız bir geliştirme makinesi.
- Geliştirme sırasında kitaplıkları indirmek için internete erişim.
Yazılım gereksinimleri
- Geliştirme ARCore cihazınızda AR için Google Play Hizmetleri (ARCore) 1.24 veya sonraki bir sürüm. Bu hizmet normalde Play Store üzerinden cihaza otomatik olarak yüklenir. Uygulamayı ARCore destekli cihaza manuel olarak da yükleyebilirsiniz.
- Geliştirme makinesinde Android Studio (v3.1 veya sonraki bir sürüm).
Ayrıca en iyi sonuçlar için ARCore hakkında temel bilgilere sahip olmanız gerekir.
2. Geliştirme ortamınızı ayarlama
Geliştirme ortamınızı kurarak başlayın.
ARCore Android SDK'sını indirin
SDK'yı indirmek için öğesini tıklayın.
ARCore Android SDK'sının sıkıştırmasını açın
Android SDK'yı makinenize indirdikten sonra, sıkıştırılmış dosyayı açı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 Mevcut bir Android Studio projesini aç'ı tıklayın.
Açılan iletişim penceresinde arcore-android-sdk-1.24/samples/hello_ar_java
'i seçin ve Aç'ı tıklayın.
Android Studio'nun proje senkronizasyonunu tamamlamasını bekleyin. Eksik bileşen varsa projenin içe aktarılması hata mesajları vererek başarısız olabilir. Devam etmeden önce bu sorunları düzeltin.
Örnek uygulamayı çalıştırma
- Geliştirme makinenize ARCore destekli bir cihaz bağlayın.
- Cihaz düzgün şekilde tanınırsa Android Studio'da cihazın adı gösterilir. .
- Çalıştır düğmesini tıklayın veya Çalıştır > Android Studio'nun cihaza yüklenmesini ve uygulamayı çalıştırmasını sağlamak için "uygulamayı" çalıştırın.
- Resim çekmek ve video kaydetmek için izin isteyen bir istem görürsünüz. Uygulamaya Kamera izinleri vermek için Bu uygulamayı kullanırken'i seçin. Daha sonra, gerçek dünyadaki ortamınızı cihazın ekranında görürsünüz. .
- Uçakları taramak için cihazı yatay olarak hareket ettirin.
- Uygulama bir uçak algıladığında beyaz bir ızgara görünür. Uçağa işaretçi yerleştirmek için bu filtreye dokunun. .
Bu adımda yaptıklarınız
- Hello AR Java projesini ayarlama
- Örnek uygulamayı ARCore destekli bir cihazda derleyip çalıştırma
Şimdi, bir MP4 dosyasına bir AR oturumu 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 kullanılan düğme.
- Depolama işlevleri, MP4 dosyasını cihaza kaydetmek için kullanılır.
- ARCore oturum kaydını başlatmak veya durdurmak için çağrılar.
Add UI for Record (Kayıt Kullanıcı Arayüzü Ekle) düğmesi
Kaydı uygulamadan önce kullanıcı arayüzüne bir düğme ekleyin. Böylece, kullanıcının kaydı ne zaman başlatacağı veya durduracağı konusunda ARCore'u bilgilendirmesi gerekir.
Proje panelinde app/res/layout/activity_main.xml
dosyasını açın.
Varsayılan olarak, Android Studio app/res/layout/activity_main.xml
dosyasını açtıktan sonra 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
ürününde, yeni Record düğmesini oluşturmak ve etkinlik işleyicisini onClickRecord()
adlı yönteme ayarlamak için kapanış etiketinin önüne 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. Bu hatayı sonraki birkaç adımda onClickRecord()
işlevini oluşturarak çözeceksiniz.
Düğmedeki metni duruma göre değiştir
Kaydet düğmesi aslında hem kaydetme hem de durdurma işlemlerini gerçekleştirir. Uygulama veri kaydetmiyorken "Record" (Kaydet) kelimesi görünmelidir. Uygulama veri kaydederken düğme, "Durdur" kelimesini gösterecek şekilde değişmelidir.
Uygulamanın, düğmeye bu işlevi sağlaması için mevcut durumunu bilmesi gerekir. Aşağıdaki kod, uygulamanın çalışma durumunu temsil eden AppState
adlı yeni bir sıralama oluşturur ve belirli durum değişikliklerini appState
adlı gizli üye değişkeni aracılığıyla izler. Bunu HelloArActivity
dersinin başındaki HelloArActivity.java
klasörüne 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 takip edebileceğinize göre updateRecordButton()
adlı bir işlev oluşturabilirsiniz. Bu işlev, düğmenin metnini uygulamanın mevcut durumuna göre değiştirir. Aşağıdaki kodu HelloArActivity.java
uygulamasındaki HelloArActivity
sınıfına 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 sonrakiyle değiştiren ve düğmenin kullanıcı arayüzünü değiştirmek için updateRecordButton()
çağrısı yapan onClickRecord()
yöntemini oluşturun. Aşağıdaki kodu HelloArActivity.java
uygulamasındaki HelloArActivity
sınıfına 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();
}
Kaydetmeye başlamak için uygulamayı etkinleştirin
ARCore'da kayda başlamak için yalnızca iki şey yapmanız gerekir:
RecordingConfig
nesnesinde kayıt dosyası URI'sini belirtin.RecordingConfig
nesnesiylesession.startRecording
öğesini çağır
Gerisi sadece standart kodlardır: yapılandırma, günlük kaydı ve doğruluk kontrolü.
Verileri kaydeden ve bir MP4 URI'sına kaydeden startRecording()
adlı yeni bir işlev oluşturun. Aşağıdaki kodu HelloArActivity.java
uygulamasındaki HelloArActivity
sınıfına 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;
}
ARCore oturumunu güvenli bir şekilde duraklatmak ve devam ettirmek için HelloArActivity.java
uygulamasında 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;
}
Kaydı durdurmak için uygulamayı etkinleştirin
Uygulamanızın yeni veri kaydetmesini durdurmak için HelloArActivity.java
ürününde stopRecording()
adlı bir işlev oluşturun. Bu işlev, uygulamanın kaydı durduramaması durumunda session.stopRecording()
öğesini çağırır ve 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 kapsamlı depolama alanını kullanarak dosya depolama tasarlama
Bu codelab'deki depolama alanıyla ilgili işlevler, Android 11 kapsamlı depolama alanı gereksinimlerine göre tasarlanmıştır.
Android 11'i hedeflemek için app/build.gradle
dosyasında bazı küçük değişiklikler yapın. Android Studio Proje panelinde bu dosya, app modülüyle ilişkilendirilmiş Gradle Komut Dosyaları düğümünün altındadır.
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ını 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 izinlerini işleme
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, hedef cihazın dosya sistemine veri kaydetmesi için depolama alanı izinleri vermeniz gerekir.
AndroidManifest.xml
bölümünde, Android 11'den (API düzeyi 30) önce uygulamanın depolama alanında 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ı sırasında WRITE_EXTERNAL_STORAGE
izinlerini istemek için HelloArActivity.java
koduna checkAndRequestStoragePermission()
adlı yardımcı bir 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 sürümlerden birini kullanıyorsanız createMp4File()
öğesinin üst kısmına depolama izinleri için 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 alanı 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 kaydedin
Şimdiye kadar neler geliştirdiğinizi görme zamanı. Mobil cihazınızı geliştirme makinenize bağlayın ve Android Studio'da Çalıştır'ı tıklayın.
Ekranın sol alt tarafında kırmızı bir Kayıt düğmesi görürsünüz. Düğmeye dokunduğunuzda metin Durdur olarak değişir. Bir oturum kaydetmek için cihazınızı hareket ettirin ve kaydı tamamlamak istediğiniz zaman Durdur düğmesini tıklayın. Bu işlemle cihazınızın harici depolama alanına arcore-xxxxxx_xxxxxx.mp4
adlı yeni bir dosya kaydedilir.
Artık 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. Aşağıda, Android cihazdaki dosyaları listelemek ve getirmek için kullanılan iki adb komutu verilmiştir:
- Filmler dizinindeki dosyaları cihazdaki harici depolama alanında göstermek için
adb shell ls '$EXTERNAL_STORAGE/Movies/*'
- Dosyayı cihazdan geliştirme makinesine kopyalamak için
adb pull /absolute_path_from_previous_adb_shell_ls/arcore-xxxxxxxx_xxxxxx.mp4
Bu, aşağıdaki iki komutu kullandıktan sonra örnek bir çıkıştır (macOS'ten):
$ 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şlatmak ve durdurmak için düğme eklendi
- Kaydı başlatmak ve durdurmak için işlevler uygulandı
- Uygulamayı cihazda test etme
- Kaydedilen MP4'ü makinenize kopyalandı ve doğruladı
Ardından, bir MP4 dosyasından bir AR oturumunu oynatırsınız.
4. MP4 dosyasından ARCore oturumunu oynatma
Artık bir Record düğmeniz ve kaydedilmiş oturumları içeren bazı MP4 dosyalarınız var. Şimdi bunları ARCore Playback API'sini kullanarak oynatacaksınız.
Oynat düğmesi için kullanıcı arayüzü ekle
Oynatmayı uygulamadan önce, kullanıcı arayüzüne bir düğme ekleyin. Böylece kullanıcı, ARCore'a oturumun ne zaman başlayacağını ve oynatmayı ne zaman durduracağını bildirebilir.
Proje panelinde app/res/layout/activity_main.xml
dosyasını açın.
activity_main.xml
ürününde, yeni Oynatma düğmesini oluşturmak ve etkinlik işleyicisini onClickPlayback()
adlı bir yönteme ayarlamak için kapanış etiketinin önüne 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 Düğmeleri Güncelle
Uygulamanın artık Playingback
adında yeni bir durumu var. Bunu işlemek için AppState
listesini ve appState
öğesini bağımsız değişken olarak alan tüm mevcut işlevleri güncelleyin.
HelloArActivity.java
içindeki AppState
sıralamasına Playingback
ekleyin:
public enum AppState {
Idle,
Recording,
Playingback // New enum value.
}
Oynatma sırasında Kaydet düğmesi ekranda hâlâ açıksa kullanıcı yanlışlıkla bu düğmeyi tıklayabilir. Bunu önlemek için Oynatma sırasında Kaydet düğmesini gizleyin. Bu şekilde, onClickRecord()
içindeki Playingback
durumunu işlemeniz gerekmez.
Uygulama Playingback
durumundayken HelloArActivity.java
işlevinde Kaydet düğmesini gizlemek için 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ı oturum kaydederken Oynatma düğmesini gizle ve "Durdur" olarak değiştirin. Kullanıcı bir oturumu aktif olarak oynattığında. Bu sayede, kendiliğinden tamamlanmasını beklemek zorunda kalmadan oynatmayı durdurabilirler.
HelloArActivity.java
içine updatePlaybackButton()
işlevi ekleyin:
// 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;
}
}
Sonunda, onClickRecord()
uygulamasını updatePlaybackButton()
araması yapacak şekilde güncelleyin. Aşağıdaki satırı HelloArActivity.java
öğesine ekleyin:
public void onClickRecord(View view) {
// ... omitted code ...
updatePlaybackButton(); // Add this line to the end of the function.
}
Oynat düğmesini kullanarak bir dosya seçin
Kullanıcı bu düğmeye dokunulduğunda Oynatma düğmesine dokunarak oynatılacak bir dosya seçebilir. Android'de, dosya seçimi başka bir Etkinlik'teki sistem dosya seçiciyle işlenir. Bunun için Depolama Erişim Çerçevesi (SAF) kullanılır. Kullanıcı bir dosya seçtiğinde, uygulama onActivityResult()
adlı bir geri çağırma alır. Asıl oynatmayı bu geri çağırma işlevinin içinde başlatırsınız.
HelloArActivity.java
içinde, dosya seçimi yapmak ve oynatmayı durdurmak için 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
ürününde, cihazdan dosya seçen bir selectFileToPlayback()
işlevi oluşturun. Android Dosya Sistemi'nden dosya seçmek için bir ACTION_OPEN_DOCUMENT
Amacı 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 sabit değerdir. Bunu, HelloArActivity.java
içinde HelloArActivity
içindeki herhangi bir yer tutucu değeri kullanarak tanımlayabilirsiniz:
private int REQUEST_MP4_SELECTOR = 1;
Dosya seçiciden geri çağırma işlemini gerçekleştirmek için HelloArActivity.java
içindeki 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);
}
Oynatmaya başlamak için uygulamayı etkinleştirin
Bir ARCore oturumunda, MP4 dosyasını çalmak 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;
}
Oynatmayı durdurmak için uygulamayı etkinleştirin
Aşağıdaki uygulama durumu değişikliklerini işlemek için HelloArActivity.java
içinde stopPlayingback()
adında bir işlev oluşturun:
- MP4 çalma, kullanıcı tarafından durduruldu
- MP4 çalma kendi kendine tamamlandı
Kullanıcı oynatmayı durdurduysa uygulama, ilk kez başlattığında olduğ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 işlemi, 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()
uygulamasında PlaybackStatus
özelliğini kontrol edin. FINISHED
ise 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 Çalıştır'ı tıklayın.
Uygulama başlatıldığında, sol tarafında kırmızı bir Kaydet ve sağ tarafında yeşil bir Oynatma düğmesi bulunan bir ekran görürsünüz.
Çal düğmesine dokunun ve az önce kaydettiğiniz MP4 dosyalarından birini seçin. arcore-
ile başlayan bir dosya adı görmüyorsanız cihazınız Filmler klasörünü göstermiyor olabilir. Bu durumda, Telefon modeli > Filmler klasörüne sol üst köşedeki menüyü kullanarak dokunun. Telefon modeli klasörünün görünmesi 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ı oynatır.
Bir oturumu oynatma ile sıradan bir videoyu oynatma arasındaki fark, kaydedilen oturumla etkileşimde bulunabilmenizdir. İşaretçileri ekrana yerleştirmek için algılanan bir uçağa dokunun.
Bu adımda yaptıklarınız
- Oynatmayı başlatmak ve durdurmak için düğme eklendi
- Uygulamanın kaydı başlatmasını ve durdurmasını sağlayan bir işlev uygulandı
- Cihazda daha önce kaydedilmiş bir ARCore oturumunun oynatılması
5. MP4'e ek veri kaydedin
ARCore 1.24 ile MP4 dosyasına ek bilgiler kaydetmek mümkündür. AR nesne yerleşimlerinin Pose
kadarını kaydedebilir ve ardından oynatma sırasında AR nesnelerini aynı konumda oluşturabilirsiniz.
Kaydedilecek yeni parçayı yapılandırın
HelloArActivity.java
ürününde UUID ve MIME etiketine sahip yeni bir kanal 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 ...
}
addTrack()
çağrısıyla RecordingConfig
nesnesini oluşturmak için mevcut kodu 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 kaydet
Kullanıcı algılanan bir uçağa her dokunduğunda, pozu ARCore tarafından güncellenecek bir Anchor
üzerine AR işaretçisi yerleştirilir.
ARCore oturumunu hâlâ kaydediyorsanız Anchor
etkinliğinin oluşturulduğu karede pozunuzu 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 kamera göreli Pose
ayarını sürdürmemizin nedeni, kayıt oturumunun dünya başlangıç noktası ile oynatma oturumunun dünya başlangıç noktasının aynı olmamasıdır. Bir kayıt oturumunun dünya genelinde kaynağı, oturum ilk kez devam ettirildiğinde, Session.resume()
ilk çağrıldığında başlar. Bir oynatma oturumunun dünya kaynağı, ilk kare kaydedildiğinde başlar, Session.resume()
ise Session.startRecording()
sonra ilk kez çağrılır.
Oynatma bağlantısı oluşturma
Anchor
hesabını yeniden oluşturmak oldukça kolaydır. HelloArActivity.java
işlevine 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()
yöntemini ç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 edin
Mobil cihazınızı geliştirme makinenize bağlayın ve Android Studio'da Çalıştır'ı tıklayın.
Öncelikle Kaydet düğmesine dokunarak oturum kaydedin. Kayıt sırasında, birkaç AR işaretçisi yerleştirmek için algılanan düzlemlere dokunun.
Kayıt durduktan sonra Oynatma düğmesine dokunun ve az önce kaydettiğiniz dosyayı seçin. Oynatma işlemi başlar. Önceki AR işaretçisi yerleşimlerinizin, tam uygulamaya dokunurken nasıl göründüğüne dikkat edin.
Bu codelab'de yapmanız gereken tüm kodlama işlemleri bu kadar.
6. Tebrikler
Tebrikler, bu codelab'in sonuna ulaştınız. Bu codelab'de yaptıklarınıza tekrar bakalım:
- ARCore Hello AR Java örneğini derleyip çalıştırın.
- AR oturumunu MP4 dosyasına kaydetmek için uygulamaya Kaydet düğmesi eklendi
- MP4 dosyasından AR oturumunu oynatmak için uygulamaya bir Oynat düğmesi eklendi
- Kullanıcı tarafından oluşturulan çapaları MP4'te oynatma için kaydetmek üzere yeni bir özellik eklendi