Mengambil video dari kamera USB di perangkat Linux

Latar Belakang


Beberapa waktu yang lalu, saya tergoda untuk "meningkatkan" sebuah tank dari set "Tank battle" yang terkenal, menambahkan kemampuan untuk bermain seolah-olah saya adalah seorang pengemudi tank. Idenya muncul setelah membaca beberapa artikel tentang Habré (misalnya, di sini: geektimes.ru/post/257528), di dalamnya saya menemukan bagaimana hal ini dapat dilakukan dengan router WiFi kecil dan kamera USB. Solusinya tampak sangat sederhana: router di-flash dengan firmware khusus, kamera terhubung dengannya, tangki dikendalikan oleh remote control asli, dan video dilihat di browser. Setelah dengan cepat membuat prototipe, saya menemukan bahwa video itu ditangkap dengan kualitas yang menjijikkan. Itu 320x440x30 atau 640x480x30. Ketika mode 1280x720 dihidupkan, paling-paling ada video robek dengan artefak, paling buruk itu tidak sama sekali. Mode 1920x1080 pada prinsipnya tidak bekerja. Ini sangat membuat saya kesal, karena pada PC kamera mendukung mode hingga 1920x1080x30 dan memiliki kompresi MJPG perangkat keras. Intuisi saya menyarankan bahwa implementasinya jauh dari sempurna.

Tujuan


  1. Video dalam resolusi FullHD (1920X1080) atau HD (1280x720) dan kecepatan bingkai normal (sehingga Anda dapat memutar).
  2. Saya berencana untuk memberikan mainan itu kepada anak-anak, jadi saya perlu penyalaan otomatis dan dukungan untuk menghubungkan / melepas kamera.

Secara umum, saya menginginkan sesuatu seperti ini:



Keterbatasan


Saya tidak akan mencari solusi yang berfungsi selalu dan di mana-mana. Pembatasan berikut sangat cocok untuk saya:

  1. Sinyal wifi bagus.
  2. Sejumlah koneksi terbatas, prioritas diberikan kepada kasus ketika hanya ada satu klien.
  3. Kamera mendukung mode MJPG.

HW dan SW


  1. Camcorder Logitech B910 HD ( http://www.logitech.com/en-us/product/b910-hd-webcam ).
  2. Router TP-LINK TL-MR3020. Bayi ini memiliki perangkat keras berikut: CPU MIPS 24K 400MHz, RAM 32 MiB, Flash 4 MiB, Ethernet 100 Mbit, USB 2.0 ( http://wiki.openwrt.org/en/toh/tp-link/tl-mr3020 ).
  3. . OR-WRT (http://roboforum.ru/wiki/OR-WRT), OpenWRT (http://openwrt.org/, 12.07 15.05).
  4. . , .
  5. “ ”.


Secara umum, ini adalah konfigurasi yang sangat lemah, terutama jika Anda ingat bahwa bingkai dalam format YUV420 ukuran 1920X1080 membutuhkan 4 MiB (2 byte per piksel). Saya didorong bahwa kamera mendukung kompresi MJPG perangkat keras. Eksperimen menunjukkan bahwa frame FullHD terkompresi biasanya <500 KiB. Jadi saya memutuskan untuk melanjutkan penelitian. Ternyata untuk mengambil video dan streaming melalui HTTP, mjpg-streamer (http://sourceforge.net/projects/mjpg-streamer/) digunakan. Analisis kodenya menunjukkan bahwa ia menggunakan 1 streaming untuk mengambil video + streaming terpisah untuk setiap klien. Ini bukan solusi terbaik untuk sistem inti tunggal, karena membutuhkan sinkronisasi utas dan memori untuk tumpukan untuk setiap utas. Dia juga menyalin frame yang diambil. Secara umum, mjpg-streamer menjadi tersangka # 1.

Temuan yang menarik


Mempelajari mjpg-streamer, saya menemukan bahwa pengambilan video di Linux dilakukan menggunakan pustaka v4l2 dan antrian buffer digunakan untuk mengabadikannya. Saat men-debug inisialisasi buffer ini di mjpg-streamer, saya menemukan bahwa bahkan untuk mode MJPG ukurannya sangat besar dan secara tak terduga bertepatan dengan ukuran frame yang tidak terkompresi. Jadi saya mulai curiga bahwa saya harus masuk ke kode driver UVC, yang bertanggung jawab untuk mendukung kamera.

Analisis Kode Pengemudi dan Keberhasilan Pertama


Mempelajari kode, saya sampai pada kesimpulan bahwa ukuran buffer diminta oleh kamera dan kamera saya mengembalikan ukuran frame yang tidak terkompresi. Ini mungkin solusi teraman dari sudut pandang pengembang kamera. Tetapi itu juga tidak optimal. Saya memutuskan bahwa untuk kasus saya, Anda dapat menyesuaikan ukuran buffer yang diperlukan menggunakan rasio kompresi minimum eksperimental. Saya memilih k = 5. Dengan nilai ini, saya memiliki margin sekitar 20%.

Penyimpangan kecil.
, JPG. . , .

Kode driver UVC siap untuk menambahkan berbagai jenis solusi "khusus", dan saya dengan mudah menemukan tempat untuk menyesuaikan ukuran buffer (fungsi uvc_fixup_video_ctrl ()). Selain itu, driver mendukung serangkaian kebiasaan yang memungkinkan Anda untuk mendukung kamera dengan berbagai penyimpangan dari standar UVC. Secara umum, pengembang driver telah melakukan yang terbaik yang mungkin untuk mendukung kamera kebun binatang.

Dengan menambahkan koreksi ukuran buffer, saya mendapatkan operasi yang stabil dalam mode 1280x720 dan bahkan dalam mode 1920x1080. Hore! Setengah masalah terpecahkan!

Mencari petualangan baru


Sedikit senang dengan kesuksesan pertama, saya ingat bahwa mjpg-streamer jauh dari sempurna. Tentunya Anda dapat melakukan sesuatu yang sederhana, tidak seunivers mjpg-streamer, tetapi lebih cocok untuk kondisi saya. Jadi saya memutuskan untuk membuat uvc2http.

Di mjpg-streamer, saya tidak suka menggunakan beberapa stream dan menyalin buffer. Ini menentukan arsitektur solusi: 1 stream dan tidak ada salinan. Menggunakan non-blocking IO, ini dilakukan cukup sederhana: menangkap frame dan mengirimkannya ke klien tanpa menyalin. Ada masalah kecil: saat kami mengirim data dari buffer, kami tidak dapat mengembalikan buffer kembali ke antrian. Dan sementara buffer tidak ada dalam antrian, pengemudi tidak dapat memasukkan bingkai baru ke dalamnya. Tetapi jika ukuran antrian> 1, maka ini menjadi mungkin. Jumlah buffer menentukan jumlah koneksi maksimum yang dapat dijamin untuk dilayani. Artinya, jika saya ingin mendukung 1 klien, maka 3 buffer sudah cukup (driver menulis ke satu buffer, data dikirim dari yang kedua, yang ketiga ada dalam persediaan untuk menghindari bersaing dengan driver untuk buffer ketika mencoba untuk mendapatkan bingkai baru).

Uvc2http


Uvc2http terdiri dari dua komponen: UvcGrabber dan HttpStreamer. Yang pertama bertanggung jawab untuk menerima buffer (bingkai) dari antrian dan mengembalikannya ke antrian. Yang kedua bertanggung jawab untuk melayani klien melalui HTTP. Ada beberapa kode lagi yang menghubungkan komponen-komponen ini. Detail dapat ditemukan di sumber.

Masalah yang tidak terduga


Semuanya luar biasa: aplikasi bekerja dan dalam resolusi 1280x720 menghasilkan 20+ bingkai / detik. Saya membuat perubahan kosmetik pada kode. Setelah beberapa perubahan, saya mengukur frame rate. Hasilnya menyedihkan - kurang dari 15 frame. Saya bergegas mencari penyebab degradasi. Saya mungkin menghabiskan 2 jam di mana frekuensi menurun dengan masing-masing pengukuran hingga nilai 7 frame / detik. Pikiran yang berbeda masuk ke kepala saya tentang degradasi karena kerja panjang dari router, karena terlalu panas. Itu adalah sesuatu yang tidak bisa dimengerti. Pada titik tertentu, saya mematikan streaming dan melihat bahwa hanya satu tangkapan (tanpa streaming) memberikan 7 frame yang sama. Saya bahkan mulai curiga ada masalah dengan kamera. Secara umum, omong kosong. Itu di malam hari dan kamera, ternyata jendela, menunjukkan sesuatu yang abu-abu. Untuk mengubah gambar suram, saya memutar kamera ke dalam ruangan. Dan lihat!Frame rate meningkat menjadi 15 dan saya mengerti segalanya. Kamera secara otomatis menyesuaikan waktu pencahayaan dan pada titik tertentu waktu ini menjadi lebih lama dari durasi bingkai pada frekuensi tertentu. Selama dua jam ini, yang berikut ini terjadi: pada awalnya hari mulai gelap (sudah malam), dan kemudian saya memutar kamera ke dalam ruangan yang terang. Mengarahkan kamera ke kandil, saya mendapat 20+ bingkai / detik. Hore.


  1. . 1-1.5 .
  2. . , , qv4l2, . : - . .
  3. . USB , ( ) ( ). USB ( ).
  4. Router memiliki sedikit ruang memori dan disk. Untuk alasan ini, saya menolak OR-WRT dan mengkompilasi gambar OpenWRT saya, menghapus semua yang tidak berguna darinya.

hasil


Di bawah ini adalah piring dengan hasil membandingkan mjpg-streamer dan uvc2http. Singkatnya, ada peningkatan yang signifikan dalam konsumsi memori dan peningkatan kecil dalam frame rate dan pemanfaatan CPU.
1280x7201920x1080
VSZ, KB, 1 klienVSZ, KB, 2 klienCPU,%, 1 klienCPU,%, 2 klienFPS, f / s, 1 klienFPS, f / s, 2 klienVSZ, KB, 1 klienVSZ, KB, 2 klienCPU,%, 1 klienCPU,%, 2 klienFPS, f / s, 1 klienFPS, f / s, 2 klien
Mjpg-streamer1686019040264317.6lima belas254562581228lima puluh13.810
uvc2http3960396026432219.675767576284315.512.2

Dan tentu saja video yang saya buat dengan anak-anak:



Foto tangki yang dihasilkan (ternyata mirip gerobak gipsi):



Menggunakan


Sumber ada di sini . Untuk digunakan pada PC Linux, Anda hanya perlu mengkompilasi (asalkan Anda tidak ingin menambal driver UVC). Utilitas ini dibangun menggunakan CMake dengan cara standar. Jika Anda perlu menggunakannya di OpenWRT, maka Anda perlu mengambil langkah-langkah tambahan:

  1. Salin konten direktori OpenWrt-15.05 ke root dari repositori OpenWRT. File-file ini hanya untuk OpenWRT 15.05. Mereka menggambarkan paket baru untuk OpenWRT dan tambalan untuk driver UVC.
  2. , quirk UVC_QUIRK_COMPRESSION_RATE uvc_driver.c. UVC. , wiki.openwrt.org/doc/devel/patches. uvc_ids. :

    /* Logitech B910 HD Webcam */
    	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
    				| USB_DEVICE_ID_MATCH_INT_INFO,
    	  .idVendor		= 0x046d,
    	  .idProduct		= 0x0823,
    	  .bInterfaceClass	= USB_CLASS_VIDEO,
    	  .bInterfaceSubClass	= 1,
    	  .bInterfaceProtocol	= 0,
    	  .driver_info		= UVC_QUIRK_RESTORE_CTRLS_ON_INIT
    				| UVC_QUIRK_COMPRESSION_RATE }, // Enable buffer correction for compressed modes
    

  3. OpenWRT (http://wiki.openwrt.org/doc/howto/build). uvc2http Multimedia.
  4. uvc2http ( ) . , .
  5. Instal paket pada perangkat / perbarui sistem

Apa berikutnya


Solusinya terdiri dari dua bagian: patch driver dan algoritma streaming lainnya. Patch driver dapat dimasukkan dalam versi baru dari kernel Linux, tetapi ini adalah keputusan yang kontroversial, karena didasarkan pada asumsi rasio kompresi minimum. Utilitas, menurut saya, sangat cocok untuk digunakan pada sistem yang lemah (mainan, sistem pengawasan video rumahan), dan dapat sedikit ditingkatkan dengan menambahkan kemampuan untuk menentukan pengaturan kamera melalui parameter.

Algoritma streaming dapat ditingkatkan karena ada margin pada pemuatan CPU dan lebar saluran (saya dengan mudah menerima 50+ MBit dari router yang menghubungkan sepuluh klien). Anda juga dapat menambahkan dukungan suara.

Source: https://habr.com/ru/post/id386273/


All Articles