I'estou realmente preso a tentar compreender a melhor forma de transmitir a saída em tempo real de ffmpeg para um cliente HTML5 utilizando o node.js, uma vez que há uma série de variáveis em jogo e eu não'tenho muita experiência neste espaço, tendo passado muitas horas a tentar combinações diferentes. O meu caso de uso é:
liveFFMPEG = child_process.spawn("ffmpeg", [
"-i", "rtsp://admin:[email protected]:554" , "-vcodec", "copy", "-f",
"mp4", "-reset_timestamps", "1", "-movflags", "frag_keyframe+empty_moov",
"-" // output to stdout
], {detached: false});
liveFFMPEG.stdout.pipe(resp);
Também utilizei o evento stream para escrever os dados FFMPEG para a resposta HTTP, mas não faz diferença
xliveFFMPEG.stdout.on("data",function(data) {
resp.write(data);
}
Utilizo o seguinte cabeçalho HTTP (que também é utilizado e funciona ao fazer streaming de ficheiros pré-gravados)
var total = 999999999 // fake a large file
var partialstart = 0
var partialend = total - 1
if (range !== undefined) {
var parts = range.replace(/bytes=/, "").split("-");
var partialstart = parts[0];
var partialend = parts[1];
}
var start = parseInt(partialstart, 10);
var end = partialend ? parseInt(partialend, 10) : total; // fake a large file if no range reques
var chunksize = (end-start)+1;
resp.writeHead(206, {
'Transfer-Encoding': 'chunked'
, 'Content-Type': 'video/mp4'
, 'Content-Length': chunksize // large size to fake a file
, 'Accept-Ranges': 'bytes ' + start + "-" + end + "/" + total
});
EDIT 3: A partir do IOS 10, a HLS irá suportar ficheiros mp4 fragmentados. A resposta agora, é criar activos mp4 fragmentados, com um manifesto DASH e HLS. > Pretend flash, iOS9 e abaixo e IE 10 e abaixo don't existe.
EDIT 2: Como as pessoas nos comentários estão a assinalar, as coisas mudam. Quase todos os browsers suportarão os codecs AVC/AAC. iOS ainda requer HLS. Mas através de adaptadores como o hls.js é possível jogar HLS em MSE. A nova resposta é HLS+hls.js, se precisar de iOS. ou apenas MP4 fragmentado (i.e. DASH) se não't
Há muitas razões pelas quais o vídeo e, especificamente, o vídeo ao vivo é muito difícil. (Note-se que a pergunta original especificava que o vídeo HTML5 é um requisito, mas o autor da pergunta afirmou que o Flash é possível nos comentários. Portanto, imediatamente, esta pergunta é enganadora)
Em primeiro lugar, vou reafirmar: **NÃO HÁ APOIO OFICIAL PARA A TRANSMISSÃO AO VIVO EM HTML5***. Há hacks, mas a sua quilometragem pode variar.
EDIT: desde que escrevi esta resposta, as Extensões das Fontes dos Media amadureceram, e estão agora muito perto de se tornarem uma opção viável. São apoiados na maioria dos principais navegadores. O IOS continua a ser uma resistência.
A seguir, é preciso compreender que o Vídeo a pedido (VOD) e o vídeo ao vivo são muito diferentes. Sim, são ambos vídeo, mas os problemas são diferentes, daí os formatos serem diferentes. Por exemplo, se o relógio do seu computador funcionar 1% mais rápido do que deveria, não notará num VOD. Com vídeo ao vivo, estará a tentar reproduzir vídeo antes de este acontecer. Se quiser aderir a um fluxo de vídeo ao vivo em curso, precisa dos dados necessários para inicializar o descodificador, pelo que deve ser repetido no fluxo, ou enviado para fora da banda. Com o VOD, pode ler o início do ficheiro que eles procuram até ao ponto que desejar.
Agora deixemos's cavar um pouco.
Plataformas:
Codecs:
Métodos comuns de entrega de vídeo ao vivo em browsers:
Métodos de entrega comuns para VOD em navegadores:
html5 video tag:
Vamos ver quais os navegadores que suportam que formatos
Safari:
Firefox
IE
Crómio
MP4 não pode ser usado para vídeo ao vivo (NOTA: DASH é um super conjunto de MP4, por isso don'não se confunda com isso). MP4 é dividido em duas partes: moov e mdat. mdat contém os dados brutos do vídeo áudio. Mas não é indexado, por isso, sem o moov, é inútil. O moov contém um índice de todos os dados no mdat. Mas devido ao seu formato, não pode ser 'achatado' até que se conheça a data e o tamanho de TODOS os frames. Pode ser possível construir um moov que 'fibs' o tamanho da moldura, mas é muito desperdiçador em largura de banda.
Por isso, se quiser entregar em todo o lado, precisamos de encontrar o denominador menos comum. Verá que aqui não há LCD sem recorrer ao flash exemplo:
O mais próximo de um LCD é usar o HLS para obter os seus utilizadores de iOS, e flash para todos os outros. O meu favorito pessoal é codificar HLS, e depois usar o flash para jogar HLS para todos os outros. Pode jogar HLS em flash via JW player 6, (ou escrever o seu próprio HLS para FLV em AS3 como eu fiz)
Em breve, a forma mais comum de o fazer será HLS no iOS/Mac e DASH via MSE em todo o lado (é o que a Netflix irá fazer em breve). Mas ainda estamos à espera que todos actualizem os seus navegadores. Também provavelmente precisará de um DASH/VP9 separado para o Firefox (conheço o open264; é uma porcaria. Pode' não pode fazer vídeo em perfil principal ou de alto perfil. Por isso, é actualmente inútil).
Obrigado a todos especialmente ao szatmary, uma vez que esta é uma questão complexa e tem muitas camadas, todas elas têm de estar a funcionar antes de se poder transmitir vídeo ao vivo. Para esclarecer a minha pergunta original e o uso do vídeo HTML5 versus flash - o meu caso de uso tem uma forte preferência pelo HTML5 porque é genérico, fácil de implementar no cliente e no futuro. O flash é um segundo melhor distante, por isso vamos ficar-nos pelo HTML5 para esta questão.
Aprendi muito através deste exercício e concordo que a transmissão ao vivo é muito mais difícil do que o VOD (que funciona bem com vídeo HTML5). Mas consegui que isto funcionasse satisfatoriamente para o meu caso de utilização e a solução resultou ser muito simples, depois de perseguir opções mais complexas como MSE, flash, esquemas de buffering elaborados em Node. O problema era que o FFMPEG corrompia o MP4 fragmentado e eu tinha de afinar os parâmetros do FFMPEG, e o redireccionamento do tubo de fluxo do nó padrão sobre o http que eu utilizava originalmente era tudo o que era necessário.
Em MP4 há um ' fragmentação' opção que quebra o mp4 em fragmentos muito mais pequenos que tem o seu próprio índice e torna viável a opção mp4 live streaming. Mas não é possível procurar de volta ao fluxo (OK para o meu caso de utilização), e versões posteriores de FFMPEG suportam a fragmentação.
O timing da nota pode ser um problema, e com a minha solução tenho um atraso de 2 a 6 segundos causado por uma combinação da remuxing (efectivamente o FFMPEG tem de receber o fluxo ao vivo, remuxá-lo e depois enviá-lo para o nó para servir sobre HTTP). Não se pode fazer muito quanto a isto, no entanto no Chrome o vídeo tenta apanhar o mais possível o que torna o vídeo um pouco nervoso mas mais actual que o IE11 (o meu cliente preferido).
Em vez de explicar como funciona o código neste post, verifique o GIST com comentários (o código do cliente é't incluído, é uma etiqueta de vídeo HTML5 padrão com o endereço do servidor http do nó). GIST está aqui: https://gist.github.com/deandob/9240090
Não consegui encontrar exemplos semelhantes deste caso de utilização, por isso espero que a explicação e o código acima indicados ajudem outros, especialmente porque aprendi muito com este site e ainda me considero um principiante!
Embora esta seja a resposta à minha pergunta específica, seleccionei a resposta de szatmary's como a resposta aceite por ser a mais abrangente.
Este é um equívoco muito comum. Não há suporte vídeo HTML5 ao vivo (excepto para HLS no iOS e Mac Safari). Pode ser capaz de 'hack' utiliza um contentor webm, mas eu não esperaria que isso fosse universalmente suportado. O que procura está incluído nas Extensões de Fontes de Meios de Comunicação, onde pode alimentar os fragmentos ao navegador um de cada vez. mas terá de escrever algum javascript do lado do cliente.