git@bitbucket.org:toreaurstad/wcfaudiostreamdemo.git
First off, we need to specify a content type of the method that returns a Stream with the audio. This can be achieved by implementing a class that inherits from System.Attribute and implementing IOperationBehavior. The class below sets a content type in its constructor and sets a Formatter of the DispatcherOperation in ApplyDispatchBehavior to the class ContentTypeMessageFormatter.
using System;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
namespace WcfStreamAudioDemo.Common
{
public class ContentTypeAttribute : Attribute, IOperationBehavior
{
public ContentTypeAttribute(string contentType)
{
ContentType = contentType;
}
public string ContentType { get; } //readonly auto property
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
// ReSharper disable once ArrangeThisQualifier
dispatchOperation.Formatter = new ContentTypeMessageFormatter(dispatchOperation.Formatter, this.ContentType);
}
public void Validate(OperationDescription operationDescription)
{
}
}
}
The ContentTypeMessageFormatter class is shown below:
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Web;
namespace WcfStreamAudioDemo.Common
{
public class ContentTypeMessageFormatter : IDispatchMessageFormatter
{
private readonly IDispatchMessageFormatter _formatter;
private readonly string _contentType;
public ContentTypeMessageFormatter(IDispatchMessageFormatter formatter, string contentType)
{
_formatter = formatter;
_contentType = contentType;
}
public void DeserializeRequest(Message message, object[] parameters)
{
_formatter.DeserializeRequest(message, parameters);
}
public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
{
if (!string.IsNullOrEmpty(_contentType))
WebOperationContext.Current.OutgoingResponse.ContentType = _contentType;
return _formatter.SerializeReply(messageVersion, parameters, result);
}
}
}
Next step is to define a Service Contract in WCF to test this out:
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Web;
namespace WcfStreamAudioDemo.Common
{
[ServiceContract]
public interface IAudioServiceContract
{
[OperationContract]
[ContentType("audio/wav")]
[WebGet(UriTemplate = "media/{trackName}")]
Stream GetAudio(string trackName);
}
}
The configuration of the WCF service in web.config sets up streaming by using transferMode set to Streamed on a webHttpBinding.
<system.serviceModel>
<services>
<service name="WcfStreamAudioDemo.Host.AudioService">
<endpoint behaviorConfiguration="RestBehaviorConfig" binding="webHttpBinding" bindingConfiguration="HttpStreaming" contract="WcfStreamAudioDemo.Common.IAudioServiceContract" />
</service>
</services>
<bindings>
<webHttpBinding>
<binding name="HttpStreaming" transferMode="Streamed" maxReceivedMessageSize="1000000000" />
</webHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the values below to false before deployment -->
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="RestBehaviorConfig">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
Finally, to test out this Audio Service you can either enter the url of an audio file the WCF service recognizes into a browser to download the .WAV file or for example use an HTML 5 <audio> element.
Here is a sample page I created to test out the Audio service (a simple ASP.NET Web form)!
<%@ Page Language="C#" %>
<script runat="server">
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Demo Audio Stream wcf</title>
</head>
<body>
<form id="HtmlForm" runat="server">
<div>
<h3>WCF Sample Audio streaming example</h3>
<p>Playing an audio file (.wav) using a custom WCF IDispatchMessageFormatter with REST (webHttpBinding).</p>
<audio controls>
<source src="http://localhost/WcfStreamAudioDemo.Host/AudioService.svc/media/Sample" type="audio/wav" />
<p>Your browser doesn't support HTML5 audio. Here is a <a href="http://localhost/WcfStreamAudioDemo.Host/AudioService.svc/media/Sample.wav">link to the audio</a> instead.</p>
</audio>
</div>
</form>
</body>
</html>
