/// <summary>
 /// Preloads an ad that will be played later.
 /// </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="cancellationToken">A cancellation token to cancel the ad.</param>
 /// <returns></returns>
 async Task PreloadAdAsync(AdDocumentPayload adDocument, IAdSource adSource, CancellationToken cancellationToken)
 {
     foreach (var adPod in adDocument.AdPods)
     {
         try
         {
             // model expects ads to be in the correct order.
             foreach (var ad in adPod.Ads)
             {
                 // 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)))
                 {
                     var newAdUnit = CreateAdUnit(creativeSet, ad, adSource);
                     if (newAdUnit != null) // a violation of the VAST spec but we will just ignore
                     {
                         loadOperation = LoadAdUnit(newAdUnit, cancellationToken);
                         try
                         {
                             await loadOperation.Task;
                             cancellationToken.ThrowIfCancellationRequested();
                         }
                         catch
                         {
                             loadOperation = null;
                             throw;
                         }
                         return;
                     }
                 }
             }
         }
         catch (LoadException) { /* ignore, move to the next adpod */ }
     }
 }
        private static IEnumerable<ICreative> GetSubsequentCreatives(IDocumentCreativeSource creativeSource, Ad creativeConcept, AdDocumentPayload payload)
        {
            if (creativeSource == null) throw new ArgumentNullException("creativeSource");
            if (payload == null) throw new ArgumentNullException("payload");
            if (creativeConcept == null) throw new ArgumentNullException("creativeConcept");

            var adPod = payload.AdPods.FirstOrDefault(ap => ap.Ads.Contains(creativeConcept));
            if (adPod != null)
            {
                return adPod.Ads
                    .SelectMany(a => a.Creatives.OrderBy(c => c.Sequence.GetValueOrDefault(int.MaxValue)))
                    .Where(c => !(c is CreativeCompanions))
                    .SkipWhile(c => c != creativeSource.Creative)
                    .Skip(1);
            }
            return null;
        }
        private static AdDocumentPayload CreateDocument(IClipAdPayload mediaSource)
        {
            var adDocument     = new AdDocumentPayload();
            var adPod          = new AdPod();
            var ad             = new Ad();
            var linearCreative = new CreativeLinear();

            linearCreative.ClickThrough = mediaSource.ClickThrough;
            linearCreative.MediaFiles.Add(new MediaFile()
            {
                Type = mediaSource.MimeType, Value = mediaSource.MediaSource
            });
            ad.Creatives.Add(linearCreative);
            adPod.Ads.Add(ad);
            adDocument.AdPods.Add(adPod);
            return(adDocument);
        }
 private static AdDocumentPayload CreateDocument(IClipAdPayload mediaSource)
 {
     var adDocument = new AdDocumentPayload();
     var adPod = new AdPod();
     var ad = new Ad();
     var linearCreative = new CreativeLinear();
     linearCreative.ClickThrough = mediaSource.ClickThrough;
     linearCreative.MediaFiles.Add(new MediaFile() { Type = mediaSource.MimeType, Value = mediaSource.MediaSource });
     ad.Creatives.Add(linearCreative);
     adPod.Ads.Add(ad);
     adDocument.AdPods.Add(adPod);
     return adDocument;
 }
        internal static async Task<AdDocumentPayload> CreateFromVast(Stream stream, int? maxRedirectDepth, bool allowMultipleAds)
#endif
        {
            XDocument xDoc = XDocument.Load(stream);
            XElement vastRoot = xDoc.Element("VAST");
            if (vastRoot == null)
            {
                vastRoot = xDoc.Element("VideoAdServingTemplate");
                if (vastRoot == null) throw new NotImplementedException();
                return await CreateFromVast1(vastRoot, maxRedirectDepth, allowMultipleAds);
            }
            else
            {
                var result = new AdDocumentPayload();
                result.Version = (string)vastRoot.Attribute("version");
                result.Error = (string)vastRoot.Element("Error");

                var eligableAds = vastRoot.Elements("Ad");
                if (!allowMultipleAds)
                {
                    eligableAds = eligableAds.Where(va => string.IsNullOrEmpty((string)va.Attribute("sequence")));
                }
                foreach (var vastAdPod in eligableAds.GroupBy(va => va.Attribute("sequence") != null ? 1 : int.MaxValue).OrderBy(vap => vap.Key))
                {
                    var adPod = new AdPod();
                    foreach (var vastAd in vastAdPod.OrderBy(va => ToNullableInt((string)va.Attribute("sequence")).GetValueOrDefault(0)))
                    {
                        var ad = new Ad();
                        ad.Id = (string)vastAd.Attribute("id");

                        if (vastAd.Elements("InLine").Any())
                        {
                            var vastAdInline = vastAd.Element("InLine");

                            ad.AdSystem = GetAdSystem(vastAdInline.Element("AdSystem"));

                            ad.Advertiser = (string)vastAdInline.Element("Advertiser");
                            ad.Description = (string)vastAdInline.Element("Description");
                            var error = (string)vastAdInline.Element("Error");
                            if (error != null) ad.Errors.Add(error);
                            ad.Title = (string)vastAdInline.Element("AdTitle");
                            ad.Survey = GetUriValue(vastAdInline.Element("Survey"));

                            ad.Pricing = new Pricing();
                            var pricing = vastAdInline.Element("Pricing");
                            if (pricing != null)
                            {
                                ad.Pricing.Currency = (string)pricing.Attribute("currency");
                                ad.Pricing.Model = (PricingModel)Enum.Parse(typeof(PricingModel), (string)pricing.Attribute("model"), true);
                                ad.Pricing.Value = Convert.ToDouble((string)pricing);
                            }

                            foreach (var vastImpression in vastAdInline.Elements("Impression"))
                            {
                                ad.Impressions.Add((string)vastImpression);
                            }

                            if (vastAdInline.Elements("Extensions").Any())
                            {
                                foreach (var vastExtension in vastAdInline.Element("Extensions").Elements("Extension"))
                                {
                                    ad.Extensions.Add(new Extension()); // TODO
                                }
                            }

                            LoadCreatives(vastAdInline, ad);

                            adPod.Ads.Add(ad);
                        }
                        else if (vastAd.Elements("Wrapper").Any())
                        {
                            Ad wrapper = new Ad();
                            var vastAdWrapper = vastAd.Element("Wrapper");

                            // parse the wrapper itself
                            wrapper.AdSystem = GetAdSystem(vastAdWrapper.Element("AdSystem"));
                            var error = (string)vastAdWrapper.Element("Error");
                            if (error != null) wrapper.Errors.Add(error);

                            foreach (var vastImpression in vastAdWrapper.Elements("Impression"))
                            {
                                wrapper.Impressions.Add((string)vastImpression);
                            }

                            LoadCreatives(vastAdWrapper, wrapper);

                            if (vastAdWrapper.Elements("Extensions").Any())
                            {
                                foreach (var vastExtension in vastAdWrapper.Element("Extensions").Elements("Extension"))
                                {
                                    ad.Extensions.Add(new Extension()); // TODO
                                }
                            }

                            AdDocumentPayload wrappedVastDoc = null;
                            var vastAdUri = GetUriValue(vastAdWrapper.Element("VASTAdTagURI"));
                            if (vastAdUri != null && (!maxRedirectDepth.HasValue || maxRedirectDepth.Value > 0))
                            {
                                try
                                {
                                    // load the stream from the web
                                    using (var s = await Extensions.LoadStreamAsync(vastAdUri))
                                    {
                                        var newAllowMultipleAds = vastAdWrapper.GetBoolAttribute("allowMultipleAds", allowMultipleAds);
                                        var followAdditionalWrappers = vastAdWrapper.GetBoolAttribute("followAdditionalWrappers", true);
                                        int? nextMaxRedirectDepth = followAdditionalWrappers ? (maxRedirectDepth.HasValue ? maxRedirectDepth.Value - 1 : maxRedirectDepth) : 0;
                                        wrappedVastDoc = await CreateFromVast(s, nextMaxRedirectDepth, newAllowMultipleAds);
                                    }
                                }
                                catch { /* swallow */ }
                            }

                            AdPod wrappedAdPod = null;
                            if (wrappedVastDoc != null)
                            {
                                wrappedAdPod = wrappedVastDoc.AdPods.FirstOrDefault();
                            }

                            if (wrappedAdPod == null || !wrappedAdPod.Ads.Any())
                            {
                                // no ads were returned
                                var fallbackOnNoAd = vastAdWrapper.GetBoolAttribute("fallbackOnNoAd", true);
                                if (fallbackOnNoAd)
                                {
                                    wrappedAdPod = FallbackAdPod;
                                }
                            }

                            if (wrappedAdPod != null)
                            {
                                // merge tracking info from this wrapper to every ad in the first adpod in the child
                                foreach (Ad inlineAd in wrappedAdPod.Ads)
                                    MergeWrappedAd(wrapper, inlineAd);

                                // add each ad from the first adpod in the child to the current adpod
                                foreach (Ad inlineAd in wrappedAdPod.Ads)
                                    adPod.Ads.Add(inlineAd);
                            }
                        }
                    }
                    result.AdPods.Add(adPod);
                }
                return result;
            }
        }
Exemplo n.º 6
0
        internal static async Task <AdDocumentPayload> CreateFromVast(Stream stream, int?maxRedirectDepth, bool allowMultipleAds)
#endif
        {
            XDocument xDoc     = XDocument.Load(stream);
            XElement  vastRoot = xDoc.Element("VAST");

            if (vastRoot == null)
            {
                vastRoot = xDoc.Element("VideoAdServingTemplate");
                if (vastRoot == null)
                {
                    throw new NotImplementedException();
                }
                return(await CreateFromVast1(vastRoot, maxRedirectDepth, allowMultipleAds));
            }
            else
            {
                var result = new AdDocumentPayload();
                result.Version = (string)vastRoot.Attribute("version");
                result.Error   = (string)vastRoot.Element("Error");

                var eligableAds = vastRoot.Elements("Ad");
                if (!allowMultipleAds)
                {
                    eligableAds = eligableAds.Where(va => string.IsNullOrEmpty((string)va.Attribute("sequence")));
                }
                foreach (var vastAdPod in eligableAds.GroupBy(va => va.Attribute("sequence") != null ? 1 : int.MaxValue).OrderBy(vap => vap.Key))
                {
                    var adPod = new AdPod();
                    foreach (var vastAd in vastAdPod.OrderBy(va => ToNullableInt((string)va.Attribute("sequence")).GetValueOrDefault(0)))
                    {
                        var ad = new Ad();
                        ad.Id = (string)vastAd.Attribute("id");

                        if (vastAd.Elements("InLine").Any())
                        {
                            var vastAdInline = vastAd.Element("InLine");

                            ad.AdSystem = GetAdSystem(vastAdInline.Element("AdSystem"));

                            ad.Advertiser  = (string)vastAdInline.Element("Advertiser");
                            ad.Description = (string)vastAdInline.Element("Description");
                            var error = (string)vastAdInline.Element("Error");
                            if (error != null)
                            {
                                ad.Errors.Add(error);
                            }
                            ad.Title  = (string)vastAdInline.Element("AdTitle");
                            ad.Survey = GetUriValue(vastAdInline.Element("Survey"));

                            ad.Pricing = new Pricing();
                            var pricing = vastAdInline.Element("Pricing");
                            if (pricing != null)
                            {
                                ad.Pricing.Currency = (string)pricing.Attribute("currency");
                                ad.Pricing.Model    = (PricingModel)Enum.Parse(typeof(PricingModel), (string)pricing.Attribute("model"), true);
                                ad.Pricing.Value    = Convert.ToDouble((string)pricing);
                            }

                            foreach (var vastImpression in vastAdInline.Elements("Impression"))
                            {
                                ad.Impressions.Add((string)vastImpression);
                            }

                            if (vastAdInline.Elements("Extensions").Any())
                            {
                                foreach (var vastExtension in vastAdInline.Element("Extensions").Elements("Extension"))
                                {
                                    ad.Extensions.Add(new Extension()); // TODO
                                }
                            }

                            LoadCreatives(vastAdInline, ad);

                            adPod.Ads.Add(ad);
                        }
                        else if (vastAd.Elements("Wrapper").Any())
                        {
                            Ad  wrapper       = new Ad();
                            var vastAdWrapper = vastAd.Element("Wrapper");

                            // parse the wrapper itself
                            wrapper.AdSystem = GetAdSystem(vastAdWrapper.Element("AdSystem"));
                            var error = (string)vastAdWrapper.Element("Error");
                            if (error != null)
                            {
                                wrapper.Errors.Add(error);
                            }

                            foreach (var vastImpression in vastAdWrapper.Elements("Impression"))
                            {
                                wrapper.Impressions.Add((string)vastImpression);
                            }

                            LoadCreatives(vastAdWrapper, wrapper);

                            if (vastAdWrapper.Elements("Extensions").Any())
                            {
                                foreach (var vastExtension in vastAdWrapper.Element("Extensions").Elements("Extension"))
                                {
                                    ad.Extensions.Add(new Extension()); // TODO
                                }
                            }

                            AdDocumentPayload wrappedVastDoc = null;
                            var vastAdUri = GetUriValue(vastAdWrapper.Element("VASTAdTagURI"));
                            if (vastAdUri != null && (!maxRedirectDepth.HasValue || maxRedirectDepth.Value > 0))
                            {
                                try
                                {
                                    // load the stream from the web
                                    using (var s = await Extensions.LoadStreamAsync(vastAdUri))
                                    {
                                        var newAllowMultipleAds      = vastAdWrapper.GetBoolAttribute("allowMultipleAds", allowMultipleAds);
                                        var followAdditionalWrappers = vastAdWrapper.GetBoolAttribute("followAdditionalWrappers", true);
                                        int?nextMaxRedirectDepth     = followAdditionalWrappers ? (maxRedirectDepth.HasValue ? maxRedirectDepth.Value - 1 : maxRedirectDepth) : 0;
                                        wrappedVastDoc = await CreateFromVast(s, nextMaxRedirectDepth, newAllowMultipleAds);
                                    }
                                }
                                catch { /* swallow */ }
                            }

                            AdPod wrappedAdPod = null;
                            if (wrappedVastDoc != null)
                            {
                                wrappedAdPod = wrappedVastDoc.AdPods.FirstOrDefault();
                            }

                            if (wrappedAdPod == null || !wrappedAdPod.Ads.Any())
                            {
                                // no ads were returned
                                var fallbackOnNoAd = vastAdWrapper.GetBoolAttribute("fallbackOnNoAd", true);
                                if (fallbackOnNoAd)
                                {
                                    wrappedAdPod = FallbackAdPod;
                                }
                            }

                            if (wrappedAdPod != null)
                            {
                                // merge tracking info from this wrapper to every ad in the first adpod in the child
                                foreach (Ad inlineAd in wrappedAdPod.Ads)
                                {
                                    MergeWrappedAd(wrapper, inlineAd);
                                }

                                // add each ad from the first adpod in the child to the current adpod
                                foreach (Ad inlineAd in wrappedAdPod.Ads)
                                {
                                    adPod.Ads.Add(inlineAd);
                                }
                            }
                        }
                    }
                    result.AdPods.Add(adPod);
                }
                return(result);
            }
        }
        internal static async Task<AdDocumentPayload> CreateFromVast1(XElement vastRoot, int? maxRedirectDepth, bool allowMultipleAds)
        {
            var result = new AdDocumentPayload();
            result.Version = (string)vastRoot.Attribute("version");

            foreach (var vastAdPod in vastRoot.Elements("Ad").GroupBy(va => va.Attribute("sequence") != null ? 1 : int.MaxValue).OrderBy(vap => vap.Key))
            {
                var adPod = new AdPod();
                foreach (var vastAd in vastAdPod.OrderBy(va => ToNullableInt((string)va.Attribute("sequence")).GetValueOrDefault(0)))
                {
                    var ad = new Ad();
                    ad.Id = (string)vastAd.Attribute("id");

                    if (vastAd.Elements("InLine").Any())
                    {
                        throw new NotImplementedException();
                    }
                    else if (vastAd.Elements("Wrapper").Any())
                    {
                        Ad wrapper = new Ad();
                        var vastAdWrapper = vastAd.Element("Wrapper");

                        // parse the wrapper itself
                        wrapper.AdSystem = GetAdSystem(vastAdWrapper.Element("AdSystem"));
                        var error = (string)vastAdWrapper.Element("Error");
                        if (error == null) wrapper.Errors.Add(error);

                        var linearCreative = new CreativeLinear();

                        foreach (var trackingEvent in GetTrackingEvents(vastAdWrapper))
                            linearCreative.TrackingEvents.Add(trackingEvent);

                        LoadVideoClicks(vastAdWrapper, linearCreative);

                        wrapper.Creatives.Add(linearCreative);

                        var vastAdUri = GetUriValue(vastAdWrapper.Element("VASTAdTagURL"));
                        if (vastAdUri != null)
                        {
                            // load the stream from the web
                            using (var s = await Extensions.LoadStreamAsync(vastAdUri))
                            {
                                int? nextMaxRedirectDepth = maxRedirectDepth.HasValue ? maxRedirectDepth.Value - 1 : maxRedirectDepth;
                                var vastDoc = await CreateFromVast(s, nextMaxRedirectDepth, allowMultipleAds);

                                var firstAdPodInChild = vastDoc.AdPods.FirstOrDefault();

                                if (firstAdPodInChild != null)
                                {
                                    // merge tracking info from this wrapper to every ad in the first adpod in the child
                                    foreach (Ad inlineAd in firstAdPodInChild.Ads)
                                        MergeWrappedAd(wrapper, inlineAd);

                                    // add each ad from the first adpod in the child to the current adpod
                                    foreach (Ad inlineAd in firstAdPodInChild.Ads)
                                        adPod.Ads.Add(inlineAd);
                                }
                            }
                        }
                    }
                }
                result.AdPods.Add(adPod);
            }
            return result;
        }
        public static async Task<AdDocumentPayload> GetAdDocumentPayload(FWTemporalAdSlot adSlot, FWAdResponse adResponse, CancellationToken c)
#endif
        {
            var payload = new AdDocumentPayload();
            var adPod = new AdPod();
            payload.AdPods.Add(adPod);
            foreach (var adReference in adSlot.SelectedAds)
            {
                var ad = await CreateAd(adResponse, adReference);
                adPod.Ads.Add(ad);

                foreach (var fallbackAdReference in adReference.FallbackAds)
                {
                    var fallbackAd = await CreateAd(adResponse, fallbackAdReference);
                    ad.FallbackAds.Add(fallbackAd);
                }
            }
            return payload;
        }
 /// <summary>
 /// Preloads an ad that will be played later.
 /// </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="cancellationToken">A cancellation token to cancel the ad.</param>
 /// <returns></returns>
 async Task PreloadAdAsync(AdDocumentPayload adDocument, IAdSource adSource, CancellationToken cancellationToken)
 {
     foreach (var adPod in adDocument.AdPods)
     {
         try
         {
             // model expects ads to be in the correct order.
             foreach (var ad in adPod.Ads)
             {
                 // 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)))
                 {
                     var newAdUnit = CreateAdUnit(creativeSet, ad, adSource);
                     if (newAdUnit != null) // a violation of the VAST spec but we will just ignore
                     {
                         loadOperation = LoadAdUnit(newAdUnit, cancellationToken);
                         try
                         {
                             await loadOperation.Task;
                             cancellationToken.ThrowIfCancellationRequested();
                         }
                         catch
                         {
                             loadOperation = null;
                             throw;
                         }
                         return;
                     }
                 }
             }
         }
         catch (LoadException) { /* ignore, move to the next adpod */ }
     }
 }
        /// <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);
        }
Exemplo n.º 11
0
        internal static async Task <AdDocumentPayload> CreateFromVast1(XElement vastRoot, int?maxRedirectDepth, bool allowMultipleAds)
        {
            var result = new AdDocumentPayload();

            result.Version = (string)vastRoot.Attribute("version");

            foreach (var vastAdPod in vastRoot.Elements("Ad").GroupBy(va => va.Attribute("sequence") != null ? 1 : int.MaxValue).OrderBy(vap => vap.Key))
            {
                var adPod = new AdPod();
                foreach (var vastAd in vastAdPod.OrderBy(va => ToNullableInt((string)va.Attribute("sequence")).GetValueOrDefault(0)))
                {
                    var ad = new Ad();
                    ad.Id = (string)vastAd.Attribute("id");

                    if (vastAd.Elements("InLine").Any())
                    {
                        throw new NotImplementedException();
                    }
                    else if (vastAd.Elements("Wrapper").Any())
                    {
                        Ad  wrapper       = new Ad();
                        var vastAdWrapper = vastAd.Element("Wrapper");

                        // parse the wrapper itself
                        wrapper.AdSystem = GetAdSystem(vastAdWrapper.Element("AdSystem"));
                        var error = (string)vastAdWrapper.Element("Error");
                        if (error == null)
                        {
                            wrapper.Errors.Add(error);
                        }

                        var linearCreative = new CreativeLinear();

                        foreach (var trackingEvent in GetTrackingEvents(vastAdWrapper))
                        {
                            linearCreative.TrackingEvents.Add(trackingEvent);
                        }

                        LoadVideoClicks(vastAdWrapper, linearCreative);

                        wrapper.Creatives.Add(linearCreative);

                        var vastAdUri = GetUriValue(vastAdWrapper.Element("VASTAdTagURL"));
                        if (vastAdUri != null)
                        {
                            // load the stream from the web
                            using (var s = await Extensions.LoadStreamAsync(vastAdUri))
                            {
                                int?nextMaxRedirectDepth = maxRedirectDepth.HasValue ? maxRedirectDepth.Value - 1 : maxRedirectDepth;
                                var vastDoc = await CreateFromVast(s, nextMaxRedirectDepth, allowMultipleAds);

                                var firstAdPodInChild = vastDoc.AdPods.FirstOrDefault();

                                if (firstAdPodInChild != null)
                                {
                                    // merge tracking info from this wrapper to every ad in the first adpod in the child
                                    foreach (Ad inlineAd in firstAdPodInChild.Ads)
                                    {
                                        MergeWrappedAd(wrapper, inlineAd);
                                    }

                                    // add each ad from the first adpod in the child to the current adpod
                                    foreach (Ad inlineAd in firstAdPodInChild.Ads)
                                    {
                                        adPod.Ads.Add(inlineAd);
                                    }
                                }
                            }
                        }
                    }
                }
                result.AdPods.Add(adPod);
            }
            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));
        }