git clone https://toreaurstad@bitbucket.org/toreaurstad/wcfaudiostreamdemo.git
 git fetch && git checkout VideBase64String_05082018
The following method is added to our WCF service contract, note that now we leave the webHttpBinding and use a CustomBinding.
     [OperationContract]
     byte[] GetVideoBytes(string videofile);
There is no [WebGet] attribute this time, as noted we will not use WCF REST but SOAP instead. 
The web.config of the host website for the WCF services exposes this CustomBinding: 
<system.serviceModel>
    <services>
      
      <service name="WcfStreamAudioDemo.Host.AudioService">
        <endpoint behaviorConfiguration="RestBehaviorConfig" binding="webHttpBinding" bindingConfiguration="HttpStreaming" contract="WcfStreamAudioDemo.Common.IAudioServiceContract" />
      </service>
      
      <service name="WcfStreamAudioDemo.Host.VideoService">
        <endpoint behaviorConfiguration="RestBehaviorConfig" binding="webHttpBinding" bindingConfiguration="HttpStreaming" contract="WcfStreamAudioDemo.Common.IVideoServiceContract" />
        <endpoint address="custom" binding="customBinding" bindingConfiguration="CustomBinding" contract="WcfStreamAudioDemo.Common.IVideoServiceContract" />
      </service> 
    </services>
    
    <bindings>
      
     <customBinding>
        <binding name="CustomBinding">
          <binaryMessageEncoding>
            <readerQuotas maxArrayLength="100000000" maxStringContentLength="100000000"/>
          </binaryMessageEncoding>
          <httpTransport />
        </binding>
      </customBinding>
      <webHttpBinding>
        <binding name="HttpStreaming" transferMode="Streamed" maxReceivedMessageSize="1000000000" maxBufferPoolSize="100000000">
          <readerQuotas maxArrayLength="100000000" maxStringContentLength="100000000"/>
        </binding>
      </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="true"/>
        </behavior>
      </serviceBehaviors>
     
      <endpointBehaviors>
        <behavior name="RestBehaviorConfig">
          <webHttp />
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <protocolMapping>
        <add binding="basicHttpsBinding" scheme="https" />
    </protocolMapping>    
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
Note that the custom binding allows us to set up a binaryMessageEncoding. 
The next step is to go to the client project and add a service reference to the host project containing the WCF service. The app.config file is then updated, relevant parts shown here: 
 <system.webServer>
    <directoryBrowse enabled="true" />
    <security>
      <requestFiltering>
        <requestLimits maxAllowedContentLength="1073741824" />
      </requestFiltering>
    </security>
        <staticContent>
            <mimeMap fileExtension=".mp4" mimeType="video/mp4" />
        </staticContent>
  </system.webServer>
  <system.serviceModel>
    <bindings>
      
      <customBinding>
        <binding name="CustomBinding">
          <binaryMessageEncoding>
            <readerQuotas maxArrayLength="100000000" maxStringContentLength="100000000" />
          </binaryMessageEncoding>
          <httpTransport maxReceivedMessageSize="100000000" />
        </binding>
      </customBinding>
    </bindings>
    <client>
      <endpoint address="http://he139920.helsemn.no/WcfStreamAudioDemo.Host/VideoService.svc/custom" contract="VideoService.IVideoServiceContract" binding="customBinding" bindingConfiguration="CustomBinding" />
    </client>
    
  </system.serviceModel>
Note the changes of setting up a MIME map for .mp4 files and setting up requestlimits. I will explain this soon. 
The client script now actually consists both of a server side script run in ASP.NET that sets up the video control in two ways. One way is to construct a HTML5 Data Url. This clearly was demanding for the browser to cope with and is not recommended. The video is actually embed on the page as a base64 encoded string! For a small video such as our video, it is actually possible still. It is of course fascinating that we can embed an entire video on our ASPX web page just as a Base64 encoded string, like it or not. The way to do this anyways is like this: 
<script runat="server">
    private void btnInvokeWcfService_OnClick(object sender, EventArgs e)
    {
        using (var proxy = new VideoServiceContractClient())
        {
            byte[] payload = proxy.GetVideoBytes("sintel_trailer-480p");
            SetVideoSourceToHtmlDataUri(payload);
        }
    }
    private void SetVideoSourceToHtmlDataUri(byte[] payload)
    {
        //Set a base64 Html data uri
        videoCtrlFedByByteArrayThroughProxy.Attributes["type"] = "video/mp4";
        string base64String = Convert.ToBase64String(payload);
        videoCtrlFedByByteArrayThroughProxy.Attributes["src"] = "data:video/mp4;base64," + base64String;
    }
</script>
The following image shows how this actually works! 
 Of course, this gives rendering and performance issues, as the web page now got very large content - the entire video is embedded into the page!
Another way is to write the byte array to a temporary file on the server, and set the src attribute to this temporary file.
Of course, this gives rendering and performance issues, as the web page now got very large content - the entire video is embedded into the page!
Another way is to write the byte array to a temporary file on the server, and set the src attribute to this temporary file. 
<script runat="server">
    private void btnInvokeWcfService_OnClick(object sender, EventArgs e)
    {
        using (var proxy = new VideoServiceContractClient())
        {
            byte[] payload = proxy.GetVideoBytes("sintel_trailer-480p");
            SetVideoSourceToTempFile(payload);
        }
    }
    private void SetVideoSourceToTempFile(byte[] payload)
    {
        //write to a temp file 
        string tempfile = Path.GetRandomFileName() + ".mp4";
        string tempfilePathForWebServer = HttpContext.Current.Server.MapPath("media/") + tempfile;
        File.WriteAllBytes(tempfilePathForWebServer, payload);
        videoCtrlFedByByteArrayThroughProxy.Attributes["src"] = "media/" + tempfile;
    }
</script>
Note that in these two samples, I have adjusted the HTML5 video control to be accessible to ASP.NET like this: 
   <asp:Button runat="server" ID="btnInvokeWcfService" Text="Load Video" OnClick="btnInvokeWcfService_OnClick"/>
        
        <video id="videoCtrlFedByByteArrayThroughProxy" type="video/mp4"  runat="server"  controls width="320" height="240">         
            <p>Your browser doesn't support HTML5 video. Here is a <a href="http://wcfaudiodemohost.azurewebsites.net/VideoService.svc/mediabytes/sintel_trailer-480p">link to the video</a> instead.</p> 
        </video>
This method of retrieving video from WCF is the quickest way, it only fetches the original byte array data and it loads quickly. An adjusted version would not write to a file directly accessible on the web server, but use for example IsolatedStorage instead. I will look into that in the future. 
Hope you found this article interesting. CustomBindings in WCF gives you a lot of flexibility!
No comments:
Post a Comment