9

Lately I’ve been trying to solve a few specific problems with Smooth Streaming, and I noticed as I go through forums, blogs, and Microsoft web sites, the only thing clear is that this topic is vague, and there are lots of unanswered questions out there. In addition, the way Microsoft uses the term “Live Smooth Streaming”, it almost seems intentionally misleading. So, with that in mind, I’d like to clear up a few things.

How does Smooth Streaming work? (the quick and straightforward version)
Smooth Streaming (or, as I will call it, On-Demand Smooth Streaming) relies on having the same stream encoded at varying bitrates, with one complete streaming file per bitrate. So you will typically have individual files that look like this in a web server directory:

  • MyStream_230.ismv
  • MyStream_305.ismv
  • MyStream_403.ismv
  • MyStream_534.ismv
  • MyStream_708.ismv
  • MyStream_937.ismv
  • MyStream_1241.ismv
  • MyStream_1644.ismv

These files all contain special timecode marks in them, and are all tied together by two “manifest” files, for example:

  • MyStream.ism
  • MyStream.ismc

These files simply contain a mapping of which bitrates correspond with each stream (for example, the 230k stream = MyStream_230.ismv, 708k stream = MyStream_708.ismv).

The Smooth Streaming player makes tiny HTTP requests requesting a second or two of video at a certain bitrate. For example, “give me 230k @ 00:01:06, give me 708k @ 00:01:08”, etc. These requests are no different than how your browser downloads images on a web page. The “smooth” part comes in because with this architecture, it’s extremely easy for your computer to switch bitrates and adjust to your network connection.

IIS Media Services
An IIS7 feature called IIS Media Services is what makes it possible for the server to understand these player requests, timecode, and a few special URL constructs.

An example of this is the Manifest request; if you had a smooth stream (with MyStream.ism) in the root folder of your IIS web server, you can open up your browser and go to:

http://server/MyStream.ism/Manifest

IIS Media Services will generate the XML manifest file that the player would consume, and you can view it in your browser.

What is “Live Smooth Streaming”?
Live Smooth Streaming uses the same technique of on-demand Smooth Streaming, except encoded in real-time for live events by hardware encoders. Although it is similar in player concept, it is quite different in implementation.

IIS Features: Smooth Streaming Presentations vs. Live Smooth Streaming Publishing Points
Once you install IIS Media Services, and navigate to a folder under an IIS web site, you will see these options:

image

  • Live Smooth Streaming Publishing Points – Similar to Windows Media Services, you can create publishing points that live hardware encoders can use.
  • Smooth Streaming Presentations – This is the feature that detects ISM and ISMC files (smooth streaming manifests) within a web site directory and interprets them as Smooth Streams.

How can you encode content for On-Demand Smooth Streaming?
Expression Encoder, with the IIS Live Smooth Streaming option.

Clarification Point: Expression Encoder is offered in 2 versions, a free version without the ability to do this, and a paid version with IIS Smooth Streaming encoding capabilities.

The IIS Smooth Streaming version will:

  • Encode your content into separate bitrate files, and….
  • Create the manifest files (.ISM, .ISMC) automatically

How can you encode content for Live Smooth Streaming?
Let me be clear, since this is the most confusing and vague part of Smooth Streaming to date: As of this writing, there is no software encoder that can do Live Smooth Streaming. You will have to be prepared to drop anywhere between $5,000 to $20,000 on a hardware IP video encoder (or a series of hardware encoders). For example, the Inlet Spinnaker Encoder.

The reason for this is the amount of sheer processing power that it requires to encode a smooth stream – it’s fairly intensive for a regular computer to encode one stream in realtime of acceptable quality; for a normal smooth streaming event, it’s eight realtime streams, ranging from low-quality to extremely high quality.

Again, Expression Encoder does NOT do Live Smooth Streaming. Expression Encoder only prepares content for On-Demand Smooth Streaming.

How do you play a Smooth Stream with Silverlight?
Just as you would play another media file in a Silverlight media player, but with a twist or two.

  • Set the MediaSource property of your playlist item to the HTTP path of your .ISM file, appended with /Manifest - for example:

    http://server/path-to-mystream.ism/Manifest

  • Make sure to also set the playlist item property IsAdaptiveStreaming to true. Without this, Silverlight won’t understand that it’s a smooth streaming source, and will be confused.
  • If your Silverlight player is not located on the same host / relative path of your Smooth Stream, make sure that the root directory of your IIS site has a CrossDomain.xml file present, with contents that look like this:

    <?xml version="1.0"?>
    <!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
    <cross-domain-policy>
      <allow-http-request-headers-from domain="*" headers="*"/>
    </cross-domain-policy>

    This simply tells Silverlight that it’s okay to contact this domain for content.

  • If you’re making your own Silverlight application with a player, Silverlight somehow needs to reference the AdaptiveStreaming.dll assembly. The easiest way to do this is to take the stock boilerplate player that Expression Encoder creates for you, and look for the SmoothStreaming.xap file in your output directory. Rename it to SmoothStreaming.zip (XAP files are really Zip files), and extract it. Inside are the assemblies you need to reference in your custom player (including AdaptiveStreaming.dll).

 

Hopefully this will clear up some understanding when working with Smooth Streaming, or at least making the total picture a little easier. Good luck!

Oh, and by the way – if you don’t want to deal with any of this junk, try NimbusHD – it’s Smooth Streaming as an affordable cloud service with a lot more features.

2

MOSS provides an Event Receiver model you can use to perform actions when something happens (i.e. a list item is updated). This is somewhat described here:

http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.speventreceiverbase.aspx

It can be easy to end up in a loop here; for example, if you handle the ItemUpdated event, and you update the item again inside your event receiver, then your event receiver is going to fire again, causing you to update the item again, which will cause the event receiver to fire, causing you to … well, you get the picture.

SharePoint does have built-in protection for this problem, though – it will only allow a “depth” of 10 recursive events to fire.

Additionally, to solve this problem, the SharePoint object model provides two methods that can prevent this:

image

This all works great, but the solution I was working with on this problem required a somewhat “mass processing” of a bunch of list items. To do this, the event receiver spawns a few new threads for processing list items. We started getting some very weird errors – my favorite?

WebPartPageUserException: Catastrophic failure (Exception from HRESULT: 0x8000FFFF (E_UNEXPECTED))

In any case, what we actually found was that DisableEventFiring and EnableEventFiring, quite frankly don’t do jack when it comes to other threads. Why?

They set a property in the SPEventManager class:

image

Which, in turn, flags some data on the current thread!

image

On one hand, this makes sense, from the HTTP model perspective (you don’t want to disable events globally, because other updates coming from a browser wouldn’t fire the initial events while you were still processing yours), but at the same time, it’s far from obvious that asynchronous multithreading is not supported in the event model.

Lessons learned:

  • The SharePoint event model is NOT friendly to multi-threading. If possible, offload asynchronous operations to a timer job or another method that will take place in a single thread / SharePoint request context.
  • For multi-threaded operations, make sure the threads you spawn disable events before updating any items, to avoid an infinite loop.

There is a severe problem with the SharePoint 2007 ConsoleNode. In particular, you can run into an astonishing performance loss when attempting to build a dynamic menu that can change depending on page state. The Microsoft.SharePoint.Publishing.WebControls.ConsoleNode class is a base class that you inherit to customize the SharePoint Editing Toolbar.

image

In the above image, the “Page”, “Workflow”, and “Tools” options, for example, are ConsoleNodes.

The high level implementation goes like this:

  1. Create a class – let’s say, MyConsoleNode – that inherits from Microsoft.SharePoint.Publishing.WebControls.ConsoleNode.
  2. Override the GET property accessor for ChildConsoleNodes, shown below.

    image

  3. When SharePoint requests the ChildConsoleNodes property, you build a few child nodes that will be displayed in the dropdown menu, and add them to the base.ChildConsoleNodes collection.

This is the pattern that Microsoft uses for their Out-of-the-Box console nodes as well.

The Problem

This appears to be a fairly straightforward pattern, and works well, until you start doing any significant calculations to build your Child Node collection. The reason is that, in my testing, this property gets called 12+ times per page load. Hang on, because this journey is not over…

Most normal developers would solve this problem with a simple boolean flag, say in the current HTTP request, that indicates whether or not you have already built your child nodes, and if so, don’t do it again. Do NOT do this! You will most likely end up with a completely blank child menu, and start to question why you became a SharePoint developer.

This is the way that MyConsoleNode is used by SharePoint when it renders the page; my best guess is that it’s done this way to calculate permissions. For the purposes of my example, we’ll assume that 2 child console nodes are built.

  1. HTTP request begins, and SharePoint starts to build the page.
  2. A new instance of MyConsoleNode is instantiated (we’ll call this Instance A).
  3. GET ChildConsoleNodes is called (Instance A), no existing child nodes.
  4. GET ChildConsoleNodes is called (Instance A), 2 child nodes are already present.
  5. A new instance of MyConsoleNode is instantiated (Instance B).
  6. GET ChildConsoleNodes is called (Instance B), no existing child nodes.
  7. GET ChildConsoleNodes is called (Instance B), 2 child nodes already present.
  8. GET ChildConsoleNodes is called (Instance B), 0 child nodes already present.
  9. GET ChildConsoleNodes is called (Instance B), 2 child nodes already present.
  10. GET ChildConsoleNodes is called (Instance B), 3 child nodes already present [don’t ask – I don’t know].
  11. GET ChildConsoleNodes is called (Instance B), 2 child nodes already present.
  12. GET ChildConsoleNodes is called (Instance B), 2 child nodes already present.
  13. GET ChildConsoleNodes is called (Instance B), 2 child nodes already present.
  14. GET ChildConsoleNodes is called (Instance B), 2 child nodes already present.
  15. GET ChildConsoleNodes is called (Instance B), 2 child nodes already present.
  16. Page is rendered.

It’s fairly obvious why a simple boolean flag will not work, for two reasons:

  • The class is instantiated twice (once in Step 2, again in Step 5).
  • Even when the class is not instantiated again, in Step 8, the previously added child nodes are gone.

There’s another mystery of why there are 3 child nodes in Step 10, but I didn’t really care to research that one.

The Solution

If you want to solve this problem, you will, unfortunately, have to use reflection to get an internal property value. I am not going to post the code to do this, because it is not guaranteed to survive a MOSS Service Pack upgrade or translate into future versions of SharePoint, and you will have to make some decisions in your code as to what to do if this property disappears at some point.

The key to solving this problem is a boolean internal property of ConsoleNode called ReadyToCreateChildNodes.

image

If you look at a Microsoft out-of-the-box Console Node (for example, let’s check out ModifyWebPartsNode) you’ll see a method called EnsureChildNodes():

image

In the code of this method, Microsoft internally checks for this condition:

image

As a side note, in my testing, I did also duplicate the ConsoleNode.IsValidStateForNode conditions, and they were always true; however, this depends on the permissions you’ve applied to the node, and I was using EmptyMask. But for the purposes of solving this problem, all you really need to care about is ReadyToCreateChildNodes.

Let’s look at the request again, now considering the state of this property.

  1. HTTP request begins, and SharePoint starts to build the page.
  2. A new instance of MyConsoleNode is instantiated (we’ll call this Instance A).
  3. GET ChildConsoleNodes is called (Instance A), no existing child nodes. [ReadyToCreateChildNodes = FALSE]
  4. GET ChildConsoleNodes is called (Instance A), 2 child nodes are already present. [ReadyToCreateChildNodes = FALSE]
  5. A new instance of MyConsoleNode is instantiated (Instance B).
  6. GET ChildConsoleNodes is called (Instance B), no existing child nodes. [ReadyToCreateChildNodes = FALSE]
  7. GET ChildConsoleNodes is called (Instance B), 2 child nodes already present. [ReadyToCreateChildNodes = FALSE]
  8. GET ChildConsoleNodes is called (Instance B), 0 child nodes already present. [ReadyToCreateChildNodes = TRUE]
  9. GET ChildConsoleNodes is called (Instance B), 2 child nodes already present. [ReadyToCreateChildNodes = FALSE]
  10. GET ChildConsoleNodes is called (Instance B), 3 child nodes already present. [ReadyToCreateChildNodes = FALSE]
  11. GET ChildConsoleNodes is called (Instance B), 2 child nodes already present. [ReadyToCreateChildNodes = FALSE]
  12. GET ChildConsoleNodes is called (Instance B), 2 child nodes already present. [ReadyToCreateChildNodes = FALSE]
  13. GET ChildConsoleNodes is called (Instance B), 2 child nodes already present. [ReadyToCreateChildNodes = FALSE]
  14. GET ChildConsoleNodes is called (Instance B), 2 child nodes already present. [ReadyToCreateChildNodes = FALSE]
  15. GET ChildConsoleNodes is called (Instance B), 2 child nodes already present. [ReadyToCreateChildNodes = FALSE]
  16. Page is rendered.

Occasionally, in my testing, ReadyToCreateChildNodes in step #9 would also be in a “true” state, but for the most part, it was only true for one call. And once I built my child nodes in Step 8, they remained populated for the rest of the request.

So, to sum this all up, the solution to this problem is: Use reflection to check the internal boolean ReadyToCreateChildNodes property before you build your child nodes.