This class holds all the data needed to setup and produce a single MP4 track.
        protected string _privateData; // Either "CodecPrivateData" or "WaveFormatEx"

        #endregion Fields

        #region Constructors

        /// <summary>
        /// Prepate a SMIL generator
        /// </summary>
        /// <param name="FileRoot">
        /// Start of the media file name (used by IIS to create archive files).
        /// This will be appended with bitrate and dot-extension.
        /// </param>
        public SmilGenerator(string FileRoot, MediaStream SourceStream)
        {
            switch (SourceStream.FourCC) {
                case "mp4a":
                    _fourCC = "mp4a";
                    Height = 0;
                    Width = 0;
                    break;

                case "mp3a":
                    _fourCC = "mp3a";
                    Height = 0;
                    Width = 0;
                    break;

                case "avc1":
                case "H264":
                    _fourCC = "H264"; // use the Microsoft form, as this is for sending to IIS & Silverlight.
                    Height = SourceStream.Height;
                    Width = SourceStream.Width;
                    break;

                default: throw new ArgumentException("Stream type " + SourceStream.FourCC + " is not supported", "SourceStream");
            }
            _channel = SourceStream.TrackId;
            _fileRoot = FileRoot;
        }
 /// <summary>
 /// Remove any frames with zero length.
 /// Zero length frames kill the live stream.
 /// </summary>
 private void SanitiseStream(MediaStream stream)
 {
     lock (SyncRoot) {
         var src = new List<GenericMediaFrame>(stream.Frames);
         stream.Frames.Clear();
         foreach (var frame in src) {
             if (frame.FrameDuration < 1) continue;
             stream.Frames.Add(frame);
         }
     }
 }
        /// <summary>
        /// Pushes a set of frames to IIS. Will trigger a connect if needed.
        /// </summary>
        private void PushStream(MediaStream stream, FileRoot TargetMp4fFile)
        {
            if (stream == null || stream.Frames == null) return; // no frames.

            SanitiseStream(stream);
            if (stream.Frames.Count < 1) return; // no frames.

            if (!PushServer.IsConnected(stream.TrackId))
                ConnectAndPushHeaders(stream, TargetMp4fFile);

            // set start-of-fragment time from PTS
            stream.Offset = stream.Frames[0].FramePresentationTime - stream.Frames[0].FrameDuration;

            // Push the fragment
            var fragment_handler = TargetMp4fFile.GenerateFragment(stream);
            PushServer.PushData(stream.TrackId, fragment_handler.MoofData());
            PushServer.PushData(stream.TrackId, fragment_handler.MdatData());
        }
        /// <summary>
        /// Used once per connection, this opens a long-life HTTP stream
        /// and pushes the very basic MP4 parts needed to get IIS working.
        /// </summary>
        private void ConnectAndPushHeaders(MediaStream stream, FileRoot TargetMp4fFile)
        {
            SmilGenerator smil = new SmilGenerator("HCS Encoder by Iain Ballard.", stream);

            smil.ApproxBitrate = stream.Bitrate;
            MP4_Mangler.ExtraBoxes.SmoothSmil ssmil = new MP4_Mangler.ExtraBoxes.SmoothSmil(smil.Generate());
            PushServer.Connect(stream.TrackId); // This pushes to the subpath: Streams({id}-stream{index})

            // push headers (only done once per track)
            // each one needs it's own HTTP Chunk, so don't concat!
            PushServer.PushData(stream.TrackId, TargetMp4fFile.GenerateFileSpec());
            PushServer.PushData(stream.TrackId, ssmil.deepData());
            PushServer.PushData(stream.TrackId, TargetMp4fFile.GenerateHeaders());
        }
        /// <summary>
        /// Generate a pair of 'moof' and 'mdat' atoms for the given stream.
        /// The stream should contains a populated list of frame data, otherwise output will be empty.
        /// Keeps track of fragment index. Does not write to file. Does not remove frames. Does not adjust offset time.
        /// </summary>
        public FragmentBoxes.MediaFragmentHandler GenerateFragment(MediaStream Stream)
        {
            if (Stream.Frames == null || Stream.Frames.Count < 1) throw new Exception("Stream was empty");

            FragmentBoxes.MediaFragmentHandler output = new MP4_Mangler.FragmentBoxes.MediaFragmentHandler((uint)Stream.FragmentNumber, Stream.Offset);

            Stream.FragmentNumber++;

            foreach (var frame in Stream.Frames) {
                output.AddFrame((uint)Stream.TrackId, frame);
            }

            Stream.Frames.Clear();

            return output;
        }
 /// <summary>
 /// Store the frames of a stream in the KnownStreams cache, adding a new entry if needed.
 /// If there is a file open, then new streams will be refused with an argument exception
 /// </summary>
 private MediaStream CacheStreamFrames(MediaStream SourceStream)
 {
     var dst_stream = KnownStreams.Where(a => a.TrackId == SourceStream.TrackId).FirstOrDefault();
     if (dst_stream == null) { // not a known stream, so add it.
         if (ActiveFile != null) throw new ArgumentException("Can't add new streams to an open file. Try adding your streams first, then open the file once all streams are known", "SourceStream");
         dst_stream = new MediaStream();
         dst_stream.FourCC = SourceStream.FourCC;
         dst_stream.Frames = new List<GenericMediaFrame>(SourceStream.Frames);
         dst_stream.Height = SourceStream.Height;
         dst_stream.TrackId = SourceStream.TrackId;
         dst_stream.Width = SourceStream.Width;
         KnownStreams.Add(dst_stream);
     } else {
         dst_stream.Frames.AddRange(SourceStream.Frames);
     }
     return dst_stream;
 }
        /// <summary>
        /// Write the frames of a stream to a file if one is open or to memory if not.
        /// </summary>
        public void WriteStream(MediaStream SourceStream)
        {
            var dst_stream = CacheStreamFrames(SourceStream);

            if (ActiveFile == null) return; // No file to write.

            // Write to file system (writes, flushes and re-closes)
            using (BinaryWriter wr = new BinaryWriter(ActiveFile.Open(FileMode.Append, FileAccess.Write, FileShare.Read))) {
                wr.Write(GenerateFragment(dst_stream).FormatData()); // Write any waiting frames, plus the newly added ones
                wr.Flush();
                wr.Close(); // closes the file as well.
            }
            dst_stream.Frames.Clear(); // OK, we've handled these -- so dump the caches
        }