Conceito básico
Para enviarmos dados assincronamente tiraremos proveito da nova classe PushStreamContent contida no namespace System.Net.Http. Essa classe permite que possamos enviar pacotes por demanda para o cliente. A referência que tive veio do blog Andru's WebLog. Em nosso caso leremos o arquivo do disco do servidor e o enviaremos pouco a pouco para o cliente que visualizará ou escutará a mídia por meio de controles HTML5 de áudio e vídeo.
Implementação
//A classe recebe como construtor uma Action public PushStreamContent(ActionCriei uma classe de ajuda que lê a mídia do disco do servidor e assincronamente a salva em um buffer e libera para o cliente de acordo com a demanda. Usei as novas técnicas de programação contidas no C# 5.0 async/await para realizar a tarefa.onStreamAvailable);
public class MediaStream
{
private readonly string _filename;
public MediaStream(string filename)
{
this._filename = filename;
}
public async void WriteToStream(Stream outputStream, HttpContent content, TransportContext context)
{
try
{
var buffer = new byte[65536];
using (var media = File.Open(_filename, FileMode.Open, FileAccess.Read))
{
var length = (int)media.Length;
var bytesRead = 1;
while (length > 0 && bytesRead > 0)
{
bytesRead = media.Read(buffer, 0, Math.Min(length, buffer.Length));
await outputStream.WriteAsync(buffer, 0, bytesRead);
length -= bytesRead;
}
}
}
catch (HttpException ex)
{
return;
}
finally
{
outputStream.Close();
}
}
}
Note que a assinatura do método WriteToStream corresponde a assinatura do métodos PushStreamContent.
Logo depois criei duas classes, uma para Vídeo e outra para Áudio ambas filhas da classe MediaStream. Fiz dessa maneira para promover uma certa extensibilidade caso seja necessário fazer algo mais complexo mais tarde.
public class VideoStream : MediaStream
{
public VideoStream(string filename) : base(filename)
{
}
}
public class AudioStream : MediaStream
{
public AudioStream(string filename) : base(filename)
{
}
}
Agora vamos para a classe Controller para áudio e vídeo, que ficaram bastante simples após a separação nas classes anteriores.
public class AudiosController : ApiController
{
// GET api/music/5
public HttpResponseMessage Get(string filename, string ext)
{
filename = @"\Content\Audio\" + filename + "." + ext;
var audio = new AudioStream(filename);
var response = Request.CreateResponse();
response.Content = new PushStreamContent(audio.WriteToStream, new MediaTypeHeaderValue("audio/" + ext));
return response;
}
}
public class VideosController : ApiController
{
// GET api/music/5
public HttpResponseMessage Get(string filename, string ext)
{
filename = @"\Content\Audio\" + filename + "." + ext;
var video = new VideoStream(filename);
var response = Request.CreateResponse();
response.Content = new PushStreamContent(video.WriteToStream, new MediaTypeHeaderValue("video/" + ext));
return response;
}
}
O nome do arquivo deve conter o caminho completo do mesmo em seu servidor, logo quando for fazer o seu teste lembre-se disso.
Agora para que o cliente possa passar um endereço e consumir os nossos serviços é necessário que este registre uma rota com a padrão correspondendo, neste exemplo estou usando api/extensão/nomeDoArquivo:
config.Routes.MapHttpRoute(
name: "DefaultVideo",
routeTemplate: "api/{controller}/{ext}/{filename}"
);
Consumindo os serviços
Basta criar uma página e adicionar nossos controles HTML5:
Agora você pode abrir alguma ferramenta como firebug e ver que o conteúdo está sendo transmitido por streaming e não sendo carregado como um todo. Uma observação é que você deve usar o Chrome para fazer seus testes e brincar com este exemplo, eu não consegui fazer funcionar em nenhum outro =). Espero que tenham gostado da dica, o código fonte se encontra abaixo:Música
Video
E se em vez do cliente receber um fluxo de um vídeo armazenado no servidor eu quiser que ele se conecte a um fluxo MMS.
ResponderExcluirNo meu caso eu estou transmitindo um evento e quero que um player na página reproduza
Funciona com vídeos grandes (100MB por exemplo)?
ResponderExcluir