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>

No comments:
Post a Comment