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