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>