Mírame completo: aprovecha al máximo el video en vivo en plataformas móviles



La forma más fácil de reproducir video en un dispositivo móvil es abrir el enlace con un reproductor existente en el sistema, pero esto no siempre es efectivo.

Puede tomar ExoPlayer y optimizarlo, o incluso puede escribir su propio reproductor de video usando solo códecs y tomas. El artículo hablará sobre el trabajo de transmisión y reproducción de video, y cómo reducir el retraso en el inicio del video, reducir el tiempo de respuesta entre el transmisor y el espectador, y optimizar el consumo de energía y la carga de hierro.

Analizaremos esto usando aplicaciones específicas como ejemplo: el cliente móvil Odnoklassniki (donde se reproducen videos) y OK Live (donde las transmisiones se transmiten desde el teléfono a 1080p). No habrá clases magistrales sobre cómo reproducir un video por referencia, con ejemplos de código. La historia se centrará en cómo se ve el video desde adentro y cómo, conociendo la arquitectura general de los reproductores de video y la transmisión de video, puede comprender cualquier sistema y mejorarlo.

El material se basa en la transcripción del informe de Alexander Tobol ( @alatobol ) e Ivan Grigoriev ( @ivan_a ) de la conferencia de Mobius .




Entrada


Para empezar, algunos números sobre el video en Odnoklassniki.

El pico de tráfico diario promedio de VOD (video a pedido) es de más de medio terabits por segundo, y para transmisiones en vivo, más de 3 terabits por segundo.

Ahora en OK hay más de 870 millones de vistas de video por día, más de la mitad de las cuales son de dispositivos móviles.



Si nos fijamos en el historial de transmisión, apareció un video móvil en YouTube en 2007. Nos subimos a este tren más tarde, pero en 2014-2015 ya teníamos reproducción de video 4K en dispositivos móviles, y en los últimos años hemos estado desarrollando activamente nuestros reproductores. Sobre esto y la conversación irá.

La segunda tendencia que surgió con Periscope en 2015 fue la transmisión desde teléfonos. Lanzamos nuestra aplicación OK Live, que le permite transmitir incluso video Full HD a través de redes móviles. En la segunda mitad del material, también hablaremos sobre la transmisión.

No nos detendremos en la API para trabajar con video, sino que ahora nos sumergiremos profundamente e intentaremos descubrir qué está sucediendo dentro.



Cuando graba un video en una cámara, llega al códec, desde allí al socket, luego al servidor (independientemente de si es VOD o Live). Y luego el servidor en el orden inverso lo distribuye a la audiencia.

Comencemos con el reproductor KPI. ¿Qué queremos de él?

  • Primer fotograma rápido. Los usuarios no quieren esperar al inicio de la reproducción.
  • Falta de amortiguación. A nadie le gusta toparse con un torso.
  • Alta calidad Cuando casi no había contenido 4K todavía, ya hicimos soporte 4K para "crecimiento": si pospones el reproductor y descubres el rendimiento, 1080p se reproducirá perfectamente incluso en dispositivos débiles.
  • Requisitos de experiencia de usuario. Necesitamos que el video se reproduzca en la cinta mientras se desplaza, y para la cinta necesitamos capturar previamente el video.


Hay muchos problemas de esta manera. La transmisión de video 4K es grande, y trabajamos en dispositivos móviles donde hay problemas de red, hay varias características de formatos de video y contenedores en diferentes dispositivos, y los dispositivos en sí también pueden convertirse en un problema.

¿Dónde crees que el video comienza más rápido, en iOS o Android?

De hecho, cualquier respuesta es correcta: depende de qué, dónde y cómo jugar. Si tomamos una región de Rusia con una red no tan buena, veremos que AVPlayer comienza en aproximadamente 800 milisegundos. Pero con la misma red, ExoPlayer en Android, con un formato diferente, lo lanzará en 660 ms. Y si crea su reproductor en iOS, podrá ejecutarlo aún más rápido.



Hay un matiz en que medimos el promedio para los usuarios, y la potencia promedio de los dispositivos iOS es mayor que en Android.

La primera parte del material será teórica: aprenderemos qué es el video y cómo se ve la arquitectura de cualquier reproductor en vivo. Y en la segunda parte, comparemos los jugadores y hablemos sobre cuándo escribir el suyo.

Primera parte


¿Qué es el video?


Comencemos con lo más básico. El video es de 60 o 24 imágenes por segundo.

Obviamente, almacenar esto con un conjunto completo de imágenes es bastante costoso. Por lo tanto, se almacenan de esta manera: algunos cuadros se denominan cuadros de referencia (cuadros I), mientras que otros (cuadros B y cuadros P) se denominan "diffs". De hecho, tiene un archivo jpg y un conjunto específico de cambios.



También existe el concepto de GOP (grupo de imágenes): este es un conjunto independiente de marcos, que comienza con un marco de referencia y continúa con un conjunto de diferencias. Se puede jugar de forma independiente, desempaquetado, etc. Al mismo tiempo, si perdió un opornik en el grupo, los fotogramas restantes ya no son relevantes.

Hay muchos algoritmos de codificación, matrices de transformación, búsqueda de movimiento y similares, en esto es en lo que los códecs difieren.

Rendimiento del códec





El clásico H.264 se conoce desde 2003 y se ha desarrollado bien. Tomaremos su efectividad como base. Él trabaja y juega en todas partes. Tiene soporte de hardware para CPU / GPU (ambos en iOS, en Android). Esto significa que hay algún tipo de coprocesador especial que puede codificarlo, o conjuntos de instrucciones incorporados que le permiten hacer esto rápidamente. En promedio, el soporte de hardware ofrece un rendimiento hasta 10 veces más rápido y ahorra batería.

En 2010, apareció VP8 de Google. En términos de eficiencia, no difiere de H.264. Bueno, en realidad la efectividad del códec es algo muy controvertido. En la frente, se mide como la relación del video original al comprimido, pero está claro que hay diferentes artefactos de video. Por lo tanto, proporcionamos un enlace a comparaciones detalladas de códecs de la Universidad Estatal de Moscú. Pero aquí nos restringimos al hecho de que VP8 se centra en una organización de software, puede arrastrarlo a cualquier parte y, por lo general, se usa como respaldo si no hay soporte H.264 nativo.

En 2013, apareció una nueva generación de códecs: H.265 (HEVC) y VP9. El códec H.265 proporciona un aumento en la eficiencia del 50%, pero en el video de Android no se pueden codificar, el decodificador apareció solo con Android 5.0+. Pero en iOS hay soporte.

Hay una alternativa a H.265 - VP9. De todos modos, pero con el apoyo de Google. Bueno, V9 es YouTube, y H.265 es Netflix. Por lo tanto, cada uno tiene sus propias peculiaridades: uno no funcionará en iOS, el otro tendrá problemas en Android. Al final, muchos permanecen en H.264.

En el futuro, se nos prometió el códec AV1, ya tiene una implementación de software y su eficiencia es un 35% mayor que la de los códecs de 2013. Ahora disponible en Chrome y Firefox, y en 2020 Google promete soporte de hardware. Creo que, muy probablemente, todos nos trasladaremos a él.

Finalmente, recientemente anunciaron el códec H.266 / JVEC, diciendo que todo será mejor y más rápido.

El patrón principal: cuanto mayor es la eficiencia del códec, más recursos informáticos requiere de los dispositivos.

En general, por defecto, todos toman H.264, y luego para dispositivos específicos puede ser complicado.

Calidad, resolución y bitrate


En 2019, no sorprenderá a nadie con calidad adaptativa: los usuarios cargan o transmiten videos en una calidad, y cortamos una línea de diferentes calidades y enviamos las más adecuadas para los dispositivos.

En este caso, es necesario que la resolución del video se correlacione con la tasa de bits. Si la resolución se duplica, la velocidad de bits también debería duplicarse:



Obviamente, si comprime una gran resolución con una velocidad de bits baja o viceversa, habrá artefactos o una quema inútil de la velocidad de bits.

¿Cómo se compara la tasa de bits del video codificado con la cantidad original de información? En una pantalla de 4K, podemos reproducir casi 6 Gb / s de información (si cuenta todos los píxeles y su frecuencia a 60 cuadros por segundo), mientras que la velocidad de bits del códec puede ser de 50 Mb / s. Es decir, el códec comprime el video hasta 100 veces.

Tecnología de entrega


Tienes audio y video con algunos códecs. Si solo lo guarda en casa, puede agregar todo el audio y el video agregando un pequeño índice que le indica desde qué segundo comienza el audio y el video. Pero el video no se puede entregar al teléfono, y para transmitir al espectador en línea, hay dos clases principales de protocolos: transmisión y segmento.



El protocolo de transmisión implica que tiene algún tipo de estado en el servidor, el cliente también, y envía datos. El servidor puede ajustar, por ejemplo, la calidad. Muy a menudo esta es una conexión UDP.

Tales protocolos son altamente complejos para el servidor y difíciles de entregar. Para traducciones muy cargadas, usamos protocolos segmentados que funcionan sobre HTTP, pueden ser almacenados en caché por nginx y CDN, y son mucho más fáciles de distribuir. Y el servidor no es responsable de nada y, en este caso, sin estado.

Cómo se ve la entrega de segmentos: cortamos el video existente en segmentos, los acompañamos con un encabezado para audio y video, MPEG-TS y MP4 como ejemplo de transporte. En el teléfono damos un manifiesto con información sobre dónde y para qué calidad se encuentra el segmento, y este manifiesto se puede actualizar periódicamente.

Históricamente, Apple entrega a través de HLS y Android a través de DASH. Veamos en qué se diferencian.

Comencemos con el HLS anterior, tiene un manifiesto que describe todas las cualidades disponibles: baja, media, alta, etc. Existen tasas de bits de estas cualidades para que el jugador pueda elegir inmediatamente la correcta. Elige la calidad y obtiene un manifiesto anidado con una lista de enlaces a segmentos. También se indica la duración de estos segmentos.



Aquí hay una característica interesante: para comenzar a jugar el primer cuadro, tendrás que hacer dos viajes de ida y vuelta adicionales. La primera solicitud obtiene el manifiesto principal, el segundo manifiesto anidado, y solo entonces accede a los datos en sí, lo que no es muy bueno.



La segunda dificultad: HLS fue diseñado para trabajar en Internet a través de HTTP, pero el flujo de transporte MPEG-2 heredado fue elegido como contenedor para datos de video, que fue desarrollado para propósitos completamente diferentes: transmitir una señal de un satélite en canales ruidosos. Como resultado, obtenemos encabezados adicionales, que en el caso de HLS son completamente inútiles y solo agregan sobrecarga.



Agregue gastos generales de red y la complejidad del análisis: si intenta reproducir 4K en DASH y HLS en Chrome, sentirá la diferencia cuando su computadora "despegue" con los paquetes HLS.

Apple está tratando de resolver esto. En 2016, anunciaron la posibilidad de usar MPEG-4 fragmentado, había cierto soporte para DASH en HLS, pero el RTT adicional y sus características no desaparecieron.



DASH parece un poco más simple: tiene un manifiesto con todas las cualidades en su interior, y cada calidad es un conjunto de segmentos. Puede jugar un segmento para jugar en una calidad, luego comprender que la velocidad ha aumentado, del siguiente segmento para cambiar a otro. Todos los segmentos siempre comienzan con marcos de referencia, lo que permite el cambio.

Aquí hay un pequeño plato sobre qué elegir:



En HLS, los códecs de video compatibles históricamente son solo H.264, en MPEG-DASH puedes empujar a cualquiera. El principal problema de HLS es un viaje de ida y vuelta adicional al principio, funciona bien tanto en iOS como en Android con 4.0. Y DASH es compatible principalmente con Google (Chrome y Android) y no se puede reproducir en iOS.

Arquitectura del jugador


Resolvimos el video más o menos, ahora veamos cómo se ve cualquier jugador.



Comencemos con la parte de la red: al iniciar un video, el reproductor sigue el manifiesto, de alguna manera selecciona la calidad, luego sigue el segmento, lo descarga, luego necesita decodificar los cuadros, comprende que hay suficientes cuadros en el búfer para la reproducción y luego comienza la reproducción.

La arquitectura general del jugador:



Hay una parte de la red, un socket, de donde provienen los datos.

Después de eso, un demultiplexor o algún tipo de cosa que obtiene transmisiones de audio y video de un transporte (HLS / DASH). Ella los envía a los códecs apropiados.

Los códecs decodifican video y audio, y luego sucede lo más interesante: deben sincronizarse para que su video y audio se reproduzcan simultáneamente. Hay varios mecanismos basados ​​en marcas de tiempo para esto.

Luego debe renderizarlo en algún lugar: en Texture, Surface, GL o Metal, en cualquier lugar.

Y en la entrada hay un control de carga, que carga los datos y controla el búfer.

¿Cómo se ve el control de carga en todos los jugadores? Hay cierta cantidad de datos que deben descargarse. El jugador espera hasta que se descargan, luego comienza a jugar, y lo descargamos más. Tenemos el límite máximo de búfer, al llegar al cual se detiene la descarga. Después de eso, durante la reproducción, la cantidad de datos en el búfer cae, y hay un borde mínimo en el que comienza a cargarse. Entonces todo esto también vive:



¿Cómo se ve el hilo del bucle principal? Los jugadores están familiarizados con el concepto de "hilo de tic", parece estar aquí. Hay una parte responsable de la red que apila todo en un búfer. Hay un extractor que lo desempaqueta y lo envía a los códecs, donde se almacena su búfer intermedio y luego se procesará. Y tiene una marca que los cambia y los controla, se ocupa de la sincronización.



En el exterior, tiene una aplicación que envía algunos comandos a través de una cola de mensajes y recibe información a través de oyentes. Y a veces puede aparecer una contrapresión, lo que reduce la calidad, por ejemplo, en una situación en la que se agota el búfer o el renderizado no puede hacer frente (por ejemplo, aparecen cuadros de caída).

Estimador


Al adaptarse, el reproductor se basa en 2 parámetros principales: velocidad de red y almacenamiento intermedio de datos.

Cómo se ve: primero, se reproduce una cierta calidad, por ejemplo, 720p. Tienes un búfer en crecimiento, almacenando cada vez más en caché Luego, la velocidad aumenta, usted comprende que puede descargar aún más, el búfer crece. Y en este momento comprende que está pisando algunos límites del búfer mínimo cuando puede probar la siguiente calidad.



Está claro que debe probarlo con cuidado: también hay un estimador que le dice si puede cumplir con esta calidad en términos de velocidad de red. Si encaja en esta evaluación y el stock de búfer lo permite, entonces cambia, por ejemplo, a 1080p y continúa jugando.

Protección contra sobrepresión


Con nosotros, ella apareció con el tiempo a través de prueba y error. La necesidad surge cuando sobrecarga ligeramente su equipo.

Hay una situación en la que la red se apaga durante la reproducción o los recursos se agotan en el back-end. Cuando el reproductor reanuda la reproducción, comienza a ponerse al día.

En este momento se ha acumulado una gran cantidad de segmentos en el manifiesto del jugador, los descarga rápidamente todos a la vez y obtenemos un "golpe de tráfico". La situación puede agravarse si se produce un tiempo de espera en los clientes y el jugador comienza a volver a consultar los datos. Por lo tanto, es imperativo proporcionar contrapresión en el sistema.

La primera forma simple que, por supuesto, utilizamos es el estrangulador en el servidor. Él entiende que el tráfico termina, reduce la calidad y ralentiza deliberadamente a los clientes para no recibir ese golpe.



Pero esto no afecta muy bien a los estimadores. Pueden generar los mismos "giros". Por lo tanto, si es posible, apoye la eliminación de la calidad del manifiesto. Para hacer esto, debe actualizar periódicamente el manifiesto o, si hay un canal de retroalimentación, dar el comando para eliminar la calidad, y el reproductor cambiará automáticamente a otro, más bajo.

Jugadores


En iOS, solo hay AVPlayer nativo, pero en Android hay una opción. Hay un MediaPlayer nativo, pero hay un ExoPlayer basado en Java de código abierto que las aplicaciones "traen con ellos". ¿Cuáles son sus pros y sus contras?

Compara los tres:



En el caso de la transmisión adaptativa, ExoPlayer reproduce DASH / HLS y tiene muchos módulos expandibles para otros protocolos, mientras que AVPlayer está empeorando.

La compatibilidad con las versiones del sistema operativo, en principio, se adapta a todos en todas partes.

La captación previa es cuando sabes que después del final de un video quieres reproducir lo siguiente en la cinta y precargarlo.

Hay un problema con las correcciones de errores de los jugadores nativos. En el caso de ExoPlayer, simplemente lo incluye en una nueva versión de su aplicación, pero en AVPlayer y MediaPlayer nativos, el error se solucionará solo en la próxima versión del sistema operativo. Nos encontramos con esto dolorosamente: en iOS 8.01 nuestro video comenzó a reproducirse mal, en iOS 8.02 todo el portal dejó de funcionar, en 8.03 todo volvió a funcionar. Y nada dependía de nosotros en este caso, simplemente nos sentamos y esperamos a que Apple lanzara la próxima versión.

El equipo de ExoPlayer habla sobre la ineficiencia del consumo de energía en el caso del audio. Hay recomendaciones generales de Google: para reproducir audio, use MediaPlayer, para todo lo demás Exo.

Entendido, usaremos ExoPLayer con DASH para video en Android y AVPlayer con HLS en iOS.

Primer fotograma rápido


Nuevamente, recuerde el tiempo hasta el primer fotograma. Cómo se ve en iOS HLS: primero RTT detrás del manifiesto, luego otro RTT detrás del manifiesto anidado, solo entonces, obteniendo el segmento y jugando. En Android, un RTT es menos, comienza un poco mejor.



Tampón


Ahora tratemos con los tampones. Tenemos una cantidad mínima de datos que deben descargarse antes de comenzar a jugar. En AVPlayer, este valor se configura utilizando AVPlayerItem preferredForwardBufferDuration.



En Android, ExoPlayer tiene muchos más mecanismos de configuración. Hay el mismo búfer mínimo que se necesita para comenzar. Pero también hay una configuración separada para rechazar (si su red se cayó, los datos del búfer se agotaron y luego regresaron):



¿Cuál es el beneficio? Si tienes una buena red, comienzas rápidamente y luchas por un primer fotograma rápido, por primera vez puedes intentar arriesgarte. Pero si la red se rompe durante la reproducción, es obvio que necesita solicitar más almacenamiento en búfer para reproducir durante el rechazo para que no haya problemas repetidos.

Calidad original





HLS en iOS tiene un problema genial: siempre comienza a reproducirse desde la primera calidad en el manifiesto m3u8. Lo que le devuelvas comenzará. Y solo entonces medirá la velocidad de descarga y comenzará a jugar en calidad normal. Está claro que esto no debería permitirse.

Optimización lógica: reordena la calidad. Ya sea en el servidor (al agregar un parámetro adicional a la calidad preferida, vuelve a ordenar el manifiesto) o en el cliente (cree un proxy que lo haga por usted).

Y en Android, hay un parámetro DefaultBandwidthMeter para esto. Le da un valor que considera el ancho de banda predeterminado de su banda.



: , — () (wi-fi, 2G, 3G, 4G). ? , Wi-Fi , initial bandwidth — 5,6 . 3G — 700 .

, Google 4G 2-3 , .

, — , . , , , , .

, , , , . , ( 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-, , .

?

  • Inicio rápido


?

  • bandwidth
  • (N x RTT, RTT)






— . , , .

: , , . — low latency.

, — . .

— 4K. , , . , 30 , . , .


, , , . ( 100 ).

- , , .

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

:



, , . , - , , .

MediaCodec VideoToolbox ( ). Server Transcoder.

— , .


, . , , reliability — ( ), throughput — ( ) low latency — ( ).



, . , - .


, : RTMP WebRTC — , OKMP — .

, RTMP TCP, — UDP.

RTMP


¿Qué da él? De cierta manera, este es un estándar que es compatible con todos los servicios: YouTube, Twitch, Flash, OK. Lo usan para que los usuarios puedan subir transmisiones en vivo. Si desea transmitir una transmisión en vivo a un servicio de terceros, lo más probable es que tenga que trabajar con RTMP.

El retraso mínimo que logramos lograr de una unidad de cinta a un reproductor es de 300 ms, pero esto se encuentra en una red ideal cuando hace buen tiempo. Cuando tenemos una red real, el retraso generalmente crece a 2-3 segundos, y si todo está mal con la red, puede crecer a decenas de segundos.

RTMP admite cambios de resolución y velocidad de bits sobre la marcha (los otros protocolos mencionados son los mismos, pero hay información errónea sobre RTMP de que no hay cambios sobre la marcha).

De los inconvenientes: construido en TCP (explicaremos más adelante por qué esto es malo), la demora no está controlada.

Si nos fijamos en el triángulo, RTMP no podrá dar baja latencia. Se puede obtener, pero no se garantiza en absoluto.

Además, RTMP es un poco basura: no admite nuevos códecs, ya que Adobe no lo hace, y la documentación es bastante antigua y torcida.



¿Por qué TCP no es adecuado para transmisiones en vivo? TCP ofrece una garantía de entrega: los datos que ingrese en el socket se entregarán exactamente en el orden y en la forma en que los puso allí. Nada se dejará caer ni se reorganizará. TCP hará esto o morirá. Pero esto significa que se excluye una garantía de demora: no podrá eliminar los datos antiguos, que tal vez ya no tengan que enviarse. El búfer, los atrasos, etc. comienza a crecer.



Como ilustración, el problema de bloqueo de Head of Line. Se encuentra no solo en la transmisión, sino también en muchos otros casos.

Que es esto Tenemos un búfer receptor inicialmente vacío. Estamos recibiendo datos de algún lugar: muchos datos y muchos paquetes IP. Recibimos el primer paquete IP, y en el receptor usando el método recv () podemos restar este paquete, obtener datos, perder, renderizar. Pero luego, de repente, el segundo paquete se perdió. ¿Qué pasa después?

Para recuperar un paquete IP perdido, TCP debe retransmitir. Para que esto suceda, debe gastar RTT, mientras que la retransmisión también se puede perder, e iremos en ciclos. Si hay muchos paquetes, esto definitivamente sucederá.

Después de esto viene una gran cantidad de datos que no podemos leer, porque estamos de pie y esperando el segundo paquete. Aunque mostró un cuadro de transmisión que sucedió hace cinco minutos y ya no es necesario.

Para entender otro problema, veamos la adaptación RTMP. Hacemos la adaptación en el lado del remitente. Si la red no puede acumular datos a la velocidad con la que se colocan en el zócalo, el búfer se llena y el zócalo dice EWOULDBLOCK o se bloquea si se usa el bloqueo en este momento.



Solo en este momento entendemos que tenemos problemas y necesitamos reducir la calidad.

Digamos que tenemos una red con una velocidad específica de 4 Mbps. Elegimos un tamaño de socket de 250 KB (correspondiente a 0,5 segundos a nuestra velocidad). De repente, la red falló 10 veces, esta es una situación normal. Tenemos 400 kbps. El búfer se llenó rápidamente en medio segundo, y solo en ese momento entendemos que necesitamos apagarlo.



Pero ahora el problema es que tenemos un búfer de 250 KB que se transmitirá durante 5 segundos. Ya estamos completamente atrasados: primero debemos introducir los datos antiguos, y solo entonces los nuevos y adaptados se pondrán al día en tiempo real.

Que hacer Aquí nuestro "triángulo de compromisos" es simplemente relevante.



  • Podemos reducir el búfer del emisor, poner en lugar de 0.5 segundos - 0.1 segundos. Pero estamos perdiendo ancho de banda, ya que a menudo "entraremos en pánico" y cambiaremos. Además, TCP funciona de tal manera que si coloca un búfer emisor más pequeño que RTT, no puede usar el ancho de banda completo del canal, disminuirá varias veces.
  • Podemos aumentar el búfer del receptor. Con un búfer grande, llegan los datos, podemos suavizar algunas irregularidades dentro del búfer. Pero, por supuesto, estamos perdiendo baja latencia, ya que configuramos inmediatamente un búfer de 5 segundos.
  • Podemos soltar agresivamente los datos antiguos. En TCP, la única opción para esto es romper la conexión y volver a crearla. Perdemos fiabilidad, porque en este momento el jugador no tiene nada que mostrar.


WebRTC


Esta es una biblioteca C ++ que ya tiene en cuenta la experiencia y se ejecuta sobre UDP. Construye bajo iOS, Android, está integrado en los navegadores, es compatible con HTML5. Como está preso para llamadas P2P, el retraso es de 0.1-1 segundos.



De las desventajas: esta es una biblioteca monolítica con una gran cantidad de legado que no se puede eliminar. Además, debido a su enfoque en llamadas P2P, prioriza la baja latencia. Parece que queríamos esto, pero por el bien de ella, ella sacrifica otros parámetros. Y no hay configuraciones para cambiar las prioridades.

También debe tenerse en cuenta que la biblioteca está orientada al cliente para una conversación entre dos clientes sin un servidor. El servidor debe ser buscado por un tercero, o escribir el suyo propio.

¿Qué elegir: RTMP o WebRTC? Implementamos ambos protocolos y los probamos en diferentes escenarios. En el gráfico, WebRTC tiene un bajo retraso, pero un bajo rendimiento, mientras que RTMP tiene lo contrario. Y entre ellos hay un agujero.

Y queríamos hacer un protocolo que cubriera completamente este agujero y pueda funcionar tanto en WebRTC como en modo RTMP. Lo hicieron y lo llamaron OKMP.


Okmp


Este es un protocolo flexible para UDP.

Soporta multiplexación. ¿Qué significa esto? Hay varios canales dentro de la sesión (en el caso de OK Live: el administrador, el audio y el video). Dentro de cada canal, se garantiza que los datos se entregarán en un cierto orden (pero no se garantiza que se entreguen), y entre los canales el pedido no está garantizado, ya que no es importante.

Que da En primer lugar, nos dio la oportunidad de priorizar canales. Podemos decir que el canal de control tiene alta prioridad, el sonido es medio y el video es bajo. La fluctuación de fase de video y la entrega desigual de video es más fácil de disfrazar, y el usuario tiene menos problemas por problemas de video que por la tartamudez desagradable del audio.



Además, nuestro protocolo tiene una garantía de entrega opcional. Podemos decir que en cierto canal trabajamos en modo TCP, con entrega garantizada, y en el resto permitimos algunas caídas.

Gracias a esto, también se puede hacer una garantía de retraso: no hay garantía de retraso en el canal TCP, pero en los otros donde se permiten caídas, se establece un umbral, después del cual los datos comienzan a caer y dejamos de entregar datos antiguos.

Por ejemplo, para audio, esto es 1 segundo, y para video, 0.5 segundos. ¿Por qué es diferente el umbral? Este es otro mecanismo de priorización. Dado que es más importante para nosotros que el audio sea fluido, primero comenzamos a soltar el video.

Nuestro protocolo está configurado de manera flexible: no hay un modo operativo único, cambiamos la configuración sobre la marcha para cambiar al modo deseado sin efectos visibles para el usuario. Por qué Por ejemplo, para las mismas videollamadas: si una videollamada comienza en una transmisión, la transferimos silenciosamente al modo de baja latencia. Y luego de vuelta al modo de rendimiento para obtener la máxima calidad.
Dificultades de implementación



Por supuesto, si decide escribir su protocolo en UDP, se encontrará con algunos problemas. Usando TCP, obtenemos mecanismos que tendremos que escribir en UDP nosotros mismos:

  • Empaquetado / desempaquetado. Debe cortar los datos en paquetes de aproximadamente 1,5 KB de tamaño para que quepan en la red MTU.
  • Reordenamiento Envías paquetes en un orden, y se reorganizan en el camino y vienen en otro. Para superar esto, debe establecer la secuencia con el número de paquete y reorganizarlos en el receptor.
  • Pérdidas Por supuesto, hay pérdidas. Cuando ocurre una pérdida, el receptor debe decirle al remitente por separado que "recibí estos paquetes, pero no los recibí", y el remitente debe retransmitir los paquetes faltantes. O dejarlos caer.
  • Control de flujo Si el receptor no recibe datos, no se mantiene al día con la velocidad con la que lo empujamos, los datos pueden comenzar a perderse, debemos procesar esta situación. En el caso de TCP, el socket de envío se bloqueará, y en el caso de UDP no se bloqueará, debe comprender que el receptor no está recibiendo datos y reducir la cantidad de datos enviados.
  • Control de congestión. Algo similar, solo en este caso la red murió. Si enviamos paquetes a la red fallecida, destruiremos no solo nuestra conexión, sino también las vecinas.
  • Cifrado Necesito cuidar el cifrado
  • ... y mucho más


OKMP vs RTMP


¿Qué obtuvimos cuando comenzamos a usar OKMP en lugar de RTMP?

  • El aumento promedio en la tasa de bits OKLive es del 30%.
  • Jitter (medida de llegada desigual de paquetes) - 0% (en promedio lo mismo).
  • Jitter Audio - -25%
  • Video Jitter - 40%


Cambios en audio y video: demostración de prioridades en nuestro protocolo. Al audio le damos una mayor prioridad, y comenzó a ser más fluido debido al video.

Cómo elegir un protocolo para la transmisión





Si necesita baja latencia - WebRTC.

Si desea trabajar con servicios externos, publicar videos en servicios de terceros, deberá usar RTMP.

Si desea un protocolo personalizado para sus scripts, implemente el suyo.

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


All Articles