예제 #1
0
        protected void TrackEvent(ActiveAdUnit adUnit, TrackingType eventToTrack)
        {
            if (AdTrackingEventOccurred != null)
            {
                AdTrackingEventOccurred(this, new AdTrackingEventEventArgs(adUnit.CreativeSource, eventToTrack));
            }

            foreach (var trackingEvent in adUnit.CreativeSource.TrackingEvents.Where(te => te.Type == eventToTrack))
            {
                var url = trackingEvent.Value;
                switch (trackingEvent.Type)
                {
                case TrackingType.FirstQuartile:
                case TrackingType.Midpoint:
                case TrackingType.ThirdQuartile:
                case TrackingType.Complete:
                    var previousEvents = quartileHistory[adUnit];
                    var currentTime    = DateTime.Now;
                    url = url.Replace(Macro_PreviousQuartile, previousEvents.Any() ? ((int)Math.Round(currentTime.Subtract(previousEvents.Last()).TotalSeconds)).ToString() : "0");
                    previousEvents.Add(currentTime);
                    break;
                }
                TrackUrl(url, adUnit.CreativeSource);
            }
        }
 void OnActivateAd(ActiveAdUnit activeAdUnit, IEnumerable <ICompanionSource> companions, CompanionAdsRequired suggestedCompanionRules)
 {
     if (ActivateAdUnit != null)
     {
         ActivateAdUnit(this, new ActivateAdUnitEventArgs(activeAdUnit.CreativeSource, activeAdUnit.Player, activeAdUnit.CreativeConcept, activeAdUnit.AdSource, companions, suggestedCompanionRules));
     }
 }
        async Task <Task> PlayAdUnitAsync(ActiveAdUnit adUnit, CancellationToken cancellationToken)
        {
#if WINDOWS_PHONE7
            // WP doesn't support 3 MediaElements all with a source loaded. Instead of preloading, just wait until we finish the ad.
            await VpaidController.FinishAdAsync(activeAd, cancellationToken);

            var finished = true;
#else
            var finished = await VpaidController.PlayAdAsync(adUnit, cancellationToken);
#endif
            if (!finished)
            {
                // if approaching end has happened, retain a new task that will run to completion
                return(TaskHelpers.Create <Task>(async() =>
                {
                    await VpaidController.FinishAdAsync(adUnit, cancellationToken);
                    CleanupAd(adUnit);
                }));
            }
            else
            {
                CleanupAd(adUnit);
                return(null);
            }
        }
        PreloadOperation LoadAdUnit(ActiveAdUnit adUnit, CancellationToken cancellationToken)
        {
            var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
            Task <ActiveAdUnit> task;

            if (loadOperation != null)
            {
                if (loadOperation.AdSource != adUnit.AdSource)  // cancel active task and wait for it to finish
                {
                    task = TaskHelpers.Create <Task <ActiveAdUnit> >(async() =>
                    {
                        try
                        {
                            await loadOperation.CancelAsync();
                        }
                        finally
                        {
                            loadOperation = null;
                        }
                        cancellationToken.ThrowIfCancellationRequested();
                        return(await GetInitializationTask(adUnit, cts.Token));
                    });
                }
                else
                {
                    task = loadOperation.Task;
                }
            }
            else
            {
                task = GetInitializationTask(adUnit, cts.Token);
            }

            return(new PreloadOperation(task, adUnit.AdSource, cts));
        }
 void OnDeactivateAd(ActiveAdUnit activeAdUnit, Exception error)
 {
     if (DeactivateAdUnit != null)
     {
         DeactivateAdUnit(this, new DeactivateAdUnitEventArgs(activeAdUnit.CreativeSource, activeAdUnit.Player, activeAdUnit.CreativeConcept, activeAdUnit.AdSource, error));
     }
 }
        async Task StartAdUnitAsync(ActiveAdUnit adUnit, Ad ad, CancellationToken cancellationToken)
        {
            // start the ad
            await VpaidController.StartAdAsync(adUnit, cancellationToken);

            // fire the impression beacon
            foreach (var url in ad.Impressions)
            {
                VpaidController.TrackUrl(url, adUnit.CreativeSource);
            }
        }
예제 #7
0
 /// <summary>
 /// Stops the ad.
 /// </summary>
 /// <param name="ad">The ad creative that should stop playing</param>
 /// <param name="userState">A user state associated with this ad. This will be included with the various events associated with this ad.</param>
 public async Task StopAdAsync(ActiveAdUnit ad, CancellationToken cancellationToken)
 {
     try
     {
         await ad.Player.StopAdAsync(cancellationToken);
     }
     catch (Exception ex)
     {
         OnLog(new ActiveAdUnitLogEventArgs(ad, "VPAID.StopAd Exception: " + ex.Message));
         throw ex;
     }
 }
예제 #8
0
        public void AddAd(ActiveAdUnit ad)
        {
            if (activeAds.ContainsKey(ad.Player))
            {
                throw new Exception("Ad is already added");
            }

            activeAds.Add(ad.Player, ad);
            trackedProgressEvents.Add(ad, new List <TrackingEvent>());
            quartileHistory.Add(ad, new List <DateTime>());
            HookupPlayer(ad.Player);
        }
예제 #9
0
 /// <summary>
 /// Tells the ad to start.
 /// </summary>
 /// <param name="ad">The ad creative that should start playing</param>
 /// <param name="userState">A user state associated with this ad. This will be included with the various events associated with this ad.</param>
 public async Task StartAdAsync(ActiveAdUnit ad, double defaultVolume, CancellationToken cancellationToken)
 {
     try
     {
         ad.Player.AdVolume = defaultVolume;
         await ad.Player.StartAdAsync(cancellationToken);
     }
     catch (Exception ex)
     {
         OnLog(new ActiveAdUnitLogEventArgs(ad, "VPAID.StartAd Exception: " + ex.Message));
         throw ex;
     }
 }
예제 #10
0
        private void TrackProgress(ActiveAdUnit activeAdUnit, TimeSpan timeEllapsed, double percentCompleted)
        {
            var trackedEvents          = trackedProgressEvents[activeAdUnit];
            var eligableTrackingEvents = activeAdUnit.CreativeSource.TrackingEvents.Where(te => te.Type == TrackingType.Progress && te.Offset != null)
                                         .Except(trackedEvents)
                                         .Where(t => t.Offset.IsAbsolute ? timeEllapsed >= t.Offset.AbsoluteOffset : percentCompleted >= t.Offset.RelativeOffset);

            foreach (var trackingEvent in eligableTrackingEvents.ToList())
            {
                TrackUrl(trackingEvent.Value, activeAdUnit.CreativeSource);
                trackedEvents.Add(trackingEvent);
            }
        }
        async Task <ActiveAdUnit> GetInitializationTask(ActiveAdUnit adUnit, CancellationToken cancellationToken)
        {
            try
            {
                await VpaidController.InitAdAsync(adUnit, cancellationToken);

                cancellationToken.ThrowIfCancellationRequested();
            }
            catch
            {
                OnUnloadPlayer(adUnit.Player);
                throw;
            }
            return(adUnit);
        }
        void LoadAdUnit(ActiveAdUnit newAdUnit, ICollection <ICreative> creativeSet)
        {
            // load companions
            CompanionAdsRequired           required         = CompanionAdsRequired.None;
            IEnumerable <ICompanionSource> companionsToLoad = Enumerable.Empty <ICompanionSource>();
            var companions = creativeSet.OfType <CreativeCompanions>().FirstOrDefault();

            if (companions != null)
            {
                companionsToLoad = companions.Companions.Cast <ICompanionSource>();
                required         = companions.Required;
            }
            else
            {
                // VPAID 2.0 supports companions that come from the VPAID player.
                // these are only to be used with the VAST file contains none.
                if (newAdUnit.Player is IVpaid2)
                {
                    var companionData = ((IVpaid2)newAdUnit.Player).AdCompanions;
                    if (!string.IsNullOrEmpty(companionData))
                    {
                        CreativeCompanions vpaidCompanions;
                        using (var stream = companionData.ToStream())
                        {
                            vpaidCompanions = AdModelFactory.CreateCompanionsFromVast(stream);
                        }
                        companionsToLoad = vpaidCompanions.Companions.Cast <ICompanionSource>();
                        required         = vpaidCompanions.Required;
                    }
                }
            }

            try
            {
                OnActivateAd(newAdUnit, companionsToLoad, required);
            }
            catch (Exception ex)
            {
                throw new LoadException(ex);
            }
        }
예제 #13
0
        /// <summary>
        /// Loads a creative. This causes VPAID.InitAd to execute.
        /// </summary>
        /// <param name="ad">The creative to load</param>
        /// <param name="bitrate">The current bitrate of the player. This is passed onto the VPAID player which can use it to initialize itself.</param>
        /// <param name="userState">A user state associated with this ad. This will be included with the various events associated with this ad.</param>
        public async Task InitAdAsync(ActiveAdUnit ad, Size dimensions, bool isFullScreen, int desiredBitrate, CancellationToken cancellationToken)
        {
            try
            {
                await ad.Player.InitAdAsync(
                    dimensions.Width,
                    dimensions.Height,
                    isFullScreen? "fullscreen" : "normal",
                    desiredBitrate,
                    ad.CreativeSource.MediaSource,
                    ad.CreativeSource.ExtraInfo,
                    cancellationToken);

                cancellationToken.ThrowIfCancellationRequested();
            }
            catch (Exception ex)
            {
                OnLog(new ActiveAdUnitLogEventArgs(ad, "VPAID.AdInit Exception: " + ex.Message));
                throw ex;
            }
        }
예제 #14
0
        /// <summary>
        /// Removes the ad.
        /// </summary>
        /// <param name="ad">The ad creative that should be removed.</param>
        public void RemoveAd(ActiveAdUnit ad)
        {
            UnhookPlayer(ad.Player);
            trackedProgressEvents.Remove(ad);
            quartileHistory.Remove(ad);
            activeAds.Remove(ad.Player);

            if (ad.Player is IDisposable)
            {
                try
                {
                    ((IDisposable)ad.Player).Dispose();
                }
                catch (Exception ex)
                {
                    OnLog(new ActiveAdUnitLogEventArgs(ad, "VPAID.Dispose Exception: " + ex.Message));
                }
            }

            if (AdRemoved != null)
            {
                AdRemoved(this, new ActiveAdUnitEventArgs(ad));
            }
        }
        ActiveAdUnit CreateAdUnit(IEnumerable <ICreative> creativeSet, Ad ad, IAdSource adSource)
        {
            CreativeCompanions companions = null;
            var activeAdUnits             = new List <ActiveAdUnit>();

            foreach (var creative in creativeSet)
            {
                if (creative is CreativeLinear)
                {
                    var          linear             = (CreativeLinear)creative;
                    var          eligableMediaFiles = new Queue <MediaFile>(PrioritizeMedia(linear.MediaFiles));
                    ActiveAdUnit chosenAd           = null;
                    while (eligableMediaFiles.Any())
                    {
                        var media          = eligableMediaFiles.Dequeue();
                        var creativeSource = new LinearSource(linear, media);
                        var vpaid          = OnLoadPlayer(creativeSource);
                        if (vpaid != null)
                        {
                            if (VpaidController.Handshake(vpaid))
                            {
                                chosenAd = new ActiveAdUnit(creativeSource, vpaid, ad, adSource);
                                break;
                            }
                            else
                            {
                                OnUnloadPlayer(vpaid);
                            }
                        }
                    }

                    if (chosenAd == null)
                    {
                        throw new LoadException(new Exception("Unable to find a player to play the linear ad."));
                    }
                    activeAdUnits.Add(chosenAd);
                }
                else if (creative is CreativeNonLinears)
                {
                    bool found      = false;
                    var  nonLinears = (CreativeNonLinears)creative;
                    foreach (var nonLinear in (nonLinears).NonLinears)
                    {
                        var creativeSource = new NonLinearSource(nonLinear, nonLinears);
                        var vpaid          = OnLoadPlayer(creativeSource);
                        if (vpaid != null)
                        {
                            if (VpaidController.Handshake(vpaid))
                            {
                                activeAdUnits.Add(new ActiveAdUnit(creativeSource, vpaid, ad, adSource));
                                found = true;
                                break;
                            }
                            else
                            {
                                OnUnloadPlayer(vpaid);
                            }
                        }
                    }
                    if (!found)
                    {
                        throw new LoadException(new Exception("Unable to find a player to play any nonlinear ads."));
                    }
                }
                else if (creative is CreativeCompanions)
                {
                    companions = (CreativeCompanions)creative;
                }
                else /* not supported, ignore */ } {
        }
        return(activeAdUnits.FirstOrDefault());    // there should only be one linear or nonlinear ad in the sequenced group. Others are ignored.
    }
예제 #16
0
 internal ActiveAdUnitEventArgs(ActiveAdUnit activeAdUnit)
 {
     ActiveAdUnit = activeAdUnit;
 }
예제 #17
0
 internal ActiveAdUnitLogEventArgs(ActiveAdUnit activeAdUnit, string message)
     : base(activeAdUnit)
 {
     Message = message;
 }
예제 #18
0
 public Task InitAdAsync(ActiveAdUnit ad, CancellationToken cancellationToken)
 {
     return(base.InitAdAsync(ad, Player.Dimensions, Player.IsFullScreen, Player.CurrentBitrate, cancellationToken));
 }
예제 #19
0
 public Task StartAdAsync(ActiveAdUnit ad, CancellationToken cancellationToken)
 {
     return(base.StartAdAsync(ad, Player.IsMuted ? 0 : Player.Volume, cancellationToken));
 }
        async Task StartAdUnitAsync(ActiveAdUnit adUnit, Ad ad, CancellationToken cancellationToken)
        {
            // start the ad
            await VpaidController.StartAdAsync(adUnit, cancellationToken);

            // fire the impression beacon
            foreach (var url in ad.Impressions)
            {
                VpaidController.TrackUrl(url, adUnit.CreativeSource);
            }
        }
 internal ActiveAdUnitEventArgs(ActiveAdUnit activeAdUnit)
 {
     ActiveAdUnit = activeAdUnit;
 }
 void OnActivateAd(ActiveAdUnit activeAdUnit, IEnumerable<ICompanionSource> companions, CompanionAdsRequired suggestedCompanionRules)
 {
     if (ActivateAdUnit != null) ActivateAdUnit(this, new ActivateAdUnitEventArgs(activeAdUnit.CreativeSource, activeAdUnit.Player, activeAdUnit.CreativeConcept, activeAdUnit.AdSource, companions, suggestedCompanionRules));
 }
 void OnDeactivateAd(ActiveAdUnit activeAdUnit, Exception error)
 {
     if (DeactivateAdUnit != null) DeactivateAdUnit(this, new DeactivateAdUnitEventArgs(activeAdUnit.CreativeSource, activeAdUnit.Player, activeAdUnit.CreativeConcept, activeAdUnit.AdSource, error));
 }
 async Task<ActiveAdUnit> GetInitializationTask(ActiveAdUnit adUnit, CancellationToken cancellationToken)
 {
     try
     {
         await VpaidController.InitAdAsync(adUnit, cancellationToken);
         cancellationToken.ThrowIfCancellationRequested();
     }
     catch
     {
         OnUnloadPlayer(adUnit.Player);
         throw;
     }
     return adUnit;
 }
        ActiveAdUnit CreateAdUnit(IEnumerable<ICreative> creativeSet, Ad ad, IAdSource adSource)
        {
            CreativeCompanions companions = null;
            var activeAdUnits = new List<ActiveAdUnit>();
            foreach (var creative in creativeSet)
            {
                if (creative is CreativeLinear)
                {
                    var linear = (CreativeLinear)creative;
                    var eligableMediaFiles = new Queue<MediaFile>(PrioritizeMedia(linear.MediaFiles));
                    ActiveAdUnit chosenAd = null;
                    while (eligableMediaFiles.Any())
                    {
                        var media = eligableMediaFiles.Dequeue();
                        var creativeSource = new LinearSource(linear, media);
                        var vpaid = OnLoadPlayer(creativeSource);
                        if (vpaid != null)
                        {
                            if (VpaidController.Handshake(vpaid))
                            {
                                chosenAd = new ActiveAdUnit(creativeSource, vpaid, ad, adSource);
                                break;
                            }
                            else
                            {
                                OnUnloadPlayer(vpaid);
                            }
                        }
                    }

                    if (chosenAd == null) throw new LoadException(new Exception("Unable to find a player to play the linear ad."));
                    activeAdUnits.Add(chosenAd);
                }
                else if (creative is CreativeNonLinears)
                {
                    bool found = false;
                    var nonLinears = (CreativeNonLinears)creative;
                    foreach (var nonLinear in (nonLinears).NonLinears)
                    {
                        var creativeSource = new NonLinearSource(nonLinear, nonLinears);
                        var vpaid = OnLoadPlayer(creativeSource);
                        if (vpaid != null)
                        {
                            if (VpaidController.Handshake(vpaid))
                            {
                                activeAdUnits.Add(new ActiveAdUnit(creativeSource, vpaid, ad, adSource));
                                found = true;
                                break;
                            }
                            else
                            {
                                OnUnloadPlayer(vpaid);
                            }
                        }
                    }
                    if (!found)
                    {
                        throw new LoadException(new Exception("Unable to find a player to play any nonlinear ads."));
                    }
                }
                else if (creative is CreativeCompanions)
                {
                    companions = (CreativeCompanions)creative;
                }
                else { /* not supported, ignore */ }
            }
            return activeAdUnits.FirstOrDefault(); // there should only be one linear or nonlinear ad in the sequenced group. Others are ignored.
        }
        async Task <ActiveAdUnit> LoadAdUnitAsync(ICollection <ICreative> creativeSet, Ad ad, IAdSource adSource, CancellationToken cancellationToken)
        {
            bool         preloaded = false;
            ActiveAdUnit result    = null;

            // check to see if there are any pre-loading ads.
            if (loadOperation != null)
            {
                try
                {
                    if (loadOperation.AdSource == adSource)
                    {
                        cancellationToken.Register(() =>
                        {
                            if (loadOperation != null)
                            {
                                // no need to wait
                                var dummyTask = loadOperation.CancelAsync();
                            }
                        });
                        result    = await loadOperation.Task;
                        preloaded = true;
                    }
                    else
                    {
                        await loadOperation.CancelAsync();
                    }
                }
                catch { /* ignore, we'll try again regardless of reason, it was just a preload optimization anyway */ }
                finally
                {
                    loadOperation = null;
                }
                cancellationToken.ThrowIfCancellationRequested();
            }
            if (result == null)
            {
                result = CreateAdUnit(creativeSet, ad, adSource);
            }
            if (result == null)
            {
                throw new LoadException(new Exception("No ad unit could be created"));
            }

            if (!preloaded)
            {
                // Start initializing the ad. It is OK if there are other ads playing still.
                loadOperation = LoadAdUnit(result, cancellationToken);
                try
                {
                    await loadOperation.Task;
                }
                catch (OperationCanceledException) { throw; }
                catch (Exception ex) { throw new LoadException(ex); }
                finally
                {
                    loadOperation = null;
                }
                cancellationToken.ThrowIfCancellationRequested();
            }

            return(result);
        }
        /// <summary>
        /// Note: This method is re-entrant safe but not thread safe.
        /// </summary>
        /// <param name="adDocument">The ad document to execute.</param>
        /// <param name="adSource">The original ad source that the ad document came from.</param>
        /// <param name="startTimeout">The timeout for the ad</param>
        /// <param name="cancellationToken">A cancellation token to cancel the ad.</param>
        /// <param name="progress">Reports progress of the ad.</param>
        /// <returns>Two tasks that indicate when the ad is finished and another for when the ad is done with its linear portion</returns>
        PlayAdAsyncResult PlayAdAsync(AdDocumentPayload adDocument, IAdSource adSource, TimeSpan? startTimeout, CancellationToken cancellationToken, IProgress<AdStatus> progress)
        {
            // primary task wll hold a task that finishes when the ad document finishes or a non-linear ad starts.
            var primaryTask = new TaskCompletionSource<object>();

            // secondary task will hold a task that finishes when the entire ad document finishes.
            var secondaryTask = TaskHelpers.Create<Task>(async () =>
            {
                LoadException loadException = null; // keep around to re-throw in case we can't recover

                using (var timeoutTokenSource = new CancellationTokenSource())
                {
                    if (startTimeout.HasValue)
                    {
                        timeoutTokenSource.CancelAfter(startTimeout.Value);
                    }
                    var timeoutToken = timeoutTokenSource.Token;
                    using (var mainCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutToken))
                    {
                        var mainCancellationToken = mainCancellationTokenSource.Token;
                        // NOTE: mainCancellationToken is reset to just the initial token once we have successfully started an ad.

                        progress.Report(AdStatus.Opening);
                        try
                        {
                            try
                            {
                                bool podPlayed = false; // throw an exception if no ad ever played
                                // a task to hold the currently playing ad
                                Task activeAdTask = null;

                                foreach (var adPod in adDocument.AdPods)
                                {
                                    try
                                    {
                                        bool adPlayed = false; // throw an exception if no ad ever played
                                        // model expects ads to be in the correct order.
                                        foreach (var defaultAd in adPod.Ads)
                                        {
                                            try
                                            {
                                                var adCandidates = new List<Ad>();
                                                adCandidates.Add(defaultAd);
                                                adCandidates.AddRange(defaultAd.FallbackAds);
                                                foreach (var ad in adCandidates)
                                                {
                                                    try // **** try ad ****
                                                    {
                                                        // group the creatives by sequence number. Always put the group without sequence number at the back of the piority list in compliance with VAST spec.
                                                        foreach (var creativeSet in ad.Creatives.GroupBy(c => c.Sequence).OrderBy(cs => cs.Key.GetValueOrDefault(int.MaxValue)).Select(cs => cs.ToList()))
                                                        {
                                                            var newAdUnit = await LoadAdUnitAsync(creativeSet, ad, adSource, mainCancellationToken);

                                                            // if there is an ad playing, wait for it to finish before proceeding.
                                                            if (activeAdTask.IsRunning())
                                                            {
                                                                await activeAdTask;
                                                                mainCancellationToken.ThrowIfCancellationRequested(); // we can safely assume this is not a timeout and only check the token passed as a param
                                                            }

                                                            if (!newAdUnit.Player.AdLinear)
                                                            {
                                                                // if the next ad is nonlinear, we've successfully finished the primary task.
                                                                primaryTask.TrySetResult(null);
                                                            }

                                                            LoadAdUnit(newAdUnit, creativeSet);

                                                            adPlayed = true;
                                                            loadException = null; // if there was a load error, we recovered, reset.
                                                            progress.Report(AdStatus.Loaded);
                                                            activeAd = newAdUnit;
                                                            VpaidController.AddAd(activeAd);

                                                            try
                                                            {
                                                                await StartAdUnitAsync(newAdUnit, ad, mainCancellationToken);

                                                                // we successfully started an ad, create a new cancellation token that is not linked to timeout
                                                                mainCancellationToken = cancellationToken;
                                                                progress.Report(AdStatus.Playing);

                                                                // returns when task is over or approaching end
                                                                activeAdTask = await PlayAdUnitAsync(newAdUnit, mainCancellationToken);
                                                                if (activeAdTask != null)
                                                                {
                                                                    activeAdTask = activeAdTask.ContinueWith((Task t) =>
                                                                    {
                                                                        progress.Report(AdStatus.Complete);
                                                                        activeAd = null;
                                                                    }, TaskScheduler.FromCurrentSynchronizationContext());
                                                                }
                                                                else
                                                                {
                                                                    progress.Report(AdStatus.Complete);
                                                                    activeAd = null;
                                                                }
                                                            }
                                                            catch
                                                            {
                                                                CleanupAd(activeAd);
                                                                progress.Report(AdStatus.Complete);
                                                                activeAd = null;
                                                                throw;
                                                            }
                                                        }
                                                        break; // we successfully played an ad, break out of loop.
                                                    } // **** end try ad ****
                                                    catch (LoadException)
                                                    {
                                                        if (ad == adCandidates.Last()) throw; // ignore if it's not the last ad in order to go to next fallback ad 
                                                    }
                                                }
                                            }
                                            catch (LoadException)
                                            {
                                                if (defaultAd == adPod.Ads.Last()) throw; // ignore if it's not the last ad in the pod
                                            }
                                        }
                                        if (!adPlayed) throw new LoadException(new Exception("No ads found."));
                                        podPlayed = true;
                                        break;  // we should only play one adPod per the VAST spec
                                    }
                                    catch (LoadException ex)
                                    {
                                        // keep around to re-throw in case we can't successfully load an ad
                                        loadException = ex;
                                        // move to the next adpod, ignore for now. We'll log this later if it's relevant
                                    }
                                }
                                // always wait for the playing ad to complete before returning
                                if (activeAdTask.IsRunning())
                                {
                                    await activeAdTask;
                                }
                                if (!podPlayed)
                                {
                                    VpaidController.TrackErrorUrl(adDocument.Error, Microsoft.Media.Advertising.VpaidController.Error_NoAd);
                                    throw new LoadException(new Exception("No ads found."));
                                }
                            }
                            catch (OperationCanceledException)
                            {
                                if (timeoutToken.IsCancellationRequested) throw new TimeoutException(); else throw;
                            }
                            catch (Exception ex)
                            {
                                throw new PlayException(ex);
                            }
                            if (loadException != null)
                            {
                                throw loadException;
                            }
                        }
                        catch (Exception ex)
                        {
                            LogError(adSource, ex);
                            primaryTask.TrySetException(ex);
                            throw;
                        }
                        finally
                        {
                            progress.Report(AdStatus.Unloaded);
                        }
                        primaryTask.TrySetResult(null);
                    }
                }
            });

            return new PlayAdAsyncResult(primaryTask.Task, secondaryTask);
        }
        async Task<Task> PlayAdUnitAsync(ActiveAdUnit adUnit, CancellationToken cancellationToken)
        {
#if WINDOWS_PHONE7
            // WP doesn't support 3 MediaElements all with a source loaded. Instead of preloading, just wait until we finish the ad.
            await VpaidController.FinishAdAsync(activeAd, cancellationToken);
            var finished = true;
#else
            var finished = await VpaidController.PlayAdAsync(adUnit, cancellationToken);
#endif
            if (!finished)
            {
                // if approaching end has happened, retain a new task that will run to completion
                return TaskHelpers.Create<Task>(async () =>
                {
                    await VpaidController.FinishAdAsync(adUnit, cancellationToken);
                    CleanupAd(adUnit);
                });
            }
            else
            {
                CleanupAd(adUnit);
                return null;
            }
        }
 void CleanupAd(ActiveAdUnit adUnit)
 {
     OnDeactivateAd(adUnit, null);
     OnUnloadPlayer(adUnit.Player);
     VpaidController.RemoveAd(adUnit);
 }
 internal ActiveAdUnitLogEventArgs(ActiveAdUnit activeAdUnit, string message)
     : base(activeAdUnit)
 {
     Message = message;
 }
        PreloadOperation LoadAdUnit(ActiveAdUnit adUnit, CancellationToken cancellationToken)
        {
            var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
            Task<ActiveAdUnit> task;
            if (loadOperation != null)
            {
                if (loadOperation.AdSource != adUnit.AdSource)  // cancel active task and wait for it to finish
                {
                    task = TaskHelpers.Create<Task<ActiveAdUnit>>(async () =>
                    {
                        try
                        {
                            await loadOperation.CancelAsync();
                        }
                        finally
                        {
                            loadOperation = null;
                        }
                        cancellationToken.ThrowIfCancellationRequested();
                        return await GetInitializationTask(adUnit, cts.Token);
                    });
                }
                else
                {
                    task = loadOperation.Task;
                }
            }
            else
            {
                task = GetInitializationTask(adUnit, cts.Token);
            }

            return new PreloadOperation(task, adUnit.AdSource, cts);
        }
        void LoadAdUnit(ActiveAdUnit newAdUnit, ICollection<ICreative> creativeSet)
        {
            // load companions
            CompanionAdsRequired required = CompanionAdsRequired.None;
            IEnumerable<ICompanionSource> companionsToLoad = Enumerable.Empty<ICompanionSource>();
            var companions = creativeSet.OfType<CreativeCompanions>().FirstOrDefault();
            if (companions != null)
            {
                companionsToLoad = companions.Companions.Cast<ICompanionSource>();
                required = companions.Required;
            }
            else
            {
                // VPAID 2.0 supports companions that come from the VPAID player.
                // these are only to be used with the VAST file contains none.
                if (newAdUnit.Player is IVpaid2)
                {
                    var companionData = ((IVpaid2)newAdUnit.Player).AdCompanions;
                    if (!string.IsNullOrEmpty(companionData))
                    {
                        CreativeCompanions vpaidCompanions;
                        using (var stream = companionData.ToStream())
                        {
                            vpaidCompanions = AdModelFactory.CreateCompanionsFromVast(stream);
                        }
                        companionsToLoad = vpaidCompanions.Companions.Cast<ICompanionSource>();
                        required = vpaidCompanions.Required;
                    }
                }
            }

            try
            {
                OnActivateAd(newAdUnit, companionsToLoad, required);
            }
            catch (Exception ex)
            {
                throw new LoadException(ex);
            }
        }
 void CleanupAd(ActiveAdUnit adUnit)
 {
     OnDeactivateAd(adUnit, null);
     OnUnloadPlayer(adUnit.Player);
     VpaidController.RemoveAd(adUnit);
 }
        /// <summary>
        /// Note: This method is re-entrant safe but not thread safe.
        /// </summary>
        /// <param name="adDocument">The ad document to execute.</param>
        /// <param name="adSource">The original ad source that the ad document came from.</param>
        /// <param name="startTimeout">The timeout for the ad</param>
        /// <param name="cancellationToken">A cancellation token to cancel the ad.</param>
        /// <param name="progress">Reports progress of the ad.</param>
        /// <returns>Two tasks that indicate when the ad is finished and another for when the ad is done with its linear portion</returns>
        PlayAdAsyncResult PlayAdAsync(AdDocumentPayload adDocument, IAdSource adSource, TimeSpan?startTimeout, CancellationToken cancellationToken, IProgress <AdStatus> progress)
        {
            // primary task wll hold a task that finishes when the ad document finishes or a non-linear ad starts.
            var primaryTask = new TaskCompletionSource <object>();

            // secondary task will hold a task that finishes when the entire ad document finishes.
            var secondaryTask = TaskHelpers.Create <Task>(async() =>
            {
                LoadException loadException = null; // keep around to re-throw in case we can't recover

                using (var timeoutTokenSource = new CancellationTokenSource())
                {
                    if (startTimeout.HasValue)
                    {
                        timeoutTokenSource.CancelAfter(startTimeout.Value);
                    }
                    var timeoutToken = timeoutTokenSource.Token;
                    using (var mainCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutToken))
                    {
                        var mainCancellationToken = mainCancellationTokenSource.Token;
                        // NOTE: mainCancellationToken is reset to just the initial token once we have successfully started an ad.

                        progress.Report(AdStatus.Opening);
                        try
                        {
                            try
                            {
                                bool podPlayed = false; // throw an exception if no ad ever played
                                // a task to hold the currently playing ad
                                Task activeAdTask = null;

                                foreach (var adPod in adDocument.AdPods)
                                {
                                    try
                                    {
                                        bool adPlayed = false; // throw an exception if no ad ever played
                                        // model expects ads to be in the correct order.
                                        foreach (var defaultAd in adPod.Ads)
                                        {
                                            try
                                            {
                                                var adCandidates = new List <Ad>();
                                                adCandidates.Add(defaultAd);
                                                adCandidates.AddRange(defaultAd.FallbackAds);
                                                foreach (var ad in adCandidates)
                                                {
                                                    try // **** try ad ****
                                                    {
                                                        // group the creatives by sequence number. Always put the group without sequence number at the back of the piority list in compliance with VAST spec.
                                                        foreach (var creativeSet in ad.Creatives.GroupBy(c => c.Sequence).OrderBy(cs => cs.Key.GetValueOrDefault(int.MaxValue)).Select(cs => cs.ToList()))
                                                        {
                                                            var newAdUnit = await LoadAdUnitAsync(creativeSet, ad, adSource, mainCancellationToken);

                                                            // if there is an ad playing, wait for it to finish before proceeding.
                                                            if (activeAdTask.IsRunning())
                                                            {
                                                                await activeAdTask;
                                                                mainCancellationToken.ThrowIfCancellationRequested(); // we can safely assume this is not a timeout and only check the token passed as a param
                                                            }

                                                            if (!newAdUnit.Player.AdLinear)
                                                            {
                                                                // if the next ad is nonlinear, we've successfully finished the primary task.
                                                                primaryTask.TrySetResult(null);
                                                            }

                                                            LoadAdUnit(newAdUnit, creativeSet);

                                                            adPlayed      = true;
                                                            loadException = null; // if there was a load error, we recovered, reset.
                                                            progress.Report(AdStatus.Loaded);
                                                            activeAd = newAdUnit;
                                                            VpaidController.AddAd(activeAd);

                                                            try
                                                            {
                                                                await StartAdUnitAsync(newAdUnit, ad, mainCancellationToken);

                                                                // we successfully started an ad, create a new cancellation token that is not linked to timeout
                                                                mainCancellationToken = cancellationToken;
                                                                progress.Report(AdStatus.Playing);

                                                                // returns when task is over or approaching end
                                                                activeAdTask = await PlayAdUnitAsync(newAdUnit, mainCancellationToken);
                                                                if (activeAdTask != null)
                                                                {
                                                                    activeAdTask = activeAdTask.ContinueWith((Task t) =>
                                                                    {
                                                                        progress.Report(AdStatus.Complete);
                                                                        activeAd = null;
                                                                    }, TaskScheduler.FromCurrentSynchronizationContext());
                                                                }
                                                                else
                                                                {
                                                                    progress.Report(AdStatus.Complete);
                                                                    activeAd = null;
                                                                }
                                                            }
                                                            catch
                                                            {
                                                                CleanupAd(activeAd);
                                                                progress.Report(AdStatus.Complete);
                                                                activeAd = null;
                                                                throw;
                                                            }
                                                        }
                                                        break; // we successfully played an ad, break out of loop.
                                                    } // **** end try ad ****
                                                    catch (LoadException)
                                                    {
                                                        if (ad == adCandidates.Last())
                                                        {
                                                            throw;                            // ignore if it's not the last ad in order to go to next fallback ad
                                                        }
                                                    }
                                                }
                                            }
                                            catch (LoadException)
                                            {
                                                if (defaultAd == adPod.Ads.Last())
                                                {
                                                    throw;                                // ignore if it's not the last ad in the pod
                                                }
                                            }
                                        }
                                        if (!adPlayed)
                                        {
                                            throw new LoadException(new Exception("No ads found."));
                                        }
                                        podPlayed = true;
                                        break;  // we should only play one adPod per the VAST spec
                                    }
                                    catch (LoadException ex)
                                    {
                                        // keep around to re-throw in case we can't successfully load an ad
                                        loadException = ex;
                                        // move to the next adpod, ignore for now. We'll log this later if it's relevant
                                    }
                                }
                                // always wait for the playing ad to complete before returning
                                if (activeAdTask.IsRunning())
                                {
                                    await activeAdTask;
                                }
                                if (!podPlayed)
                                {
                                    VpaidController.TrackErrorUrl(adDocument.Error, Microsoft.Media.Advertising.VpaidController.Error_NoAd);
                                    throw new LoadException(new Exception("No ads found."));
                                }
                            }
                            catch (OperationCanceledException)
                            {
                                if (timeoutToken.IsCancellationRequested)
                                {
                                    throw new TimeoutException();
                                }
                                else
                                {
                                    throw;
                                }
                            }
                            catch (Exception ex)
                            {
                                throw new PlayException(ex);
                            }
                            if (loadException != null)
                            {
                                throw loadException;
                            }
                        }
                        catch (Exception ex)
                        {
                            LogError(adSource, ex);
                            primaryTask.TrySetException(ex);
                            throw;
                        }
                        finally
                        {
                            progress.Report(AdStatus.Unloaded);
                        }
                        primaryTask.TrySetResult(null);
                    }
                }
            });

            return(new PlayAdAsyncResult(primaryTask.Task, secondaryTask));
        }