reproducir - insertar video en html5
HTML5<audio>/<video> y transcodificación en vivo con FFMPEG (4)
AFAIK puedes codificar a stdout en ffmpeg. Así que puedes configurar tu servidor HTTP para:
- iniciar la codificación en la memoria caché cuando se recibe GET.
- Transmitir el rango solicitado de bytes al cliente.
- Llenando el buffer y usándolo para rangos posteriores.
No tengo ni idea, pero creo que puedes escaparte sin saber la longitud del flujo final.
En una nota al margen, creo que esto es propenso a DoS.
Entonces, desde mi servidor web, me gustaría usar FFMPEG para transcodificar un archivo multimedia para usarlo con una etiqueta HTML <audio>
o <video>
. Bastante fácil, ¿verdad?
La conversión debería realizarse en tiempo real, cuando un cliente HTTP solicitó el archivo convertido. Lo ideal sería que el archivo se retransmitiera al cliente HTTP a medida que se transcodifica (y no después al final, ya que potencialmente tomaría un tiempo antes de que los datos comiencen a ser devueltos).
Esto estaría bien, excepto que en los navegadores de hoy, una etiqueta de audio o video HTML5 solicita el archivo de medios en varias solicitudes HTTP con el encabezado de Range
. Vea esta pregunta para más detalles .
En esa pregunta vinculada arriba, puedes ver que Safari solicita fragmentos extraños del archivo, incluidos los pocos bytes finales. Esto plantea un problema en el sentido de que el servidor web DEBERÍA esperar a que finalice la conversión para entregar los bytes finales del archivo para cumplir con la solicitud de Range
.
Así que mi pregunta es, ¿es correcto mi pensamiento? ¿Hay una mejor manera de entregar contenido de transcodificación a una etiqueta <audio>
o <video>
que no implique esperar a que termine la conversión completa? ¡Gracias por adelantado!
Esto debería ser factible a través de VLC . Pude hacerlo funcionar configurando VLC para que aloje un gran archivo avi y lo transcodifique a OGG, luego mi html5 hizo referencia a la transmisión:
<source src="http://localhost:8081/stream.ogg">
Fue capaz de transcodificar en vlc, y mostrarse bien en mi navegador chrome y en mi teléfono Android, pero terminé tomando una solución diferente en lugar de trabajar en la creación de mi propia aplicación web para alojar mi colección de medios y crear flujos para archivos solicitados: busqué y no pude encontrar uno gratuito que lo hiciera de la forma que necesitaba o me gustaba.
Gracias por la respuesta Camilo . Eché un vistazo más de cerca a la especificación HTTP con respecto a la solicitud de rango y encontré:
The header SHOULD indicate the total length of the full entity-body, unless
this length is unknown or difficult to determine. The asterisk "*" character
means that the instance-length is unknown at the time when the response was
generated.
Por lo tanto, es solo una cuestión de probar cómo reaccionan los navegadores cuando responden con un Content-Range: bytes 0-1/*
, por ejemplo. Te haré saber lo que pasa.
Recientemente me encontré con el mismo problema ya que quiero servir mi biblioteca a los navegadores. Sorprendentemente, la idea de enviar la transmisión a través de ffmpeg y entregarla sobre la marcha funciona bastante bien. El problema principal era apoyar la búsqueda ...
A continuación, encontrará código sniplets en Python utilizando Flask para resolver el problema:
Necesitamos una función para transmitir el contenido:
@app.route(''/media/<path:path>.ogv'')
def media_content_ogv(path):
d= os.path.abspath( os.path.join( config.media_folder, path ) )
if not os.path.isfile( d ): abort(404)
start= request.args.get("start") or 0
def generate():
cmdline= list()
cmdline.append( config.ffmpeg )
cmdline.append( "-i" )
cmdline.append( d );
cmdline.append( "-ss" )
cmdline.append( str(start) );
cmdline.extend( config.ffmpeg_args )
print cmdline
FNULL = open(os.devnull, ''w'')
proc= subprocess.Popen( cmdline, stdout=subprocess.PIPE, stderr=FNULL )
try:
f= proc.stdout
byte = f.read(512)
while byte:
yield byte
byte = f.read(512)
finally:
proc.kill()
return Response(response=generate(),status=200,mimetype=''video/ogg'',headers={''Access-Control-Allow-Origin'': ''*'', "Content-Type":"video/ogg","Content-Disposition":"inline","Content-Transfer-Enconding":"binary"})
Entonces necesitamos una función para devolver la duración:
@app.route(''/media/<path:path>.js'')
def media_content_js(path):
d= os.path.abspath( os.path.join( config.media_folder, path ) )
if not os.path.isfile( d ): abort(404)
cmdline= list()
cmdline.append( config.ffmpeg )
cmdline.append( "-i" )
cmdline.append( d );
duration= -1
FNULL = open(os.devnull, ''w'')
proc= subprocess.Popen( cmdline, stderr=subprocess.PIPE, stdout=FNULL )
try:
for line in iter(proc.stderr.readline,''''):
line= line.rstrip()
#Duration: 00:00:45.13, start: 0.000000, bitrate: 302 kb/s
m = re.search(''Duration: (..):(..):(..)/...'', line)
if m is not None: duration= int(m.group(1)) * 3600 + int(m.group(2)) * 60 + int(m.group(3)) + 1
finally:
proc.kill()
return jsonify(duration=duration)
Y, por último, lo introducimos en HTML5 utilizando videojs:
<!DOCTYPE html>
<html>
<head>
<link href="//vjs.zencdn.net/4.5/video-js.css" rel="stylesheet">
<script src="//vjs.zencdn.net/4.5/video.js"></script>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
</head>
<body>
<video id="video" class="video-js vjs-default-skin" controls preload="auto" width="640" height="264">
</video>
<script>
var video= videojs(''video'');
video.src("media/testavi.avi.ogv");
// hack duration
video.duration= function() { return video.theDuration; };
video.start= 0;
video.oldCurrentTime= video.currentTime;
video.currentTime= function(time)
{
if( time == undefined )
{
return video.oldCurrentTime() + video.start;
}
console.log(time)
video.start= time;
video.oldCurrentTime(0);
video.src("media/testavi.avi.ogv?start=" + time);
video.play();
return this;
};
$.getJSON( "media/testavi.avi.js", function( data )
{
video.theDuration= data.duration;
});
</script>
</body>
Puede encontrar un ejemplo de trabajo en https://github.com/derolf/transcoder .
dero