Example #1
0
 internal static Task <object> GetApproachingEndTask(this IVpaid vpaid, CancellationToken cancellationToken)
 {
     return(TaskHelpers.FromEvent <object>(eh => vpaid.AdVideoThirdQuartile += eh, eh => vpaid.AdVideoThirdQuartile -= eh, cancellationToken));
 }
Example #2
0
 internal static Task <object> GetStoppedTask(this IVpaid vpaid, CancellationToken cancellationToken)
 {
     return(TaskHelpers.FromEvent <object>(eh => vpaid.AdStopped += eh, eh => vpaid.AdStopped -= eh, cancellationToken));
 }
Example #3
0
 internal static Task <Exception> GetErrorTask(this IVpaid vpaid, CancellationToken cancellationToken)
 {
     return(TaskHelpers.FromEvent <VpaidMessageEventArgs>(eh => vpaid.AdError += eh, eh => vpaid.AdError -= eh, cancellationToken).ContinueWith(t => new Exception(t.Result.Message), TaskContinuationOptions.OnlyOnRanToCompletion));
 }
Example #4
0
 internal static Task <EventArgs> GetLoadedTask(this IVpaid vpaid, CancellationToken cancellationToken)
 {
     return(TaskHelpers.FromEvent(eh => vpaid.AdLoaded += eh, eh => vpaid.AdLoaded -= eh, cancellationToken));
 }
        /// <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));
        }