void AddMarker(MidrollAdvertisement ad)
        {
            if (ad.TimePercentage.HasValue)
            {
                ad.Time = TimeSpan.FromSeconds(ad.TimePercentage.Value * MediaPlayer.Duration.TotalSeconds);
            }

            MediaPlayer.Markers.Add(new TimelineMarker()
            {
                Type = MarkerType_Play, Text = ad.Id, Time = ad.Time
            });
            if (PreloadTime.HasValue)
            {
                MediaPlayer.Markers.Add(new TimelineMarker()
                {
                    Type = MarkerType_Preload, Text = ad.Id, Time = TimeSpanExtensions.Max(TimeSpan.FromMilliseconds(1), ad.Time.Subtract(PreloadTime.Value))
                });                                                                                                                                                                                          // clamp to 1 ms
            }
        }
        private void CreateAdvertisement(VmapAdBreak adBreak)
        {
            Advertisement ad = null;

            switch (adBreak.TimeOffset)
            {
            case "start":
                ad = new PrerollAdvertisement();
                break;

            case "end":
                ad = new PostrollAdvertisement();
                break;

            default:
                var offset = FlexibleOffset.Parse(adBreak.TimeOffset);
                if (offset != null)
                {
                    var midroll = new MidrollAdvertisement();
                    if (!offset.IsAbsolute)
                    {
                        midroll.TimePercentage = offset.RelativeOffset;
                    }
                    else
                    {
                        midroll.Time = offset.AbsoluteOffset;
                    }
                    ad = midroll;
                }
                break;
            }

            if (ad != null)
            {
                ad.Source = GetAdSource(adBreak.AdSource);
                if (ad.Source != null)
                {
                    Advertisements.Add(ad);
                    adBreaks.Add(ad, adBreak);
                }
            }
        }
        private void CreateAdvertisement(VmapAdBreak adBreak)
        {
            Advertisement ad = null;
            switch (adBreak.TimeOffset)
            {
                case "start":
                    ad = new PrerollAdvertisement();
                    break;
                case "end":
                    ad = new PostrollAdvertisement();
                    break;
                default:
                    var offset = FlexibleOffset.Parse(adBreak.TimeOffset);
                    if (offset != null)
                    {
                        var midroll = new MidrollAdvertisement();
                        if (!offset.IsAbsolute)
                        {
                            midroll.TimePercentage = offset.RelativeOffset;
                        }
                        else
                        {
                            midroll.Time = offset.AbsoluteOffset;
                        }
                        ad = midroll;
                    }
                    break;
            }

            if (ad != null)
            {
                ad.Source = GetAdSource(adBreak.AdSource);
                if (ad.Source != null)
                {
                    Advertisements.Add(ad);
                    adBreaks.Add(ad, adBreak);
                }
            }
        }
        /// <summary>
        /// Loads ads from a source URI. Note, this is called automatically if you set the source before the MediaLoading event fires and normally does not need to be called.
        /// </summary>
        /// <param name="source">The SmartXML source URI</param>
        /// <param name="c">A cancellation token that allows you to cancel a pending operation</param>
        /// <returns>A task to await on.</returns>
        public async Task LoadAds(Uri source, CancellationToken c)
        {
            adSlots.Clear();
            var cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(c, cts.Token).Token;

#if SILVERLIGHT
            adResponse = await FreeWheelFactory.LoadSource(source, cancellationToken);
#else
            adResponse = await FreeWheelFactory.LoadSource(source).AsTask(cancellationToken);
#endif

            var videoTracking = adResponse.SiteSection.VideoPlayer.VideoAsset.EventCallbacks.FirstOrDefault(ec => ec.Name == FWEventCallback.VideoView);
            if (videoTracking != null)
            {
                // use the tracking plugins to help with tracking markers. Create it if it doesn't exist.
                var positionTrackingPlugin = MediaPlayer.Plugins.OfType <PositionTrackingPlugin>().FirstOrDefault();
                if (positionTrackingPlugin == null)
                {
                    positionTrackingPlugin = new PositionTrackingPlugin();
                    MediaPlayer.Plugins.Add(positionTrackingPlugin);
                }
                positionTrackingPlugin.EventTracked += trackingPlugin_EventTracked;
                lastTrackingEvent = null; // reset
                trackingEnded     = false;
                positionTrackingPlugin.TrackingEvents.Add(new PositionTrackingEvent()
                {
                    PositionPercentage = 1, Data = videoTracking, Area = TrackingEventArea
                });

                var playTimeTrackingPlugin = MediaPlayer.Plugins.OfType <PlayTimeTrackingPlugin>().FirstOrDefault();
                if (playTimeTrackingPlugin == null)
                {
                    playTimeTrackingPlugin = new PlayTimeTrackingPlugin();
                    MediaPlayer.Plugins.Add(playTimeTrackingPlugin);
                }
                playTimeTrackingPlugin.EventTracked += trackingPlugin_EventTracked;
                for (int i = 0; i < 60; i = i + 15)
                {
                    playTimeTrackingPlugin.TrackingEvents.Add(new PlayTimeTrackingEvent()
                    {
                        PlayTime = TimeSpan.FromSeconds(i), Data = videoTracking, Area = TrackingEventArea
                    });
                }
                for (int i = 60; i < 60 * 3; i = i + 30)
                {
                    playTimeTrackingPlugin.TrackingEvents.Add(new PlayTimeTrackingEvent()
                    {
                        PlayTime = TimeSpan.FromSeconds(i), Data = videoTracking, Area = TrackingEventArea
                    });
                }
                for (int i = 60 * 3; i < 60 * 10; i = i + 60)
                {
                    playTimeTrackingPlugin.TrackingEvents.Add(new PlayTimeTrackingEvent()
                    {
                        PlayTime = TimeSpan.FromSeconds(i), Data = videoTracking, Area = TrackingEventArea
                    });
                }
                for (int i = 60 * 10; i < 60 * 30; i = i + 120)
                {
                    playTimeTrackingPlugin.TrackingEvents.Add(new PlayTimeTrackingEvent()
                    {
                        PlayTime = TimeSpan.FromSeconds(i), Data = videoTracking, Area = TrackingEventArea
                    });
                }
                for (int i = 60 * 30; i < 60 * 60; i = i + 300)
                {
                    playTimeTrackingPlugin.TrackingEvents.Add(new PlayTimeTrackingEvent()
                    {
                        PlayTime = TimeSpan.FromSeconds(i), Data = videoTracking, Area = TrackingEventArea
                    });
                }
                for (int i = 60 * 60; i < 60 * 180; i = i + 600)
                {
                    playTimeTrackingPlugin.TrackingEvents.Add(new PlayTimeTrackingEvent()
                    {
                        PlayTime = TimeSpan.FromSeconds(i), Data = videoTracking, Area = TrackingEventArea
                    });
                }
            }

            var videoAsset = adResponse.SiteSection.VideoPlayer.VideoAsset;
            if (videoAsset != null)
            {
                foreach (var adSlot in videoAsset.AdSlots)
                {
                    Advertisement ad = null;
                    switch (adSlot.TimePositionClass)
                    {
                    case "preroll":
                        ad = new PrerollAdvertisement();
                        break;

                    case "postroll":
                        ad = new PostrollAdvertisement();
                        break;

                    default:
                        var midroll = new MidrollAdvertisement();
                        midroll.Time = adSlot.TimePosition;
                        ad           = midroll;
                        break;
                    }

#if SILVERLIGHT
                    var payload = await FreeWheelFactory.GetAdDocumentPayload(adSlot, adResponse, cancellationToken);
#else
                    var payload = await FreeWheelFactory.GetAdDocumentPayload(adSlot, adResponse).AsTask(cancellationToken);
#endif
                    ad.Source = new AdSource(payload, DocumentAdPayloadHandler.AdType);

                    Advertisements.Add(ad);
                    adSlots.Add(ad, adSlot);
                }
            }

            ShowCompanions();
        }
        /// <summary>
        /// Loads ads from a source URI. Note, this is called automatically if you set the source before the MediaLoading event fires and normally does not need to be called.
        /// </summary>
        /// <param name="source">The SmartXML source URI</param>
        /// <param name="c">A cancellation token that allows you to cancel a pending operation</param>
        /// <returns>A task to await on.</returns>
        public async Task LoadAds(Uri source, CancellationToken c)
        {
            adSlots.Clear();
            var cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(c, cts.Token).Token;

#if SILVERLIGHT
            adResponse = await FreeWheelFactory.LoadSource(source, cancellationToken);
#else
            adResponse = await FreeWheelFactory.LoadSource(source).AsTask(cancellationToken);
#endif

            var videoTracking = adResponse.SiteSection.VideoPlayer.VideoAsset.EventCallbacks.FirstOrDefault(ec => ec.Name == FWEventCallback.VideoView);
            if (videoTracking != null)
            {
                // use the tracking plugins to help with tracking markers. Create it if it doesn't exist.
                var positionTrackingPlugin = MediaPlayer.Plugins.OfType<PositionTrackingPlugin>().FirstOrDefault();
                if (positionTrackingPlugin == null)
                {
                    positionTrackingPlugin = new PositionTrackingPlugin();
                    MediaPlayer.Plugins.Add(positionTrackingPlugin);
                }
                positionTrackingPlugin.EventTracked += trackingPlugin_EventTracked;
                lastTrackingEvent = null; // reset
                trackingEnded = false;
                positionTrackingPlugin.TrackingEvents.Add(new PositionTrackingEvent() { PositionPercentage = 1, Data = videoTracking, Area = TrackingEventArea });

                var playTimeTrackingPlugin = MediaPlayer.Plugins.OfType<PlayTimeTrackingPlugin>().FirstOrDefault();
                if (playTimeTrackingPlugin == null)
                {
                    playTimeTrackingPlugin = new PlayTimeTrackingPlugin();
                    MediaPlayer.Plugins.Add(playTimeTrackingPlugin);
                }
                playTimeTrackingPlugin.EventTracked += trackingPlugin_EventTracked;
                for (int i = 0; i < 60; i = i + 15)
                    playTimeTrackingPlugin.TrackingEvents.Add(new PlayTimeTrackingEvent() { PlayTime = TimeSpan.FromSeconds(i), Data = videoTracking, Area = TrackingEventArea });
                for (int i = 60; i < 60 * 3; i = i + 30)
                    playTimeTrackingPlugin.TrackingEvents.Add(new PlayTimeTrackingEvent() { PlayTime = TimeSpan.FromSeconds(i), Data = videoTracking, Area = TrackingEventArea });
                for (int i = 60 * 3; i < 60 * 10; i = i + 60)
                    playTimeTrackingPlugin.TrackingEvents.Add(new PlayTimeTrackingEvent() { PlayTime = TimeSpan.FromSeconds(i), Data = videoTracking, Area = TrackingEventArea });
                for (int i = 60 * 10; i < 60 * 30; i = i + 120)
                    playTimeTrackingPlugin.TrackingEvents.Add(new PlayTimeTrackingEvent() { PlayTime = TimeSpan.FromSeconds(i), Data = videoTracking, Area = TrackingEventArea });
                for (int i = 60 * 30; i < 60 * 60; i = i + 300)
                    playTimeTrackingPlugin.TrackingEvents.Add(new PlayTimeTrackingEvent() { PlayTime = TimeSpan.FromSeconds(i), Data = videoTracking, Area = TrackingEventArea });
                for (int i = 60 * 60; i < 60 * 180; i = i + 600)
                    playTimeTrackingPlugin.TrackingEvents.Add(new PlayTimeTrackingEvent() { PlayTime = TimeSpan.FromSeconds(i), Data = videoTracking, Area = TrackingEventArea });
            }

            var videoAsset = adResponse.SiteSection.VideoPlayer.VideoAsset;
            if (videoAsset != null)
            {
                foreach (var adSlot in videoAsset.AdSlots)
                {
                    Advertisement ad = null;
                    switch (adSlot.TimePositionClass)
                    {
                        case "preroll":
                            ad = new PrerollAdvertisement();
                            break;
                        case "postroll":
                            ad = new PostrollAdvertisement();
                            break;
                        default:
                            var midroll = new MidrollAdvertisement();
                            midroll.Time = adSlot.TimePosition;
                            ad = midroll;
                            break;
                    }

#if SILVERLIGHT
                    var payload = await FreeWheelFactory.GetAdDocumentPayload(adSlot, adResponse, cancellationToken);
#else
                    var payload = await FreeWheelFactory.GetAdDocumentPayload(adSlot, adResponse).AsTask(cancellationToken);
#endif
                    ad.Source = new AdSource(payload, DocumentAdPayloadHandler.AdType);

                    Advertisements.Add(ad);
                    adSlots.Add(ad, adSlot);
                }
            }

            ShowCompanions();
        }