Harici cihazlarla etkileşime geçen kodun WebAssembly ve Fugu API'leri ile web'e nasıl taşınabileceğini öğrenin.
Önceki bir yayında, File System Access API, WebAssembly ve Asyncify ile dosya sistemi API'lerini kullanan uygulamaların web'e nasıl taşınacağını göstermiştim. Şimdi Fugu API'lerini WebAssembly ile entegre etme ve önemli özellikleri kaybetmeden uygulamaları web'e taşımayla ilgili aynı konuya devam etmek istiyorum.
C dilinde yazılmış popüler bir USB kitaplığı olan libusb'yi WebAssembly'e (Emscripten aracılığıyla), Asyncify'ye ve WebUSB'ye taşıyarak USB cihazlarla iletişim kuran uygulamaların web'e nasıl taşınabileceğini göstereceğim.
Öncelikle bir demo
Bir kitaplığı taşırken yapılması gereken en önemli şey doğru demoyu seçmektir. Bu demo, taşınan kitaplığın özelliklerini sergilemeli, çeşitli şekillerde test etmenize olanak tanımalı ve aynı zamanda görsel açıdan ilgi çekici olmalıdır.
DSLR uzaktan kumandası fikrini seçtim. Özellikle gPhoto2 adlı açık kaynak proje, geriye dönük mühendislik yaparak çok çeşitli dijital kameralar için destek sunacak kadar uzun süredir bu alanda faaliyet gösteriyor. Çeşitli protokolleri destekler ancak en çok ilgilendiğim, libusb üzerinden gerçekleştirdiği USB desteğiydi.
Bu demoyu oluşturma adımlarını iki bölümde açıklayacağım. Bu blog yayınında, libusb'i nasıl taşıdığımı ve diğer popüler kitaplıkları Fugu API'lerine taşımak için hangi hileleri uygulamam gerektiğini açıklayacağım. İkinci yayında, gPhoto2'nin taşınması ve entegrasyonu hakkında ayrıntılı bilgi vereceğim.
Sonuç olarak, DSLR'den gelen canlı yayını önizleyen ve ayarlarını USB üzerinden kontrol edebilen çalışan bir web uygulaması elde ettim. Teknik ayrıntıları okumadan önce canlı veya önceden kaydedilmiş demoya göz atabilirsiniz:
Kameraya özgü özelliklerle ilgili not
Videoda ayarların değiştirilmesinin biraz zaman aldığını fark etmiş olabilirsiniz. Karşılaşabileceğiniz diğer sorunların çoğunda olduğu gibi, bu sorun da WebAssembly veya WebUSB'nin performansından değil, gPhoto2'nin demo için seçilen kamerayla etkileşimin nasıl olduğundan kaynaklanır.
Sony a6600, ISO, diyafram veya deklanşör hızı gibi değerleri doğrudan ayarlamak için bir API sağlamaz. Bunun yerine, bu değerleri belirtilen adım sayısı kadar artırma veya azaltma komutları sağlar. Daha da karmaşık hale getirmek gerekirse, aslında desteklenen değerlerin listesini de döndürmez. Döndürülen liste, birçok Sony kamera modelinde sabit kodlanmış gibi görünüyor.
gPhoto2, bu değerlerden birini ayarlarken aşağıdakilerden başka bir seçeneği yoktur:
- Seçilen değere doğru bir adım (veya birkaç adım) atın.
- Kameranın ayarları güncellemesi için biraz bekleyin.
- Kameranın gerçekten odaklandığı değeri geri okuyun.
- Son adımın istenen değeri atlamadığını veya listenin sonuna ya da başına sarıldığını kontrol edin.
- Tekrarla.
Bu işlem biraz zaman alabilir. Ancak değer kamera tarafından gerçekten destekliyorsa değere ulaşılır. Aksi takdirde, en yakın desteklenen değerde durur.
Diğer kameralarda farklı ayar grupları, temel API'ler ve tuhaflıklar olabilir. gPhoto2'nin açık kaynak bir proje olduğunu ve mevcut tüm kamera modellerinin otomatik veya manuel olarak test edilmesinin mümkün olmadığını unutmayın. Bu nedenle, ayrıntılı sorun raporları ve PR'ler her zaman memnuniyetle karşılanır (ancak önce sorunları resmi gPhoto2 istemcisinde yeniden oluşturduğunuzdan emin olun).
Platformlar arası uyumlulukla ilgili önemli notlar
Maalesef Windows'ta DSLR kameralar da dahil olmak üzere "iyi bilinen" tüm cihazlara WebUSB ile uyumlu olmayan bir sistem sürücüsü atanır. Windows'ta denemeyi denemek istiyorsanız bağlı DSLR'nin sürücüsünü WinUSB veya libusb ile geçersiz kılmak için Zadig gibi bir araç kullanmanız gerekir. Bu yaklaşım benim için ve diğer birçok kullanıcı için işe yaradı ancak bu yöntemi kullanmanın riski size aittir.
Linux'da, DSLR'nize WebUSB üzerinden erişime izin vermek için muhtemelen özel izinler ayarlamanız gerekir. Bu, dağıtımınıza bağlıdır.
Demo, macOS ve Android'de hazır olarak çalışır. Android telefonda denerseniz duyarlı hale getirmek için çok fazla çaba göstermediğim için yatay moda geçtiğinizden emin olun (PR'ler kabul edilir):
WebUSB'nin platformlar arası kullanımıyla ilgili daha ayrıntılı bir kılavuz için "WebUSB için cihaz oluşturma" başlıklı makalenin "Platforma özgü hususlar" bölümüne bakın.
libusb'ye yeni bir arka uç ekleme
Şimdi teknik ayrıntılara geçelim. libusb'ye benzer bir shim API sağlamak (bu daha önce başkaları tarafından yapılmıştır) ve diğer uygulamaları buna bağlamak mümkün olsa da bu yaklaşım hatalara açıktır ve daha fazla uzantı veya bakım işlemini zorlaştırır. İşleri doğru şekilde, gelecekte ana akışa katkıda bulunabilecek ve libusb ile birleştirilebilecek bir şekilde yapmak istedim.
Neyse ki libusb README'da şu bilgiler yer alıyor:
"libusb, diğer işletim sistemlerine taşınabilmesi için dahili olarak soyutlanmıştır. Daha fazla bilgi için lütfen PORTING dosyasını inceleyin.
libusb, herkese açık API'nin "arka uçlardan" ayrı olduğu şekilde yapılandırılmıştır. Bu arka uçlar, işletim sisteminin düşük seviyeli API'leri aracılığıyla cihazları listelemekten, açmaktan, kapatmaktan ve cihazlarla gerçek iletişim kurmaktan sorumludur. libusb, Linux, macOS, Windows, Android, OpenBSD/NetBSD, Haiku ve Solaris arasındaki farklılıkları bu şekilde soyutlar ve tüm bu platformlarda çalışır.
Bunun için Emscripten+WebUSB "işletim sistemi" için başka bir arka uç eklemem gerekiyordu. Bu arka uçların uygulamaları libusb/os klasöründe bulunur:
~/w/d/libusb $ ls libusb/os
darwin_usb.c haiku_usb_raw.h threads_posix.lo
darwin_usb.h linux_netlink.c threads_posix.o
events_posix.c linux_udev.c threads_windows.c
events_posix.h linux_usbfs.c threads_windows.h
events_posix.lo linux_usbfs.h windows_common.c
events_posix.o netbsd_usb.c windows_common.h
events_windows.c null_usb.c windows_usbdk.c
events_windows.h openbsd_usb.c windows_usbdk.h
haiku_pollfs.cpp sunos_usb.c windows_winusb.c
haiku_usb_backend.cpp sunos_usb.h windows_winusb.h
haiku_usb.h threads_posix.c
haiku_usb_raw.cpp threads_posix.h
Her arka uç, ortak türleri ve yardımcıları içeren libusbi.h başlığını içerir ve usbi_os_backend türüne sahip bir usbi_backend değişkeni göstermesi gerekir. Örneğin, Windows arka ucu aşağıdaki gibi görünür:
const struct usbi_os_backend usbi_backend = {
"Windows",
USBI_CAP_HAS_HID_ACCESS,
windows_init,
windows_exit,
windows_set_option,
windows_get_device_list,
NULL, /* hotplug_poll */
NULL, /* wrap_sys_device */
windows_open,
windows_close,
windows_get_active_config_descriptor,
windows_get_config_descriptor,
windows_get_config_descriptor_by_value,
windows_get_configuration,
windows_set_configuration,
windows_claim_interface,
windows_release_interface,
windows_set_interface_altsetting,
windows_clear_halt,
windows_reset_device,
NULL, /* alloc_streams */
NULL, /* free_streams */
NULL, /* dev_mem_alloc */
NULL, /* dev_mem_free */
NULL, /* kernel_driver_active */
NULL, /* detach_kernel_driver */
NULL, /* attach_kernel_driver */
windows_destroy_device,
windows_submit_transfer,
windows_cancel_transfer,
NULL, /* clear_transfer_priv */
NULL, /* handle_events */
windows_handle_transfer_completion,
sizeof(struct windows_context_priv),
sizeof(union windows_device_priv),
sizeof(struct windows_device_handle_priv