Kullanıcıların mobil cihazlarda geçirdiği zamanın büyük bir kısmını medya tüketimi oluşturur. Bu nedenle, oynatma deneyiminin kusursuz, son derece verimli ve pil ömrünü dikkate alan bir yapıda olması gerekir. Ayrıca kullanıcılar, bu deneyimlerin telefonlardan Living Room TV'lere ve diğer cihazlara sorunsuz bir şekilde geçiş yapmasını bekliyor.
Media3 ve ExoPlayer'daki mimari güncellemelerin, farklı form faktörlerinde sorunsuz oynatma deneyimi sunmak için nasıl kullanıldığını öğrenin. Media3 Compose kullanıcı arayüzü araç setiyle duyarlı arayüzler oluşturma, yeni PreloadManager'ı kullanarak feed'lerdeki arabelleğe alma sorununu ortadan kaldırma ve CastPlayer ile üç boyutlu ses sayesinde uygulamanızı oturma odası ekranlarına ve Android XR'a genişletme konuları ele alınacak.
Transkript
Anladım.
Herkese merhaba.
Adım Toni.
Android medya ekibinde mühendisim.
Kristina ve Donovan ile birlikte Jetpack Media3'ün Android'deki tüm medya ihtiyaçlarınızda size nasıl yardımcı olabileceğini konuşacağız.
Media3, Android'in medyayla ilgili her şey için destek kitaplığıdır. ExoPlayer ile oynatma, medya oturumu ve Cast ile medya paylaşma, Transformer ile medya düzenleme veya kare ya da meta veri ayıklama gibi medya inceleme işlemleri için kullanılabilir.
Media3, bu alanın tamamını tek bir kitaplık grubunda kapsar.
Destek kitaplığı olarak uygulamanıza dahil edilir.
Bu sayede tutarlılık sağlayabiliyoruz. Yani aynı kod tüm OS sürümlerinde ve tüm cihazlarda çalışıyor.
Ayrıca güncellenebilme durumuna da yardımcı olur.
Yaptığımız her hata düzeltmesi API 24'e kadar ulaşır ve herhangi bir OS sürüm döngüsüne bağlı değildir.
Destek kitaplığı olarak, Android çerçevesinde mümkün olmayan çok zengin özelleştirme seçeneklerine izin verebiliriz.
GitHub'da bir kitaplık olarak önceliğimiz açık kaynaklı olmaktır.
Yani yaptığımız her değişiklik GitHub'da anında kullanılabilir.
Etkin bir Issue Tracker'ımız var ve pull isteklerini kabul ediyoruz.
Burada vurgulanması gereken asıl nokta, tüm bu kullanım alanlarında, üst düzey kullanıcı arayüzü öğelerinden basit hazır API'lere kadar her düzeydeki ayrıntıya gerçekten önem vermemizdir. Ayrıca derin özelleştirme seçenekleri, çeşitli biçimler ve standartlar için geniş destek ve tüm bunların temelini oluşturan performans ve verimlilik de önemlidir.
Media3 bu alanın tamamını kapsar.
Bugün bu konulardan bazılarını daha ayrıntılı bir şekilde ele alacağız.
Yeni Compose kullanıcı arayüzü modülümüzden, medya oturumuyla paylaşımı daha da kolaylaştıran yeni CastPlayer'dan, yeni gelişmiş efektler içeren yenilenmiş düzenleme ardışık düzeninden, meta verileri ve kareleri almak için yeni araçlardan, özellikle Eclipsa video ve ses gibi bazı yeni akış teknolojilerinden ve önceden yükleme, ileri geri oynatma ve pil tasarrufu için bir dizi deneyim kalitesi yardımcı programından bahsedeceğiz.
Şimdi ilk konumuza geçelim: Media3, medya için kullanıcı arayüzü öğeleri oluşturmaya nasıl yardımcı olabilir?
Kısa süre önce iki yeni modül yayınladık: Media3 UI Compose ve Media3 UI Compose Material 3.
Burada, Media3 1.10'da başka değişiklik yapılmadan kullanıma sunulan üst düzey bir oynatıcı bileşenini görebilirsiniz.
@Composable
fun videoScreen() {
Player(viewModel.exoPlayer)
}Kullanıma hazırdır.
Yalnızca Media3 oynatıcı gerekir.
Buradaki örnekte, genellikle görünüm modelinizde bulunan bir ExoPlayer kullanılmıştır.
Ancak CastPlayer, medya kumandası veya uygulamanızdaki başka bir oynatıcı da olabilir.
Burada ExoPlayer'ın doğru bilgi kaynağı olduğunu belirtmekte fayda var.
Tüm bilgileri içerir ve oynatıcı composable'ı, kullanıcı arayüzünü oluşturup her şeyi birbirine bağlar.
Ancak bu kullanıcı arayüzünü özelleştirmek, yeni düğmeler, farklı konumlar, farklı stiller ve metin yer paylaşımları eklemek isterseniz ne yapmanız gerekir?
Oynatıcı composable'ı, yapı taşlarından oluşur.
Bunları değiştirebilirsiniz.
Her bir bloğu özelleştirebilir veya uygulamanızın başka bir yerinde bağımsız olarak kullanabilirsiniz.
Örneğin, üst, orta ve bottomControls kısımlarını ayrı ayrı özelleştirebilirsiniz.
Ayrıca, videolar oynatılmadığında gösterilen deklanşörün görünümünü veya hata yer paylaşımını da özelleştirebilirsiniz.
Ancak bu sadece ilk katman.
Varsayılan merkez ve bottomControls, özelleştirebileceğiniz daha ayrıntılı alanlara da sahiptir.
Örneğin, diğer her şeyi olduğu gibi bırakıp ortadaki düğmeyi değiştirebilir veya kaydırma çubuğunun üstüne ya da altına öğeler ekleyebilirsiniz.
Media3 Compose, bunu yapmak için ihtiyacınız olan tüm yapı taşlarını sağlar.
Örneğin, oynat/duraklat düğmesi, tekrar modu, karışık modu, mikrofonu kapatma düğmesi gibi çeşitli düğmelerimiz var.
Ardından, ekranda gösterilen kaydırma çubuğu ve konum metni gibi ilerleme göstergeleri vardır.
Ayrıca çeşitli kapsayıcılar ve yer paylaşımları da vardır.
Size gösterdiğim üst düzey oynatıcı composable'ı bir örnek, ancak hata yer paylaşımları veya altyazılar gibi daha birçok örnek var.
Ayrıca, buradaki mini kumanda gibi başka kullanışlı bileşenlerimiz de var.
Kullanıcı uygulamanızda gezinirken alt sayfaya yerleştirebilirsiniz.
Henüz bitmedi.
Bu kitaplığı aktif olarak genişletmeye devam ediyoruz. Örneğin, yakında kırpma kaydırıcısı gibi yeni düzenleme kontrolleri ekleyeceğiz.
Şimdi, bu yapı taşlarının nasıl kullanıldığını görmek için bu özelleştirmeyi uygulamalı olarak görelim.
Player(viewModel.exoPlayer)Şu anda kaydırma çubuğunun yanında bulunan konum ve süre metnini, çubuğun üstüne taşımak istediğimizi varsayalım.
Player(
player = viewModel.exoPlayer,
bottomControls = {
PlayerDefaults.BottomControls(
player,
left = {}, right = {},
above = {
PositionAndDurationText(
player, separator = " • ")
},
)
},
)Bu nedenle, bu bottomControls değerlerini geçersiz kılabiliriz.
Varsayılan bileşeni kullanmaya devam edebiliriz ancak sol ve sağ alanları boş olarak ayarlayarak görünmez hale getiririz.
Bunun yerine, pozisyon ve süre metnini yukarıdaki yuvaya yerleştirip özel bir ayırıcıyla daha ilgi çekici hale getiriyoruz.
Şimdi de centerControls yer paylaşımını tamamen kaldırıp yerine hız kontrolü ve mikrofonu kapatma düğmesi olan bir kaydırma çubuğunun hemen üstüne oynatma düğmesi yerleştirmek istediğimizi varsayalım.
Player(
player = viewModel.exoPlayer,
centerControls = {},
bottomControls = { ...
above = {
Row(Modifier.fillMaxWidth()) {
PositionAndDurationText(...)
Spacer(Modifier.weight(1f))
PlaybackSpeedToggleButton(player)
MuteButton(player)
PlayPauseButton(player)
}
},
)
}Bu da yine aynı fikir.
centerControls öğesini boş olarak ayarlayarak kaybolmasını sağlayabilir ve kaydırıcının üstündeki alanı biraz daha genişleterek göstermek istediğimiz üç düğmeyi ekleyebiliriz.
Bu düğmeler kitaplığın kendisinden geldiği için ek kod eklemeniz gerekmez.
Hepsi bu kadar.
Bu öğeleri de aynı şekilde biçimlendirebilirsiniz.
Örneğin, mikrofonu kapatma düğmesinin simgesini değiştirmek ancak işlevini korumak istiyorsanız aynı şekilde çalışır.
Player(
...
bottomControls = { ...
above = {
...
MuteButton(player,
painter = { painterResource(
if (showMuted)
R.drawable.muted
else
R.drawable.unmuted )})
}
},
)Burada gördüğünüz gibi, düğme için özel bir boyayıcı tanımlayabilir ve simgeleri kendiniz seçebilirsiniz.
Bileşen size bazı meta veriler sağlar. Bu nedenle, doğru simgeyi seçmek için bu örnekteki showMuted gibi duruma bakmanız yeterlidir.
Media3 Compose'un asıl avantajı da budur.
Siz uygulamanızın görünümüne ve tarzına odaklanabilirsiniz, geri kalan her şeyi Media3 halleder.
Oynatıcıdan durumu okur, oynatıcıyla etkileşime girer ve aradaki tüm geçişleri ve animasyonları yönetir.
Şimdi kullanıcı arayüzünden medya paylaşımına geçelim.
Oynatma durumunuzu aynı cihazdaki veya uzak cihazlara bağlı diğer uygulamalarla paylaşabilirsiniz.
Devam eden oynatmanızı yayınlamanın temel yolu MediaSession kullanmaktır.
Oynatma durumunuzu ve meta verilerinizi diğer kullanıcıların erişimine açarak oynatmanızı kontrol etmelerine olanak tanır.
Gemini, sistem arayüzü medya bildirimi, Cast, Wear OS veya Android Auto tamamlayıcı uygulamaları, uygulamanızla bu şekilde iletişim kurar.
MediaSession, bir Media3 oynatıcıyı sarmalar ve oynatıcınız olduğunda bunu oluşturmak çok kolaydır.
MediaSession.Builder(context, player).build()Yine, Compose kullanıcı arayüzünde olduğu gibi bu, ExoPlayer veya uygulamanızdaki başka bir oynatıcı olabilir.
Arka planda oynatma yapıyorsanız bunu genellikle bir MediaSessionService veya MediaLibraryService içinde barındırırsınız. Bu hizmet, tüm yaşam döngüsünü ve bildirim gibi şeyleri sizin için otomatik olarak yönetir.
class PlaybackService : MediaSessionService() {
private lateinit var mediaSession:
MediaSession
override fun onGetSession(
info: ControllerInfo) : MediaSession?
= mediaSession
}Ancak bir medya uygulamanız varsa muhtemelen tüm bunları zaten biliyorsunuzdur.
Size göstermek istediğim heyecan verici yeni özellik, yeniden yazılan CastPlayer'ımız.
Google Cast, yerel oynatmanızı Android TV'ler, Chromecast veya akıllı hoparlörler gibi uzak bir cihaza aktarmanıza olanak tanır.
Daha önce ayrı bir Cast SDK'sı yayınlıyorduk. Bu SDK artık bakım modunda.
Bunun yerine, Media3 Cast çok daha basit ve daha akıcı bir entegrasyon sağladığı için Cast SDK'nın yerini tamamen alır.
Buradaki en önemli öğe, ExoPlayer gibi aynı standart oynatıcı arayüzünü uygulayan CastPlayer'dır.
ExoPlayer yerel oynatma için kullanılırken CastPlayer, ExoPlayer aracılığıyla yerel olarak veya Cast protokolü aracılığıyla uzaktan oynatabilen bir yetkilendirme örneğidir.
Her şeyi halleder.
Çıkış cihazı değişikliklerini dinler, bağlantıyı kurar, durumu senkronize eder ve gerektiğinde oynatmayı sizin için aktarır.
Bu nedenle, CastPlayer'ı uygulamanıza entegre etmek son derece kolaydır.
val player = ExoPlayer.Builder(context).build()
mediaSession = MediaSession.Builder(context,player)
.build()Bir ExoPlayer oluşturup bunu MediaSession ile paylaşmak yerine CastPlayer kullanmanız yeterlidir.
Cast.getSingletonInstance(context).initialize()
val player = CastPlayer.Builder(context).build()
mediaSession = MediaSession.Builder(context,player)
.build()Yapmanız gereken tek şey, Cast sistemini uygulamanızda kullanılmadan önce başlatmaktır.
Bu CastPlayer'ın, yerel oynatma için dahili olarak bir ExoPlayer oluşturduğunu unutmayın. Bu nedenle, yerel oynatıcıyı daha fazla özelleştirmek için kendi ExoPlayer'ınızı oluşturup bu CastPlayer'a yerel oynatıcı olarak iletebilirsiniz.
Cast.getSingletonInstance(context).initialize()
val exoPlayer = ExoPlayer.Builder(context).build()
val player = CastPlayer.Builder(context)
.setLocalPlayer(exoPlayer).build()
mediaSession = MediaSession.Builder(context,player)
.build()Bu da yine aynı Media3 ilkesidir.
Kullanıma hazır basit API yapı taşlarının yanı sıra daha fazla özelleştirme için ihtiyacınız olan tüm özelleştirme seçeneklerini sunuyoruz.
Bu düzende kullanıcının yerel ve uzaktan oynatma arasında nasıl geçiş yaptığını merak ediyor olabilirsiniz.
Bu özellik, medya bildirimindeki sistem çıkış değiştirici iletişim kutusuyla kullanıma hazırdır.
Köşedeki küçük düğme.
Ancak oynatma ekranınızda bir yayınlama düğmesi göstererek daha kolay bulunmasını sağlamak isterseniz ne yapmanız gerekir?
Birkaç dakika önce oluşturduğumuz bu özelleştirilmiş Compose kullanıcı arayüzünü hatırlıyor musunuz?
Player(
exoPlayer,
centerControls = {}
bottomControls = { /* customized */ },
)Media3 Cast, buraya yerleştirebileceğiniz hazır bir düğme de sunar.
Örneğin, hazır MediaRouteButton öğesini topControls'e eklemek kadar basit bir işlem. Kurulum için başka parametreler gerekmiyor.
Player(
castPlayer,
centerControls = {}
bottomControls = { /* customized */ },
topControls = {
PlayerDefaults.TopControls(castPlayer) {
MediaRouteButton(
Modifier.align(Alignment.TopEnd))
}
},
)Bu düğmeyi tıkladığınızda aynı sistem çıkış değiştirici iletişim kutusu açılır ve CastPlayer, oynatmayı sizin için aktarır.
Ayrıca, uzaktan gerçekleşen her şeyin yerel kullanıcı arayüzünüze yansıtıldığından emin olmak için CastPlayer'ı buradaki kullanıcı arayüzüne bağladığımı da fark etmişsinizdir.
Şimdi sözü, düzenleme kullanım alanlarından bahsedecek olan Kristina'ya bırakıyorum.
Teşekkür ederiz.
Merhaba, şimdi düzenleme ve gelişmiş efektlere geçelim.
Media3 API'leri birden fazla bileşenden oluşur.
İlk olarak Transformer, düzenleme API'lerimiz için ana giriş noktasıdır.
Transformer, medyayı işler, kodunu dönüştürür ve bir dosyaya aktarır.
Sırada, medya kompozisyonlarınızın gerçek zamanlı önizlemelerini oluşturan CompositionPlayer var.
Bu sayede, son dışa aktarma işleminizden önce düzenlemelerinizi önizleyebilir ve hassas ayarlamalar yapabilirsiniz.
Sırada efektler var.
Basit görsel efektler uygularken veya grafik yer paylaşımları eklerken kare kare işleme üzerinde tam kontrol sahibi olmanızı sağlar.
Son olarak Muxer'ı ele alalım.
İşlem hattımızdaki son çıkış aşaması olarak görev yapar. İşlenen ses, video ve meta veri parçalarını alıp standartlaştırılmış MP4 kapsayıcılarına yazar.
Geçtiğimiz yıl boyunca video düzenleme sürecimizi baştan sona yeniden tasarlayıp oluşturduk. Bugün, bu süreçte yaptığımız önemli iyileştirmeleri paylaşmaktan heyecan duyuyorum.
Ardışık düzeni neden yeniden oluşturduk?
Eski mimari, çeşitli zorluklara neden oluyordu.
İlk olarak, meta verileri tüm işleme adımlarına yayamıyordu.
OpenGL ile sıkı bir şekilde bağlı olması, modern grafik araçları için esnekliği sınırlıyordu.
Karmaşık API'si nedeniyle özelleştirme ve bakım, geliştiriciler için büyük bir engel oluşturuyordu.
Bu zorlukların üstesinden gelmek için yepyeni bir işleme ardışık düzeni oluşturduk.
İlk olarak, karmaşık çoklu öğe kompozisyonları oluşturmak artık çok daha kolay.
İkinci olarak, OpenGL'den ayrılarak WebGPU gibi modern grafik çerçevelerini kullanma konusunda tam özgürlüğe sahip olursunuz.
Son olarak, Media3 artık kod çözücüyü ve kodlayıcıyı doğrudan yöneterek tüm cihazlarda daha fazla tutarlılık ve verimlilik sağlar.
Şimdi bu ardışık düzenin işleyiş şeklini ayrıntılarıyla inceleyelim.
Kod çözücüler, kareleri doğrudan Media3 tarafından yönetilen donanım arabelleklerine gönderir.
Kare Toplayıcı, birden fazla sekans genelinde kareleri koordine ederek özel kare işleyicinize bu arabellekleri okumak ve kodlayıcıya göndermek üzere işlemek için doğrudan erişim sağlar.
FrameProcessor arayüzü, uygulama düzeyinde işleme için birincil uzantı noktası olarak kullanılır.
public interface FrameProcessor
extends AutoCloseable {
interface Listener {
void onWakeup();
void onError(
VideoFrameProcessingException exception);
void onFrameProcessed(
Frame frame,
@Nullable SyncFenceWrapper onCompleteFence);
}
interface Factory {
FrameProcessor create(
FrameWriter output,
Executor listenerExecutor,
Listener listener);
}
boolean queue(List<AsyncFrame> frames);
void signalEndOfStream();
}Uygulamanız, dinleyici arayüzü aracılığıyla bir kare işlendiğinde Media3'ü bilgilendirir ve senkronizasyon sınırlarını iletir. Böylece Media3, arabelleklerin ne zaman güvenli bir şekilde yeniden kullanılabileceğini tam olarak bilir.
Birleşik bellek hakkında bir not: Media3'ün donanım arabelleklerinin sahipliğini alması sayesinde uygulamanız, verileri doğrudan yerinde okuyabilir ve değiştirebilir.
Bu sıfır kopyalı yaklaşım, bellek ek yükünü azaltır ve işlemeyi son derece hızlı tutar.
Yeni düzenleme API'lerinden yararlanan geliştiricilerden biri de Adobe.
Adobe Premiere ekibi, Android ekosisteminde daha fazla uyumluluk sağlamak için medya düzenleme altyapılarının temeli olarak Media3 düzenleme API'lerini kullandı. Aynı zamanda kendi özel ardışık düzenlerini ve WebGPU'yu kullanmak için ihtiyaç duydukları esnekliği de korudu.
Media3 1.12 sürümü kapsamında yeni efektler yayınlayacağımızı duyurmaktan mutluluk duyuyoruz.
Video iyileştirmelerle başlayın.
Arka planda mimarimiz modüler kuruluma dayanır.
Uygulamanız, Google Play Hizmetleri Geliştirme modülü ile arayüz oluşturan Media3 efektli Google Play Hizmetleri ile entegre olur.
Video Geliştirme modülü; ton eşleme, gürültü giderme, bulanıklık giderme ve süper çözünürlük özelliklerini içerir.
Video Enhance'i ardışık düzeninize entegre etmek için yalnızca birkaç satır kod gerekir.
val frameProcessorFactory = EnhancementSessionDecorator.Builder(/* context = */ this, baseGlFactory)
.setTonemappingEnabled(true)
.setDeblurAndDenoiseEnabled(true)
.setUpscaleVideoEnabled(true)
.build()
val transformerBuilder = Transformer.Builder(/* context = */ this)
.setFrameProcessorFactory(frameProcessorFactory)EnhancementSessionDecorator.Builder kullanarak ton eşleme, bulanıklık giderme veya ölçek büyütme gibi özellikleri seçerek etkinleştirebilir ve elde edilen frameProcessorFactory ile donanım arabelleği yardımcılarını Transformer.Builder'ye iletebilirsiniz.
Media3 1.12'de sunulacak bir diğer heyecan verici efekt de Studio Sound.
Stüdyo Ses Kalitesi, videonuzun ses parçasını otomatik olarak analiz eder ve konuşma, ortam gürültüsü ve müzik gibi ayrı parçalara ayırır.
İçerik üreticiler, rüzgar veya kalabalık gibi dikkat dağıtıcı unsurları kolayca azaltırken net diyalogları ve müzikleri ön plana çıkarabilir.
Stüdyo Ses'i entegre etmek için Stüdyo Ses ses işlemcisini bir efekt nesnesine aktarın.
val editedMediaItem =
EditedMediaItem.Builder(MediaItem.fromUri(media.uri))
.setEffects(
Effects(
listOf(
soundSeparationAudioProcessor
),
emptyList(), // video effects
)
)
.build()Bu işlem, ses işlemcisini medya öğenize ekler.
Studio Ses ses işlemcisini oluştururken, analiz edilen ses kanalı eşlemesini onResult aracılığıyla almak için dinleyici geri çağırma işlevini ekleyin.
val soundSeparationAudioProcessor =
SoundSeparationAudioProcessor(
getApplication(),
mainExecutor,
object : SoundSeparationAudioProcessor.Listener {
override fun onResult(channelToAudioType: SparseIntArray) {
for (i in 0 until channelToAudioType.size()) {
val channelIndex = channelToAudioType.keyAt(i)
val audioType = channelToAudioType.valueAt(i)
val label = toAudioTypeName(audioType)
detectedChannelVolumes.add(ChannelVolume(channelIndex, audioType, label))
}
}
},
)İşlemin sonucu kullanılabilir olduğunda kanal dizinini, tanımlanan ses türünü ve okunabilir etiketi ayıklamak için her kanalı iterasyona tabi tutun.
Şimdi Stüdyo Ses Kalitesi'ni uygulamalı olarak görelim.
İlk etki. Bebeğim, lütfen ağlama.
Akşam yemeğiniz için kesim yapıyoruz.
Arka planda bebeğin ağladığını duyabiliyorsunuz. Şimdi efekt uygulandı.
Evet, duyabileceğiniz gibi, Stüdyo Ses efekti sayesinde arka planda ağlayan bebek sesi yoktu.
Şimdi meta veri alma konusuna geçelim.
Media3'ün, container meta verilerini okumanızı ve kareleri ayıklamanızı nasıl kolaylaştırdığını inceleyeceğiz.
MetadataRetriever ile başlayarak, temiz ve engelleyici olmayan kod için Kotlin eş yordamlarıyla sorunsuz bir şekilde entegre olur.
Medya öğesi başına bir adet sabit alıcı kullanan daha temiz ve hataya daha az açık bir tasarıma sahiptir.
Tüm API sürümlerinde ve cihazlarda tutarlı bir şekilde çalışır.
Ayrıca, çok çeşitli medya biçimlerini de destekler.
MetadataRetriever kullanan pratik bir örneği aşağıda bulabilirsiniz.
MetadataRetriever.Builder(context, mediaItem).build().use { retriever ->
// 1. Duration (in microseconds)
val durationUs = retriever.retrieveDurationUs().await()
// 2. Track formats & container metadata (reuses the same preparation pass)
val trackGroups = retriever.retrieveTrackGroups().await()
for (i in 0 until trackGroups.length) {
val format = trackGroups[i].getFormat(0)
// Video/Audio properties: format.width, format.height, format.colorInfo …
// Rich metadata: SlowMotionData, MotionPhotoMetadata, Mp4TimestampData …
val metadata = format.metadata
}
// 3. Stream structure & manifest info
val timeline = retriever.retrieveTimeline().await()
}Kotlin eş yordamlarını kullanarak süreyi eşzamansız olarak alabilir, parça biçimlerini ve meta verileri tek bir temiz blokta inceleyebilirsiniz.
Şimdi FrameExtractor hakkında konuşalım.
Bu sayede, kodu çözülmüş video karelerini medya öğesinden hızlı ve verimli bir şekilde alabilirsiniz.
Kod çözücüler gibi temel kaynakları otomatik olarak yönetir, görsel efektler veya HDR ton eşleme uygulamanıza olanak tanır ve ihtiyacınız olan kareyi tam olarak elde edebilmeniz için ayrıntılı arama kontrolü sağlar.
Kare çıkarmanın kodda nasıl göründüğünü aşağıda bulabilirsiniz.
FrameExtractor.Builder(context, mediaItem)
.setSeekParameters(SeekParameters.CLOSEST_SYNC) // Fast keyframe seeking
.setEffects(listOf(Presentation.createForHeight(360))) // GPU downscaling
.setExtractHdrFrames(true) // Optional: HLG HDR or UltraHDR
.build()
.use { extractor ->
// Representative thumbnail
val thumbnail = extractor.getThumbnail().await()
// Frame at specific timestamp (bitmap + actual presentationTimeMs)
val frame = extractor.getFrame(positionMs = 5_000L).await()
}Arama parametreleri, GPU küçültme, efektler ve HDR desteği gibi seçeneklerle bir örnek oluşturursunuz. Ardından, temiz askıya alma çağrılarını kullanarak küçük resimleri ve belirli kareleri eşzamansız olarak beklersiniz.
Şimdi de Donovan'a geçelim.
Teşekkürler, Kristina.
Şimdi de Media3'e eklediğimiz temel akış teknolojisi desteklerinden bazılarına değinelim. Bunlar arasında Eclipsa Video ve Eclipsa Audio gibi biçimler için destek de yer alıyor.
Bu nedenle Media3'teki temel çalışmalardan biri, akış standartlarının önüne geçmektir. Böylece yeni bir spesifikasyon her geldiğinde medya ardışık düzeninizi yeniden yazmanız gerekmez.
Yakın zamanda kullanıma sunduğumuz özelliklerden bazılarını aşağıda bulabilirsiniz.
İçerik yönlendirme özelliğimiz, arka uç CDN yollarının yayın sırasında dinamik olarak değiştirilmesine olanak tanır.
Sorunsuz kişiselleştirilmiş reklam birleştirme için sunucu destekli reklam ekleme özelliğini destekliyoruz.
Ayrıca, tam AV1 HDR desteği de dahil olmak üzere birden fazla giriş noktasında AV1 desteği sunmaya başladık.
Yüksek kaliteli çekim ve düzenleme için Android profesyonel video desteği sunuyoruz.
Ayrıca hem JPEG hem de HEIC biçimindeki hareketli fotoğraflar için destek ekliyoruz.
Ayrıca Eclipsa Video ve Eclipsa Audio için de destek ekliyoruz. Bu konuya birazdan daha ayrıntılı değineceğim.
Cihaz desteği sunulmaya başladığında tüm bu özellikler otomatik olarak desteklenecek.
Şimdi HDR'nin bir sonraki evrimi olan Eclipsa Video'ya ve çözmeye çalıştığı temel soruna göz atalım.
Önceki HDR biçimleri, açıklayıcı meta veriler sağladığından sahnenin nasıl görünmesi gerektiğini belirlerdi ancak ekrana sahneyi nasıl oluşturacağını tam olarak söylemezdi.
Önce bunu temellendirelim.
İçeriğinizi net ve rahat bir şekilde görebilmeniz için farklı ortamlarda ortalama ekran parlaklığınızla ilgili farklı öneriler vardır.
Loş bir odada bu aralık 100 ile 300 nit arasında olacaktır.
Daha aydınlık ortamlara geçtiğinizde ise ekranın buna ayak uydurması ve parlaklığını artırması gerekir. Böylece içeriğinizi net bir şekilde görebilirsiniz. Telefonu doğrudan güneş ışığı altında kullanırken içeriğinizi görebilmeniz için ekranın 1.000, 2.000, hatta daha fazla nit parlaklığa ulaşması gerekebilir.
Geleneksel SDR, 100 ila 300 aralığında çalışır.
HDR'nin amacı ekranı genel olarak parlaklaştırmak değildir.
Dinamik aralığı genişleterek ateş ve güneş ışığı gibi öğeler için yerelleştirilmiş vurgular elde etmek istiyorsunuz.
Ancak akıllı telefonlar, bu süreci biraz karmaşıklaştırıyor. Çünkü telefonlarımızı her yere götürüyoruz. Evimizdeki karanlık bir odaya da, güneşli bir plaja da.
Ayrıca birçok uygulama, SDR kullanıcı arayüzü öğelerini SDR ve HDR içeriklerle karma feed'lerde birleştiriyor. Bu da özellikle gece geç saatlerde ekranı kaydırırken rahatsız edici ve göz kamaştırıcı bir etkiye neden olabiliyor.
HDR'nin neden tercih edildiğini anlayabiliyoruz, değil mi?
Bu sayede içeriklerimiz daha fazla ortamda harika görünür.
HDR etkinleştirildiğinde kullanıcı etkileşiminin arttığını bile gördük.
Ancak mevcut HDR formatları bu ortamları hesaba katmaz.
Eclipsa Video'nun en büyük avantajı da bu.
Pause for laughter.
Teşekkür ederiz.
Teşekkür ederiz.
Bu nedenle, Eclipsa Video açıklayıcı meta veriler yerine kuralcı meta veriler sağlar.
Bu da üç ana temele dayanıyor.
İlk sütun, referans beyaz sabitleyicidir.
Bu sayede standart parlaklık, ekranın ortama göre ayarlanmış SDR beyaz noktasına kilitlenir ve ani parlaklık artışları önlenir.
İkinci temel unsur, içerik üretici SDR referansıdır.
Bu sayede, içerik üreticinin ton eşleme eğrisi SDR'ye tam olarak korunur.
Üçüncü olarak, tavan uyarlamalı kazanç eğrilerimiz var. Bunlar, içeriğin görüntülendiği ortama uyması için vurguları ekranın gerçek zamanlı HDR tavanına uyacak şekilde deterministik olarak ölçeklendirir.
Bu özellik, Android 17 ve sonraki sürümleri çalıştıran cihazlarda Media3 1.11'deki ExoPlayer'da kullanıma hazır olarak sunulur.
Eclipsa Video, sektörler arası uyumluluk için açık standartlar üzerine inşa edilmiştir.
HDR'yi desteklemenin tam zamanı.
Ses tarafında ise açık kaynaklı, telif hakkı ücreti gerektirmeyen, 3D üç boyutlu ses formatı olan Eclipsa Audio var.
Alliance for Open Media'nın IAMF spesifikasyonunu temel alır.
Sahne tabanlı Ambisonics'i kanal tabanlı sesle birleştirerek birden fazla karma sunu içeren tek bir akış oluşturur.
Bu sayede, diyalog geliştirme gibi nesne benzeri işlemler yapılabilir. Bu özellik, stereo kulaklıklardan surround ses sistemlerine kadar her cihazda çalışır.
Media3'te iki oynatma yolu vardır.
Android 17 ve sonraki sürümlerde ExoPlayer ve Android Audio Framework, Eclipsa Audio'yu otomatik olarak oluşturur.
Android 16 ve önceki sürümlerde ise başka bir seçeneğiniz var.
IAMF uzantısını isteğe bağlı bir Media3 eklenti kitaplığı aracılığıyla etkinleştirebilirsiniz.
İçerik üretme tarafında ise Media3 muxer'ları kullanarak IAMF ses parçaları yazabilirsiniz.
Ancak yüksek kaliteli formatları desteklemek, hikayenin yalnızca yarısıdır.
Bu kaliteyi verimli bir şekilde sunmamız gerekiyor.
Şimdi Media3'ün, ardışık düzenlerimizi nasıl daha hızlı ve verimli hale getirdiğinden bahsetmek istiyorum.
Bugün bahsettiğimiz birçok şey gibi, bu performans iyileştirmelerinin çoğu da Media3 sürümünüzü güncel tutarak kullanıma hazır hale gelir.
Özelliklerimizden biri olan PCM ses aktarımı, ses çalma işlemini düşük güçte çalışan bir işlem birimine yönlendirebilir.
İlk analizimizde, ekran kapalıyken ses oynatma için %20'ye varan güç tasarrufu gördük.
PCM ses boşaltmaya ek olarak, ExoPlayer'ın dahili oluşturma döngüsü uyandırmalarını VSYNC ve kod çözücü arabelleği kullanılabilirliğiyle uyumlu hale getiren dinamik planlama özelliğimiz de var.
Ayrıca, oluşturucu ön ısıtma özelliğimiz de vardır. Bu özellik, ikincil bir video oluşturucuyu önceden ısıtmanıza olanak tanır. Bu sayede, anında oynatma listesi geçişleri ve daha kolay reklam birleştirme yapılabilir.
Ayrıca, takılan oynatıcı algılama ve önden belleğe alma özelliğimiz de var. Bu özellik, takılan arabellekleri ve oluşturucuları otomatik olarak algılar ve önden belleğe alınmış belleği temiz bir şekilde sınırlar.
Kullanabileceğiniz bir diğer mod da ileri geri oynatma modudur.
Bu özellik, kullanıcılarınızın içerikleri sık sık ileri geri oynattığı uygulamalar için tasarlanmıştır.
İleri geri oynatma modunu etkinleştirdiğinizde, arama çubuğunu sürüklemek çok daha hızlı ve sorunsuz hale gelir.
Bu arada, bu özellik ileri geri oynatma modu kapalıyken kullanılabilir.
Sağda ise ileri geri oynatma modunun açık olduğunu görüyoruz.
Teşekkür ederiz.
Teşekkür ederiz.
İşte böyle.
Şimdi Android System Health'ten bahsedelim.
Paylaşılan RAM, CPU ve pil kaynaklarımıza saygılı olmamız gerekiyor. Android, ses güçlendirme ve ön plan hizmetleriyle ilgili kurallarını sıkılaştırdıkça kendi ham hizmetlerini ve bit eşlemlerini yöneten uygulamalar genellikle bazı hatalarla karşılaşabiliyor.
Ön plan hizmeti başlatmaya izin verilmeyen istisnalar veya bağlayıcı bellek yetersiz istisnaları gibi durumlarla karşılaşmış olabilirsiniz.
Bu nedenle, medya hizmeti oturumunu ve medya bildirim yöneticisini, ayrıca bit eşlem ardışık düzenlerimizi çeşitli şekillerde güçlendirdik. Örneğin, eski ön plan hizmet intent'lerini engelledik ve resim belleğini sınırladık.
Ancak Media3 kullanıyorsanız ve standart yönergeleri uyguluyorsanız tüm bu sistem sağlığı kısıtlamaları sizin için sorunsuz bir şekilde yönetilir.
Son olarak, Media3'e yakında eklenecek bir özellikten bahsetmek istiyorum.
Videoları kodlama veya dışa aktarma zamanı geldiğinde donanım çeşitliliği en büyük zorluklardan biri olabilir.
Birçok uygulama, herkese uygun bir codec yapılandırması kullanır. Bu da kalite ve performansın ideal düzeyde olmamasına yol açar.
CodecDB'yi bu nedenle geliştirdik.
CodecDB, Android cihazlarda standartlaştırılmış kalite metrikleri ve donanım karşılaştırması kullanılarak yapılan kapsamlı gerçek dünya video ölçümlerine dayanır.
Şimdi bunu uygulamalı olarak görelim.
Solda, birçok cihazda kullanabileceğimiz çok genel bir video yapılandırması kullanarak telefonla çektiğimiz bir videonun karesi var.
Sağda ise CodecDB'den aldığımız cihaz düzeyinde optimum yapılandırmayı kullandık.
Peki, farklar nerede?
Yüksek ayrıntılı alanlara bakarsanız en büyük kalite farkını görürsünüz.
Ağaçların ve çalıların dallarındaki netliğe veya binanın kenarının keskinliğine bakın.
CodecDB'den yararlanarak belirli bir cihazın özelliklerini kullanan çok daha iyi bir izleme deneyimi elde ediyoruz.
CodecDB'yi kodda kullanmak da oldukça basittir.
val config = CodecDb.onDevice(context)
.forEncodingFallingBackTo {
constraints -> createFallbackConfig(constraints)
}
.findOptimalSettings()
.forEditing()
.withResolution(1920, 1080, /* frameRate= */ 30.0)
.withAnyCodec()
.targetingQuality(VideoEncoder.QUALITY_LEVEL_VERY_GOOD)
.get()Gerekli her parametre için size yol gösteren akıcı bir aşama oluşturucu kalıbı kullanırsınız.
Bu nedenle kullanım alanınızı belirtmeniz gerekir.
Burada düzenleme için kullanıyoruz.
Bu, CodecDB'ye Media3 dışa aktarma veya kod dönüştürme ardışık düzenini kullandığınızı bildirir.
Örneğin, kamerayla çekim yapıyorsanız bu kullanım alanını ayarlayabilirsiniz.
Ayrıca hedef çözünürlüğünüzü ve kare hızınızı da iletirsiniz.
Ardından, 1080p videonun saniyede 8 megabit mi yoksa 15 megabit mi gerektirdiğini tahmin etmek yerine CodecDB'ye kullanmak istediğiniz codec'i söyleyip algısal kalite hedefi istersiniz.
Bu örnekte, kalite seviyesini "çok iyi" olarak ayarlıyoruz.
CodecDB, arka planda söz konusu SOC için deneysel karşılaştırma eğrilerini arar ve ideal codec ile kodlayıcı parametrelerini döndürür.
CodecDB'nin çok yakında Media3'te kullanıma sunulmasını bekliyoruz. Kullanıma sunulduğunda yerel Media3 türlerini kullanacak.
Bu nedenle, mevcut Media3 kodunuzla entegre etmek çok kolaydır.
Anladım.
Bugün Media3'te çok sayıda muhteşem işlev gördük. Şimdi bunları özetleyelim.
Bugün yeni Compose kullanıcı arayüzü modüllerimiz, yeni CastPlayer, yenilenen düzenleme ardışık düzeni ve Media3'te kullanabileceğiniz birçok yeni efekt hakkında bilgi edindiniz.
Meta verileri ve kareleri almak için yeni araçlar da gördük.
Ayrıca yeni akış teknolojisi desteği, performans iyileştirme ve yardımcı programları inceledik.
Media3 açık kaynaklı bir kitaplıktır. Bu nedenle, tüm bu özellikleri nasıl uyguladığımızı GitHub depomuzda görebilirsiniz.
Bugün itibarıyla Media3'ün en son kararlı sürümü 1.11.1'dir.
Şu anda alfa aşamasında olan 1.12.0 sürümüne de göz atabilirsiniz.
Media3 ile ilgili geliştirici belgelerimize ulaşmak için bu kısa bağlantıyı takip edin.
Çok teşekkür ederiz. ADS'nin geri kalanında iyi eğlenceler dileriz.
@Composable
fun videoScreen() {
Player(viewModel.exoPlayer)
}Player(viewModel.exoPlayer)Player(
player = viewModel.exoPlayer,
bottomControls = {
PlayerDefaults.BottomControls(
player,
left = {}, right = {},
above = {
PositionAndDurationText(
player, separator = " • ")
},
)
},
)Player(
player = viewModel.exoPlayer,
centerControls = {},
bottomControls = { ...
above = {
Row(Modifier.fillMaxWidth()) {
PositionAndDurationText(...)
Spacer(Modifier.weight(1f))
PlaybackSpeedToggleButton(player)
MuteButton(player)
PlayPauseButton(player)
}
},
)
}Player(
...
bottomControls = { ...
above = {
...
MuteButton(player,
painter = { painterResource(
if (showMuted)
R.drawable.muted
else
R.drawable.unmuted )})
}
},
)MediaSession.Builder(context, player).build()class PlaybackService : MediaSessionService() {
private lateinit var mediaSession:
MediaSession
override fun onGetSession(
info: ControllerInfo) : MediaSession?
= mediaSession
}val player = ExoPlayer.Builder(context).build()
mediaSession = MediaSession.Builder(context,player)
.build()Cast.getSingletonInstance(context).initialize()
val player = CastPlayer.Builder(context).build()
mediaSession = MediaSession.Builder(context,player)
.build()Cast.getSingletonInstance(context).initialize()
val exoPlayer = ExoPlayer.Builder(context).build()
val player = CastPlayer.Builder(context)
.setLocalPlayer(exoPlayer).build()
mediaSession = MediaSession.Builder(context,player)
.build()Player(
exoPlayer,
centerControls = {}
bottomControls = { /* customized */ },
)Player(
castPlayer,
centerControls = {}
bottomControls = { /* customized */ },
topControls = {
PlayerDefaults.TopControls(castPlayer) {
MediaRouteButton(
Modifier.align(Alignment.TopEnd))
}
},
)public interface FrameProcessor
extends AutoCloseable {
interface Listener {
void onWakeup();
void onError(
VideoFrameProcessingException exception);
void onFrameProcessed(
Frame frame,
@Nullable SyncFenceWrapper onCompleteFence);
}
interface Factory {
FrameProcessor create(
FrameWriter output,
Executor listenerExecutor,
Listener listener);
}
boolean queue(List<AsyncFrame> frames);
void signalEndOfStream();
}val frameProcessorFactory = EnhancementSessionDecorator.Builder(/* context = */ this, baseGlFactory)
.setTonemappingEnabled(true)
.setDeblurAndDenoiseEnabled(true)
.setUpscaleVideoEnabled(true)
.build()
val transformerBuilder = Transformer.Builder(/* context = */ this)
.setFrameProcessorFactory(frameProcessorFactory)val editedMediaItem =
EditedMediaItem.Builder(MediaItem.fromUri(media.uri))
.setEffects(
Effects(
listOf(
soundSeparationAudioProcessor
),
emptyList(), // video effects
)
)
.build()val soundSeparationAudioProcessor =
SoundSeparationAudioProcessor(
getApplication(),
mainExecutor,
object : SoundSeparationAudioProcessor.Listener {
override fun onResult(channelToAudioType: SparseIntArray) {
for (i in 0 until channelToAudioType.size()) {
val channelIndex = channelToAudioType.keyAt(i)
val audioType = channelToAudioType.valueAt(i)
val label = toAudioTypeName(audioType)
detectedChannelVolumes.add(ChannelVolume(channelIndex, audioType, label))
}
}
},
)MetadataRetriever.Builder(context, mediaItem).build().use { retriever ->
// 1. Duration (in microseconds)
val durationUs = retriever.retrieveDurationUs().await()
// 2. Track formats & container metadata (reuses the same preparation pass)
val trackGroups = retriever.retrieveTrackGroups().await()
for (i in 0 until trackGroups.length) {
val format = trackGroups[i].getFormat(0)
// Video/Audio properties: format.width, format.height, format.colorInfo …
// Rich metadata: SlowMotionData, MotionPhotoMetadata, Mp4TimestampData …
val metadata = format.metadata
}
// 3. Stream structure & manifest info
val timeline = retriever.retrieveTimeline().await()
}FrameExtractor.Builder(context, mediaItem)
.setSeekParameters(SeekParameters.CLOSEST_SYNC) // Fast keyframe seeking
.setEffects(listOf(Presentation.createForHeight(360))) // GPU downscaling
.setExtractHdrFrames(true) // Optional: HLG HDR or UltraHDR
.build()
.use { extractor ->
// Representative thumbnail
val thumbnail = extractor.getThumbnail().await()
// Frame at specific timestamp (bitmap + actual presentationTimeMs)
val frame = extractor.getFrame(positionMs = 5_000L).await()
}val config = CodecDb.onDevice(context)
.forEncodingFallingBackTo {
constraints -> createFallbackConfig(constraints)
}
.findOptimalSettings()
.forEditing()
.withResolution(1920, 1080, /* frameRate= */ 30.0)
.withAnyCodec()
.targetingQuality(VideoEncoder.QUALITY_LEVEL_VERY_GOOD)
.get()