Tonton saya secara penuh: peras video langsung paling banyak di platform seluler



Cara termudah untuk memutar video pada perangkat seluler adalah membuka tautan dengan pemutar yang ada di sistem, tetapi ini tidak selalu efektif.

Anda dapat mengambil ExoPlayer dan mengoptimalkannya, atau Anda bahkan dapat menulis pemutar video Anda sendiri hanya menggunakan codec dan soket. Artikel ini akan berbicara tentang pekerjaan streaming dan pemutaran video, dan bagaimana cara mengurangi penundaan dalam memulai video, mengurangi waktu respons antara streamer dan penonton, dan mengoptimalkan konsumsi daya dan beban besi.

Kami akan menganalisis ini menggunakan aplikasi spesifik sebagai contoh: Klien seluler Odnoklassniki (tempat video diputar) dan OK Live (tempat siaran dialirkan dari telepon ke 1080p). Tidak akan ada kelas master tentang cara memutar video dengan referensi, dengan contoh kode. Cerita ini akan fokus pada bagaimana video terlihat dari dalam, dan bagaimana, mengetahui arsitektur umum pemutar video dan streaming video, Anda dapat memahami sistem apa pun dan membuatnya lebih baik.

Materi tersebut didasarkan pada transkrip laporan Alexander Tobol ( @alatobol ) dan Ivan Grigoriev ( @ivan_a ) dari konferensi Mobius .




Entri


Sebagai permulaan - beberapa nomor tentang video di Odnoklassniki.

Lalu lintas VOD harian rata-rata harian (video berdasarkan permintaan) lebih dari satu setengah terabit per detik, dan untuk siaran langsung - lebih dari 3 terabit per detik.

Sekarang di OK, ada lebih dari 870 juta tayangan video per hari, lebih dari setengahnya berasal dari perangkat seluler.



Jika Anda melihat riwayat streaming, maka video seluler muncul di YouTube pada 2007. Kami naik kereta ini nanti, tetapi pada 2014-2015 kami sudah memiliki pemutaran video 4K di perangkat seluler, dan dalam beberapa tahun terakhir kami telah secara aktif mengembangkan pemain kami. Tentang ini dan pembicaraan akan berlalu.

Tren kedua yang muncul dengan Periscope pada 2015 adalah penyiaran dari telepon. Kami meluncurkan aplikasi OK Live kami, yang memungkinkan Anda untuk streaming bahkan video Full HD melalui jaringan seluler. Di bagian kedua materi, kami juga akan berbicara tentang streaming.

Kami tidak akan berkutat pada API untuk bekerja dengan video, tetapi saat ini menyelam dalam-dalam dan mencoba mencari tahu apa yang terjadi di dalam.



Ketika Anda merekam video pada kamera, ia sampai ke codec, dari sana ke soket, lalu ke server (terlepas dari apakah VOD atau Live). Dan kemudian server dalam urutan terbalik mendistribusikannya ke audiens.

Mari kita mulai dengan pemain KPI. Apa yang kita inginkan darinya?

  • Bingkai pertama cepat. Pengguna tidak ingin menunggu dimulainya pemutaran.
  • Kurangnya buffering. Tidak ada yang suka lari ke badan.
  • Kualitas tinggi Ketika hampir tidak ada konten 4K, kami sudah membuat dukungan 4K β€œtumbuh lebih besar”: jika Anda mematikan pemain untuk itu dan mengetahui kinerjanya, maka 1080p akan bermain dengan sempurna bahkan pada perangkat yang lemah.
  • Persyaratan UX. Kita membutuhkan video untuk diputar dalam rekaman saat menggulir, dan untuk rekaman itu kita perlu mengambil video sebelumnya.


Ada banyak masalah dengan cara ini. Aliran untuk video 4K besar, dan kami bekerja pada perangkat seluler di mana ada masalah dengan jaringan, ada berbagai fitur format video dan wadah pada perangkat yang berbeda, dan perangkat itu sendiri juga dapat menjadi masalah.

Menurut Anda di mana video dimulai lebih cepat, di iOS atau Android?

Faktanya, jawaban apa pun benar: tergantung pada apa, di mana, dan bagaimana cara bermainnya. Jika kita mengambil wilayah Rusia dengan jaringan yang tidak terlalu bagus, kita akan melihat bahwa AVPlayer dimulai sekitar 800 milidetik. Tetapi dengan jaringan yang sama, ExoPlayer di Android, memainkan format yang berbeda, akan meluncurkannya dalam 660 ms. Dan jika Anda membuat pemutar Anda di iOS, maka itu akan dapat berjalan lebih cepat.



Ada nuansa di mana kami mengukur rata-rata untuk pengguna, dan rata-rata kekuatan perangkat iOS lebih tinggi daripada di Android.

Bagian pertama dari materi tersebut adalah teoretis: kita akan mempelajari apa itu video dan seperti apa arsitektur Live player mana pun. Dan di bagian kedua, mari kita bandingkan para pemain dan bicarakan kapan harus menulis sendiri.

Bagian satu


Apa itu video?


Mari kita mulai dengan yang paling mendasar. Video adalah 60 atau 24 gambar per detik.

Jelas, menyimpan ini dengan set gambar penuh cukup mahal. Oleh karena itu, mereka disimpan dengan cara ini: beberapa frame disebut frame referensi (I-frame), sementara yang lain (B-frame dan P-frame) disebut "diffs". Bahkan, Anda memiliki file jpg dan perubahan khusus untuk file itu.



Ada juga konsep GOP (kelompok gambar) - ini adalah seperangkat bingkai independen, yang dimulai dengan kerangka referensi dan berlanjut dengan serangkaian diffs. Itu dapat dimainkan secara independen, membongkar dan sebagainya. Pada saat yang sama, jika Anda kehilangan opornik dalam grup, frame yang tersisa tidak lagi relevan.

Ada banyak algoritma pengkodean, matriks transformasi, pencarian gerakan, dan sejenisnya - inilah perbedaan codec.

Kinerja codec





H.264 klasik telah dikenal sejak tahun 2003 dan telah berkembang dengan baik. Kami akan mengambil efektivitasnya sebagai basis. Dia bekerja dan bermain di mana-mana. Ini memiliki dukungan perangkat keras untuk CPU / GPU (keduanya di iOS, di Android). Ini berarti ada beberapa jenis coprocessor khusus yang dapat mengkodekannya, atau set instruksi bawaan yang memungkinkan Anda melakukan ini dengan cepat. Rata-rata, dukungan perangkat keras memberikan kinerja hingga 10x lebih cepat dan menghemat masa pakai baterai.

Pada 2010, VP8 dari Google muncul. Dalam hal efisiensi, itu tidak berbeda dari H.264. Sebenarnya efektivitas codec adalah hal yang sangat kontroversial. Di dahi, diukur sebagai rasio dari video asli ke yang dikompresi, tetapi jelas bahwa ada artefak video yang berbeda. Oleh karena itu, kami menyediakan tautan ke perbandingan terperinci dari codec dari Universitas Negeri Moskow. Tapi di sini kita membatasi diri pada kenyataan bahwa VP8 difokuskan pada organisasi perangkat lunak, Anda dapat menyeretnya ke mana saja, dan biasanya digunakan sebagai mundur jika tidak ada dukungan H.264 asli.

Pada 2013, generasi baru codec muncul - H.265 (HEVC) dan VP9. Codec H.265 memberikan peningkatan efisiensi 50%, tetapi pada video Android tidak dapat disandikan, decoder hanya muncul dengan Android 5.0+. Tetapi di iOS ada dukungan.

Ada alternatif untuk H.265 - VP9. Semua sama, tetapi didukung oleh Google. Yah, V9 adalah YouTube, dan H.265 adalah Netflix. Jadi masing-masing memiliki kekhasan sendiri: satu tidak akan bekerja di iOS, yang lain akan memiliki masalah pada Android. Pada akhirnya, banyak yang tetap pada H.264.

Di masa depan, kami dijanjikan codec AV1, ia sudah memiliki implementasi perangkat lunak, dan efisiensinya 35% lebih tinggi daripada codec 2013. Sekarang tersedia di Chrome dan Firefox, dan pada tahun 2020 Google menjanjikan dukungan perangkat keras - Saya pikir, kemungkinan besar, kita semua akan pindah ke sana.

Akhirnya, mereka baru-baru ini mengumumkan codec H.266 / JVEC, mengatakan bahwa semuanya akan lebih baik dan lebih cepat.

Pola utama: semakin tinggi efisiensi codec, semakin banyak sumber daya komputasi yang dibutuhkan dari perangkat.

Secara umum, secara default, semua orang mengambil H.264, dan kemudian untuk perangkat tertentu itu bisa rumit.

Kualitas, Resolusi, dan Bitrate


Pada tahun 2019, Anda tidak akan mengejutkan siapa pun dengan kualitas adaptif: pengguna mengunggah atau mengalirkan video dalam satu kualitas, dan kami memotong sederet kualitas yang berbeda dan mengirim yang paling cocok ke perangkat.

Dalam hal ini, perlu bahwa resolusi video berkorelasi dengan bitrate. Jika resolusi digandakan, laju bit juga harus berlipat ganda:



Jelas, jika Anda mengompres resolusi besar dengan bit rate yang rendah atau sebaliknya, maka akan ada artefak atau pembakaran bit rate yang tidak berguna.

Bagaimana perbandingan bitrate dari video yang disandikan dengan jumlah informasi asli? Pada layar 4K, kita dapat memainkan hampir 6 Gb / s informasi (jika Anda menghitung semua piksel dan frekuensinya pada 60 frame per detik), sedangkan bitrate codec dapat mencapai 50 Mb / s. Artinya, codec memampatkan video hingga 100 kali.

Teknologi pengiriman


Anda memiliki audio dan video yang dikemas dengan beberapa codec. Jika Anda hanya menyimpannya di rumah, Anda dapat menambahkan semua audio dan video dengan menambahkan indeks kecil yang memberi tahu Anda dari detik apa audio dan video dimulai. Tetapi video tidak dapat dikirim ke telepon, dan untuk streaming ke pemirsa online, ada dua kelas utama protokol: streaming dan segmen.



Protokol streaming menyiratkan bahwa Anda memiliki semacam status di server, klien juga, dan mengirimkan data. Server dapat menyesuaikan, misalnya, kualitas. Sangat sering ini adalah koneksi UDP.

Protokol seperti itu sangat kompleks untuk server dan sulit untuk disampaikan. Untuk terjemahan yang banyak dimuat, kami menggunakan protokol tersegmentasi yang berfungsi di atas HTTP, dapat di-cache oleh nginx dan CDN, dan lebih mudah untuk didistribusikan. Dan server tidak bertanggung jawab atas apa pun dan, dalam hal ini, stateless.

Seperti apa bentuk pengiriman segmen: kami memotong video yang ada menjadi segmen, menemani mereka dengan tajuk untuk audio dan video, MPEG-TS dan MP4 sebagai contoh transportasi. Di ponsel kami memberikan manifes dengan informasi tentang di mana dan untuk kualitas apa segmen terletak, dan manifes ini dapat diperbarui secara berkala.

Secara historis, Apple memberikan melalui HLS, dan Android melalui DASH. Mari kita lihat perbedaannya.

Mari kita mulai dengan HLS yang lebih lama, ia memiliki manifes yang menggambarkan semua kualitas yang tersedia - rendah, sedang, tinggi, dan sebagainya. Ada bitrate dari kualitas ini sehingga pemain dapat segera memilih yang tepat. Dia memilih kualitas dan mendapat manifes bersarang dengan daftar tautan ke segmen. Durasi segmen ini juga ditunjukkan.



Ada fitur yang menarik di sini: untuk mulai memainkan frame pertama, Anda harus melakukan dua perjalanan bolak-balik tambahan. Permintaan pertama Anda mendapatkan manifes utama, manifes bersarang kedua, dan hanya kemudian mengakses data itu sendiri, yang tidak terlalu baik.



Kesulitan kedua: HLS dirancang untuk bekerja di Internet melalui HTTP, tetapi Transport Stream MPEG-2 yang lama dipilih sebagai wadah untuk data video, yang dikembangkan untuk tujuan yang sama sekali berbeda: mentransmisikan sinyal dari satelit di saluran bising. Akibatnya, kami mendapatkan tajuk tambahan, yang dalam kasus HLS sama sekali tidak berguna dan hanya menambah overhead.



Tambahkan overhead jaringan dan kompleksitas parsing: jika Anda mencoba memainkan 4K di DASH dan HLS di Chrome, Anda akan merasakan perbedaannya ketika komputer Anda "lepas landas" dengan paket HLS.

Apple sedang mencoba menyelesaikan ini. Pada 2016, mereka mengumumkan kemungkinan menggunakan Fragmented MPEG-4, ada beberapa dukungan untuk DASH di HLS, tetapi RTT tambahan dan fitur-fiturnya tidak hilang.



DASH terlihat sedikit lebih sederhana: Anda memiliki satu manifes dengan semua kualitas di dalamnya, dan setiap kualitas adalah sekumpulan segmen. Anda dapat memainkan satu segmen untuk bermain dalam satu kualitas, lalu pahami bahwa kecepatan telah meningkat, dari segmen berikutnya untuk beralih ke yang lain. Semua segmen selalu dimulai dengan bingkai referensi, memungkinkan pengalihan.

Ini adalah piring kecil tentang apa yang harus dipilih:



Dalam HLS, codec video yang didukung secara historis hanya H.264, dalam MPEG-DASH Anda dapat mendorong siapa pun. Masalah utama HLS adalah perjalanan pulang-pergi ekstra di awal, ini berfungsi baik di iOS dan Android dengan 4.0. Dan DASH terutama didukung oleh Google (Chrome dan Android) dan tidak dapat diputar di iOS.

Arsitektur pemain


Kami menyortir video lebih atau kurang, sekarang mari kita lihat seperti apa pemain itu.



Mari kita mulai dengan bagian jaringan: ketika memulai video, pemain mengikuti manifes, entah bagaimana memilih kualitas, kemudian mengikuti segmen, mengunduhnya, kemudian perlu men-decode frame, memahami bahwa ada cukup frame di buffer untuk diputar, dan kemudian mulai pemutaran.

Arsitektur umum pemain:



Ada bagian jaringan, soket, tempat data berasal.

Setelah itu - demultiplexer atau sejenisnya yang mendapat stream audio dan video dari transport (HLS / DASH). Dia mengirim mereka ke codec yang sesuai.

Codec mendekode video dan audio, dan kemudian hal yang paling menarik terjadi: mereka perlu disinkronkan agar video dan audio Anda diputar secara bersamaan. Ada berbagai mekanisme berdasarkan cap waktu untuk ini.

Maka Anda perlu membuatnya di suatu tempat - di Tekstur, Permukaan, GL atau Logam, di mana saja.

Dan pada input ada kontrol beban, yang memuat data dan mengontrol buffer.

Seperti apa kontrol beban di semua pemain? Ada sejumlah data yang perlu diunduh. Pemain menunggu sampai mereka diunduh, kemudian mulai bermain, dan kami unduh lebih lanjut. Kami memiliki batas buffer maksimum, setelah mencapai saat unduhan berhenti. Setelah itu, selama pemutaran, jumlah data dalam buffer turun - dan ada batas minimum di mana ia mulai memuat. Jadi semua ini juga hidup:



Seperti apakah tampilan loop utama? Gamer yang akrab dengan konsep "tick tick", sepertinya ada di sini. Ada bagian yang bertanggung jawab untuk jaringan yang menumpuk semuanya menjadi satu buffer. Ada extractor yang membongkar dan mengirimkannya ke codec, di mana buffer antara dan kemudian akan digunakan untuk rendering. Dan Anda memiliki tanda centang yang menggeser dan mengendalikan mereka, berkaitan dengan sinkronisasi.



Di luar, Anda memiliki aplikasi yang mengirimkan beberapa perintah melalui antrian pesan dan menerima beberapa informasi melalui pendengar. Dan kadang-kadang tekanan balik mungkin muncul, yang mengurangi kualitas - misalnya, dalam situasi di mana buffer Anda habis atau render tidak dapat mengatasinya (misalnya, drop frames muncul).

Pengukur


Saat beradaptasi, pemain mengandalkan 2 parameter utama: kecepatan jaringan dan buffer data.

Bagaimana tampilannya: pertama, kualitas tertentu direproduksi, misalnya, 720p. Anda memiliki buffer yang terus bertambah, semakin banyak caching. Kemudian kecepatan bertambah, Anda mengerti bahwa Anda dapat mengunduh lebih banyak lagi, buffer bertambah. Dan pada saat ini Anda mengerti bahwa Anda menginjak beberapa batas buffer minimum ketika Anda dapat mencoba kualitas berikut.



Jelas bahwa Anda perlu mencobanya dengan hati-hati: ada juga estimator yang mengatakan jika Anda dapat memenuhi kualitas ini dalam hal kecepatan jaringan. Jika Anda masuk ke dalam penilaian ini dan stok buffer memungkinkan, maka Anda beralih, misalnya, ke 1080p dan terus bermain.

Lebih dari perlindungan tekanan


Bersama kami, dia muncul dari waktu ke waktu melalui trial and error. Kebutuhan untuk itu muncul ketika Anda sedikit membebani peralatan Anda.

Ada situasi ketika jaringan tumpul selama pemutaran atau sumber daya habis di backend. Ketika pemain melanjutkan pemutaran, ia mulai mengejar.

Satu set besar segmen telah terakumulasi dalam manifes pemain pada saat ini, ia dengan cepat mengunduh semuanya sekaligus, dan kami mendapatkan "pukulan lalu lintas". Situasi ini dapat diperburuk jika terjadi timeout pada klien dan pemain mulai meminta kembali data. Oleh karena itu, sangat penting untuk menyediakan tekanan balik dalam sistem.

Cara sederhana pertama yang kami, tentu saja, gunakan adalah throttler di server. Dia mengerti bahwa lalu lintas berakhir, mengurangi kualitas dan sengaja memperlambat pelanggan agar tidak mendapatkan pukulan yang hebat.



Tapi ini tidak mempengaruhi penaksir dengan sangat baik. Mereka dapat menghasilkan "tikungan" yang sama. Karena itu, jika memungkinkan, mendukung penghapusan kualitas dari manifes. Untuk melakukan ini, Anda harus memperbarui manifes, atau jika ada saluran umpan balik, berikan perintah untuk menghapus kualitas, dan pemain akan secara otomatis beralih ke manifes lain, lebih rendah.

Pemain


Di iOS, hanya ada AVPlayer asli, tetapi di Android ada pilihan. Ada MediaPlayer asli, tetapi ada sumber terbuka ExoPlayer berbasis Java yang aplikasi "bawa." Apa pro dan kontra mereka?

Bandingkan ketiganya:



Dalam hal streaming adaptif, ExoPlayer memainkan DASH / HLS dan memiliki banyak modul yang dapat diupgrade untuk protokol lain, sementara AVPlayer semakin buruk.

Dukungan untuk versi sistem operasi, pada prinsipnya, cocok untuk semua orang di mana saja.

Prefetching adalah ketika Anda tahu bahwa setelah akhir satu video Anda ingin memainkan yang berikut dalam rekaman, dan memuatnya.

Ada masalah dengan perbaikan bug dari pemain asli. Dalam kasus ExoPlayer, Anda cukup menggulungnya ke versi baru aplikasi Anda, tetapi dalam AVPlayer asli dan MediaPlayer bug akan diperbaiki hanya pada rilis OS berikutnya. Kami menemukan ini dengan menyakitkan: di iOS 8.01 video kami mulai diputar dengan buruk, di iOS 8.02 seluruh portal berhenti bekerja, di 8.03 semuanya bekerja kembali. Dan tidak ada yang bergantung pada kami dalam hal ini, kami hanya duduk dan menunggu Apple untuk meluncurkan versi berikutnya.

Tim ExoPlayer berbicara tentang inefisiensi konsumsi energi dalam hal audio. Ada rekomendasi umum dari Google: untuk memutar audio, gunakan MediaPlayer, untuk semua yang lain Exo.

Dipahami, kita akan menggunakan ExoPLayer dengan DASH untuk video di Android, dan AVPlayer dengan HLS di iOS.

Bingkai pertama cepat


Sekali lagi, ingat waktu sampai frame pertama. Bagaimana tampilannya di iOS HLS: RTT pertama di belakang manifes, lalu RTT lain di belakang manifes bersarang, baru setelah itu - dapatkan segmen dan mainkan. Di Android, satu RTT lebih sedikit, itu mulai sedikit lebih baik.



Ukuran penyangga


Sekarang mari kita berurusan dengan buffer. Kami memiliki jumlah data minimum yang perlu diunduh sebelum kami mulai bermain. Dalam AVPlayer, nilai ini dikonfigurasikan menggunakan AVPlayerItem preferForwardBufferDuration.



Di Android, ExoPlayer memiliki lebih banyak mekanisme konfigurasi. Ada buffer minimum yang sama yang diperlukan untuk memulai. Tetapi ada juga pengaturan yang terpisah untuk penolakan (jika jaringan Anda jatuh, data dari buffer habis, dan kemudian dikembalikan):



Apa untungnya? Jika Anda memiliki jaringan yang bagus, Anda dengan cepat memulai dan berjuang untuk frame pertama yang cepat, untuk pertama kalinya Anda dapat mencoba mengambil kesempatan. Tetapi jika jaringan rusak selama pemutaran, jelas bahwa Anda perlu meminta lebih banyak buffering untuk bermain selama penolakan sehingga tidak ada masalah berulang.

Kualitas asli





HLS pada iOS memiliki masalah keren: selalu mulai diputar dari kualitas pertama dalam manifes m3u8. Apa yang Anda berikan padanya akan dimulai. Dan hanya dengan itu akan mengukur kecepatan unduhan dan mulai bermain dalam kualitas normal. Jelas bahwa ini tidak boleh diizinkan.

Optimalisasi logis - mengurutkan ulang kualitas. Baik di server (dengan menambahkan parameter tambahan ke preferensi kualitas, ia mengurutkan ulang manifes), atau pada klien (membuat proxy yang akan melakukan ini untuk Anda).

Dan di Android, ada parameter DefaultBandwidthMeter untuk ini. Ini memberi nilai yang dianggap sebagai bandwidth default band Anda.



Cara kerjanya: ada tabel besar konstanta dalam kode, dan parameternya sederhana - negara (wilayah) dan jenis koneksi (wi-fi, 2G, 3G, 4G). Apa artinya? Misalnya, jika Anda memiliki Wi-Fi dan berlokasi di AS, bandwidth awal Anda adalah 5,6 Mbps. Dan jika 3G adalah 700 kbps.

Dapat dilihat bahwa, menurut perkiraan Google, 4G di Rusia 2-3 kali lebih cepat daripada di Amerika.

, β€” , . , , , , .

, , , , . , ( Android ).


(seek), , . , , , .



, , - . iOS, , , , ( , , ).

ExoPlayer 2.7.0 , , Β« Β». . , .



( , ), - , Android prepare(mediaSource), seekTo(). , , , . β€” :



, ( , ), . ( 100 ), , .




iOS , Android legacy-.
TextureView. , , , , UI. β€” .

SurfaceView. , . Android- . YouTube , .

GLSurfaceView β€” . , .



: , ExoPlayer, 23%. «» 10%. 4% . 4% β€” , .

: Android


  • MediaPlayer , ExoPlayer
  • start, seek, swap
  • ,
  • view

: iOS


iOS :

  • RTT HLS AVPlayer
  • AVPlayer#pause
  • β€” , iOS


DASH-, Β« live-Β». :

  • cURL GCDAsyncSocket
  • AVAssetReader,
  • CADisplayLink
  • AVSampleBufferDisplayLayer


, . 28%, «» 6%. , HLS DASH 100 /, 6%.

iOS :

  • start seek
  • HLS over Fragmented mp4
  • DASH-


, .

:


, , .

  • ( mp4)
  • (ExoPlayer, AVPlayer)
  • firstFrame, seek, emptyBuffer
  • ( )
  • - , . 4, : performance, , .


β€” .

:


, ?



API . API iOS Android, β€” , .

: - wrapper , POSIX-, , .

?

  • Mulai cepat


?

  • bandwidth
  • (N x RTT, RTT)






β€” . , , .

: , , . β€” low latency.

, β€” . .

β€” 4K. , , . , 30 , . , .


, , , . ( 100 ).

- , , .

. 100 , . , 300 kbps FullHD- 480p, FullHD . , : , , overhead-. .

:



, , . , - , , .

MediaCodec VideoToolbox ( ). Server Transcoder.

β€” , .


Ketika kami mulai mempelajari streaming, kami menemukan sejumlah kompromi. Secara khusus, ada segitiga di sudut-sudut yang keandalannya reliabilitas (tanpa tetes), throughput adalah bandwidth (seberapa banyak kita menggunakan jaringan) dan latensi rendah adalah latensi rendah (kita mendapatkan latensi rendah).



Jika kita mulai mengoptimalkan salah satu dari parameter ini, sisanya pasti akan gagal. Kita tidak bisa mendapatkan semuanya sekaligus, kita harus mengorbankan sesuatu.

Protokol


Protokol yang akan kita lihat hari ini: RTMP dan WebRTC adalah protokol standar, OKMP adalah protokol khusus kami.

Perlu disebutkan bahwa RTMP berjalan di atas TCP dan dua lainnya di UDP.

RTMP


Apa yang dia berikan? Dengan cara tertentu, ini adalah standar yang didukung oleh semua layanan - YouTube, Kedutan, Flash, OK. Mereka menggunakannya agar pengguna dapat mengunggah streaming langsung. Jika Anda ingin mengalirkan streaming langsung ke layanan pihak ketiga, kemungkinan besar Anda harus bekerja dengan RTMP.

Penundaan minimum yang berhasil kami capai dari tape drive ke pemain adalah 300 ms, tetapi ini berada dalam jaringan yang ideal di cuaca cerah. Ketika kita memiliki jaringan nyata, penundaan biasanya tumbuh menjadi 2-3 detik, dan jika semuanya buruk dengan jaringan, itu dapat tumbuh hingga puluhan detik.

RTMP mendukung perubahan resolusi dan bit rate on the fly (protokol lain yang disebutkan adalah sama, tetapi ada informasi yang salah tentang RTMP bahwa tidak ada perubahan dengan cepat).

Dari minus: dibangun pada TCP (kami akan menjelaskan nanti mengapa ini buruk), keterlambatan tidak terkendali.

Jika Anda melihat segitiga, RTMP tidak akan dapat memberikan latensi rendah. Itu bisa didapat, tapi sama sekali tidak dijamin.

Selain itu, RTMP sedikit omong kosong: tidak mendukung codec baru, karena Adobe tidak melakukan ini, dan dokumentasinya cukup kuno dan bengkok.



Mengapa TCP tidak cocok untuk siaran langsung? TCP memberikan jaminan pengiriman: data yang Anda masukkan ke soket akan dikirimkan tepat dalam urutan dan dalam bentuk di mana Anda meletakkannya di sana. Tidak ada yang akan dijatuhkan atau disusun kembali. TCP akan melakukan ini atau mati. Tetapi ini berarti bahwa jaminan keterlambatan dikecualikan - dia tidak akan dapat melepaskan data lama, yang sudah, mungkin, tidak perlu dikirim. Buffer, backlog, dan sebagainya mulai tumbuh.



Sebagai ilustrasi, masalah Head of Line menghalangi. Ditemukan tidak hanya dalam streaming, tetapi juga dalam banyak kasus lainnya.

Apa ini Kami memiliki buffer penerima yang awalnya kosong. Kami menerima data dari suatu tempat: banyak data dan banyak paket IP. Kami menerima paket IP pertama, dan pada penerima dengan menggunakan metode recv () kita bisa mengurangi paket ini, mendapatkan data, kehilangan, render. Namun tiba-tiba paket kedua hilang. Apa yang terjadi selanjutnya?

Untuk memulihkan paket IP yang hilang, TCP harus mengirim ulang. Agar ini terjadi, Anda harus menghabiskan RTT, sementara pengiriman ulang juga bisa hilang, dan kami akan terus berputar. Jika ada banyak paket, ini pasti akan terjadi.

Setelah ini muncul banyak data yang tidak bisa kita baca, karena kita berdiri dan menunggu paket kedua. Meskipun ia menunjukkan bingkai siaran yang terjadi lima menit yang lalu dan tidak lagi diperlukan.

Untuk memahami masalah lain, mari kita lihat adaptasi RTMP. Kami melakukan adaptasi di sisi pengirim. Jika jaringan tidak dapat menjejalkan data dengan kecepatan yang dimasukkan ke dalam soket, buffer diisi dan soket mengatakan EWOULDBLOCK atau diblokir jika pemblokiran digunakan pada saat ini.



Hanya pada saat ini kita mengerti bahwa kita memiliki masalah, dan kita perlu mengurangi kualitasnya.

Katakanlah kita memiliki jaringan dengan kecepatan spesifik 4 Mbps. Kami memilih ukuran soket 250 KB (sesuai dengan 0,5 detik pada kecepatan kami). Tiba-tiba, jaringan gagal 10 kali - ini adalah situasi yang normal. Kami memiliki 400 kbps. Buffer cepat terisi dalam setengah detik, dan hanya pada saat itu kita mengerti bahwa kita perlu beralih.



Tapi sekarang masalahnya adalah kita memiliki buffer 250 KB yang akan dikirim selama 5 detik. Kita sudah benar-benar ketinggalan: kita perlu mendorong data lama terlebih dahulu, dan baru kemudian data baru dan yang diadaptasi akan menyusulnya secara realtime.

Apa yang harus dilakukan Di sini "segitiga kompromi" kami hanya relevan.



  • Kita dapat mengurangi buffer pengirim, alih-alih 0,5 detik - 0,1 detik. Tapi kita kehilangan bandwidth, karena kita sering "panik" dan beralih. Selain itu, TCP berfungsi sedemikian rupa sehingga jika Anda menempatkan buffer pengirim lebih kecil dari RTT, Anda tidak dapat menggunakan bandwidth penuh saluran, itu akan berkurang beberapa kali.
  • Kami dapat meningkatkan buffer penerima. Dengan buffer besar, data tiba, kami dapat memuluskan beberapa penyimpangan dalam buffer. Tetapi, tentu saja, kami kehilangan latensi rendah, karena kami segera menyiapkan buffer 5 detik.
  • Kami dapat secara agresif menjatuhkan data lama. Dalam TCP, satu-satunya pilihan untuk ini adalah memutuskan koneksi dan membuatnya kembali. Kami kehilangan keandalan, karena saat ini pemain tidak memiliki apa-apa untuk ditampilkan.


WebRTC


Ini adalah pustaka C ++ yang sudah memperhitungkan pengalaman akun dan berjalan di atas UDP. Dibangun di bawah iOS, Android, dibangun di browser, mendukung HTML5. Karena dipenjara untuk panggilan P2P, penundaannya adalah 0,1-1 detik.



Dari minus: ini adalah perpustakaan monolitik dengan banyak warisan yang tidak dapat dihapus. Selain itu, karena fokusnya pada panggilan P2P, ia memprioritaskan latensi rendah. Tampaknya kami menginginkan ini, tetapi demi ini, ia mengorbankan parameter lain. Dan tidak ada pengaturan untuk mengubah prioritas.

Juga harus diingat bahwa perpustakaan berorientasi klien untuk percakapan antara dua klien tanpa server. Server harus dicari pihak ketiga, atau tulis milik Anda sendiri.

Apa yang harus dipilih - RTMP atau WebRTC? Kami menerapkan kedua protokol dan mengujinya dalam skenario yang berbeda. Pada grafik, WebRTC memiliki delay yang rendah, tetapi throughput yang rendah, sedangkan RTMP memiliki yang sebaliknya. Dan di antara mereka ada lubang.

Dan kami ingin membuat protokol yang sepenuhnya menutupi lubang ini dan dapat berfungsi baik dalam mode WebRTC dan dalam mode RTMP. Mereka membuat dan menamakannya OKMP.


Okmp


Ini adalah protokol yang fleksibel untuk UDP.

Mendukung multiplexing. Apa artinya ini: ada beberapa saluran di dalam sesi (dalam kasus OK Live - manajer, audio dan video). Di dalam setiap saluran, data dijamin untuk dikirimkan dalam urutan tertentu (tetapi mereka sendiri tidak dijamin untuk dikirim), dan urutan antara saluran tidak dijamin, karena itu tidak penting.

Apa yang diberikannya? Pertama, ini memberi kami kesempatan untuk memprioritaskan saluran. Kita dapat mengatakan bahwa saluran kontrol memiliki prioritas tinggi, suara sedang, dan videonya rendah. Jitter video dan pengiriman video yang tidak rata lebih mudah disamarkan, dan pengguna memiliki lebih sedikit masalah dari masalah video daripada dari gagap audio yang tidak menyenangkan.



Selain itu, protokol kami memiliki jaminan pengiriman opsional. Kami dapat mengatakan bahwa pada saluran tertentu kami bekerja dalam mode TCP, dengan pengiriman yang terjamin, dan sisanya kami mengizinkan beberapa tetes.

Berkat ini, jaminan keterlambatan juga dapat dibuat: tidak ada jaminan keterlambatan pada saluran TCP, tetapi pada yang lain di mana tetes diizinkan, ambang batas ditetapkan, setelah itu data mulai turun dan kami berhenti mengirimkan data lama.

Misalnya, untuk audio, ini adalah 1 detik, dan untuk video, 0,5 detik. Mengapa ambang berbeda? Ini adalah mekanisme prioritas lain. Karena lebih penting bagi kami bahwa audionya halus, kami mulai menjatuhkan video terlebih dahulu.

Protokol kami dikonfigurasi secara fleksibel: tidak ada mode operasi tunggal, kami mengubah pengaturan dengan cepat untuk beralih ke mode yang diinginkan tanpa efek yang terlihat bagi pengguna. Mengapa Misalnya, untuk panggilan video yang sama: jika panggilan video dimulai dalam aliran, kami dengan tenang mentransfernya ke mode latensi rendah. Dan kemudian kembali ke mode throughput untuk kualitas maksimal.
Kesulitan implementasi



Tentu saja, jika Anda memutuskan untuk menulis protokol Anda di UDP, Anda akan menemukan beberapa masalah. Menggunakan TCP, kami mendapatkan mekanisme yang harus kami tulis sendiri di UDP:

  • Paket / Depacketizing. Anda perlu memotong data menjadi paket-paket berukuran sekitar 1,5 KB agar sesuai dengan jaringan MTU.
  • Penataan ulang. Anda mengirim paket dalam satu urutan, dan mereka diatur ulang di jalan dan datang di lain. Untuk mengatasinya, Anda perlu mengatur urutan dengan nomor paket, dan mengatur ulang pada penerima.
  • Kerugian. Tentu saja ada kerugian. Ketika terjadi kerugian, penerima harus memberi tahu pengirim secara terpisah bahwa "Saya menerima paket-paket ini, tetapi tidak menerimanya", dan pengirim harus mengirimkan kembali paket-paket yang hilang. Atau jatuhkan mereka.
  • Kontrol aliran Jika Receiver tidak menerima data, tidak mengikuti kecepatan kita mendorongnya, data mungkin mulai hilang, kita harus memproses situasi ini. Dalam kasus TCP, soket pengiriman akan diblokir, dan dalam kasus UDP tidak akan diblokir, Anda harus memahami diri sendiri bahwa penerima tidak menerima data, dan mengurangi jumlah data yang dikirim.
  • Kontrol Kemacetan. Hal serupa, hanya dalam kasus ini jaringan mati. Jika kita mengirim paket ke jaringan yang sudah mati, kita tidak hanya akan menghancurkan koneksi kita, tetapi juga yang tetangga.
  • Enkripsi Perlu menjaga enkripsi
  • ... dan banyak lagi


OKMP vs RTMP


Apa yang kita dapatkan ketika kita mulai menggunakan OKMP, bukan RTMP?

  • Rata-rata peningkatan bitrate OKLive adalah 30%.
  • Jitter (ukuran kedatangan paket tidak merata) - 0% (rata-rata sama).
  • Audio Jitter - -25%
  • Video Jitter - 40%


Perubahan audio dan video - demonstrasi prioritas dalam protokol kami. Audio kami berikan prioritas yang lebih tinggi, dan itu mulai datang lebih lancar karena video.

Cara memilih protokol untuk streaming





Jika Anda memerlukan latensi rendah - WebRTC.

Jika Anda ingin bekerja dengan layanan eksternal, mempublikasikan video pada layanan pihak ketiga, Anda harus menggunakan RTMP.

Jika Anda ingin protokol yang dirancang untuk skrip Anda - implementasikan sendiri.

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


All Articles