Streaming Media Content Over WCF RESTful Service

| 1 comments

WCF RESTful service API enables you to serve POX type of content over the http transport, and its default content-type header is application/xml if you use DataContractSerializer and application/json if you use DataContractJsonSerializer. If you need to serve up other contents for instance video/audio content, you need to explicitly control the content-type header, this could be achieved again by writing custom message formatter as demonstrated in the previous post, here is the code:

public class ContentTypeMessageFormatter : IDispatchMessageFormatter
{
    private IDispatchMessageFormatter formatter;
    private String contentType;
    public ContentTypeMessageFormatter(IDispatchMessageFormatter formatter, String contentType)
    {
        this.formatter = formatter;
        this.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);
    }
}

public class ContentTypeAttribute : Attribute, IOperationBehavior
{
    public ContentTypeAttribute(String contentType)
    {
        this.ContentType = contentType;
    }

    public String ContentType
    {
        get;
        set;
    }

    public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
    {
    }

    public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
    {
        dispatchOperation.Formatter = new ContentTypeMessageFormatter(dispatchOperation.Formatter, ContentType);
    }

    public void Validate(OperationDescription operationDescription)
    {
    }
}

Then you could directly specify the ContentTypeAttribute at the service operation level, the following service could stream WMV video content over http by turning on the streamed transfer mode of WCF:

[ServiceContract]
public interface IMediaService
{
    [OperationContract]
    [ContentType("audio/x-ms-wmv")]
    [WebGet(UriTemplate = "media/{name}")]
    Stream GetMedia(String name);
}

[AspNetCompatibilityRequirements(RequirementsMode=AspNetCompatibilityRequirementsMode.Required)]
public class MediaService : IMediaService
{
    public Stream GetMedia(String name)
    {
        var dir = HttpContext.Current.Server.MapPath("~");
        var file = String.Format("{0}.wmv", name);
        var filePath = Path.Combine(dir, file);
        return File.OpenRead(filePath);
    }
}

You turn on streaming in WCF, you need to configure the service as follows:

<system.serviceModel>
  <
serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
  <
services>
    <
service
     behaviorConfiguration="serviceBehavior"
     name="CustomContentTypeInRESTDemo.MediaService">
      <
endpoint
       behaviorConfiguration="RestBehaviorConfig"
       binding="webHttpBinding"
       bindingConfiguration="HttpStreaming"
       contract="CustomContentTypeInRESTDemo.IMediaService"/>
    </
service>
  </
services>
  <
bindings>
    <
webHttpBinding>
      <
binding name="HttpStreaming" maxReceivedMessageSize="67108864" transferMode="Streamed"/>
    </
webHttpBinding>
  </
bindings>
  <
behaviors>
    <
endpointBehaviors>
      <
behavior name="RestBehaviorConfig">
        <
webHttp/>
      </
behavior>
    </
endpointBehaviors>
  </
behaviors>
</
system.serviceModel>

Streaming audio/video content using RESTful service could be pretty useful, in particular if you need to use WPF’s MediaElement to playback content provided by your service, since MediaElement only allows you to specify a Uri of the media file, the DirectShow has build-in source filter to feed video/audio bits from HTTP transport or local file system, but doesn’t provide a built-in source filter to read media bits from arbitrary stream.

In WPF, you could directly have MediaElement’s Source property pointing to the templated REST Uri:

<MediaElement Source="http://localhost:8080/MediaService.svc/media/testVideo"/>

Actually, if you need VCR type of control over the streamed media content, then you need native protocol level support such as RTSP. Windows Media Services is Microsoft’s server implementation of RTSP protocol, and is available in Windows Server 2003 and Windows Server 2008. This link illustrates how to configure WMS in Windows Server 2003.

Include XML Declaration in WCF RESTful Service Response

| 10 comments

When using WCF to write RESTful web services, the default XML response stream doesn’t contain the XML Declaration or XML DOCTYPE. the default XML serialization mechanism used by WCF’s data contract serializer does support emitting XML declaration, but it isn’t turned out by default. You could try using XmlSerializer instead which does support emitting XML declaration,  but if you need stick to data contract serializer, you could try hooking into the WCF serialization process by writing a custom message formatter, the following code contains the full implementation of this approach:

public class XmlDeclarationMessage : Message
{
    private Message message;
    public XmlDeclarationMessage(Message message)
    {
        this.message = message;
    }

    public override MessageHeaders Headers
    {
        get { return message.Headers; }
    }

    protected override void OnWriteBodyContents(System.Xml.XmlDictionaryWriter writer)
    {
        // WCF XML serialization doesn't support emitting XML DOCTYPE, you need to roll up your own here.
        writer.WriteStartDocument();
        message.WriteBodyContents(writer);
    }


    public override MessageProperties Properties
    {
        get { return message.Properties; }
    }

    public override MessageVersion Version
    {
        get { return message.Version; }
    }
}

public class XmlDeclarationMessageFormatter : IDispatchMessageFormatter
{
    private IDispatchMessageFormatter formatter;
    public XmlDeclarationMessageFormatter(IDispatchMessageFormatter formatter)
    {
        this.formatter = formatter;
    }

    public void DeserializeRequest(Message message, object[] parameters)
    {
        formatter.DeserializeRequest(message, parameters);
    }

    public Message SerializeReply(MessageVersion messageVersion, Object[] parameters, Object result)
    {
        var message = formatter.SerializeReply(messageVersion, parameters, result);
        return new XmlDeclarationMessage(message);
    }
}

public class IncludeXmlDeclarationAttribute : Attribute, IOperationBehavior
{
    public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
    {
    }

    public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
    {
        dispatchOperation.Formatter = new XmlDeclarationMessageFormatter(dispatchOperation.Formatter);
    }

    public void Validate(OperationDescription operationDescription)
    {
    }
}

Then you could specify the IncludeXmlDeclarationAttribute at service operation level, if you need to enable this at service wide level, you could try implementing the IServiceBehavior interface instead.

[ServiceContract]
public interface IDemoService
{
    [OperationContract]
    [IncludeXmlDeclaration]
    [WebGet(UriTemplate = "customers/{name}/")]
    Customer GetCustomer(String name);
}