예제 #1
0
        /// <summary>
        /// Construct.
        /// </summary>
        /// <param name="msMinDequeuePeriod">Minimum delay in milliseconds between dequeue events</param>
        /// <param name="scriptBitrate">Bits per second available in the current Windows Media script stream</param>
        public ScriptQueue(int msMinDequeuePeriod, int scriptBitrate, String baseUrl, String extent)
        {
            seenStudentSubmissions = new Hashtable();
            rtUpdate         = new RTUpdate();
            mainQueue        = new ArrayList();
            subQueue         = new ArrayList();
            lastSlideTime    = lastURLTime = lastScrollTime = DateTime.Now;
            lastEnqueueTime  = DateTime.MinValue;
            lastSlideIndex   = 0;
            lastDeckGuid     = Guid.Empty;
            minDequeuePeriod = msMinDequeuePeriod;

            this.scriptBitrate = scriptBitrate;
            maxBytesPerSecond  = scriptBitrate / 10; //theoretically we would divide by 8, but in practice we need a little extra safety factor.
            bwUpdateLockObject = new object();

            this.baseUrl = baseUrl;
            this.extent  = extent;
            lastUrl      = null;

            scEnqueueMutex     = new Mutex(false, "scEnqueue");
            DequeueThread      = new Thread(new ThreadStart(dequeueThread));
            DequeueThread.Name = "ScriptQueue dequeue thread";
            DequeueThread.SetApartmentState(ApartmentState.MTA); //Needed for WM interop
            DequeueThread.Start();
            LastRtUpdate = new RTUpdate();
            cp3Mgr       = new CP3Manager.CP3Manager();
        }
예제 #2
0
 /// <summary>
 /// We receive this message when an instructor navigates between slides in a deck.  There's also one that we
 /// want to ignore that occurs when the instructor opens a new deck but before navigating to a slide in the deck.
 /// </summary>
 /// <param name="stdm"></param>
 /// <returns></returns>
 internal object AddSlideDeckTraversal(CP3Msgs.SlideDeckTraversalMessage stdm)
 {
     //If m.Predecessor.Child is a TOC entry, look up info for RTUPdate there.
     if ((stdm.Predecessor != null) && (stdm.Predecessor.Child != null) &&
         (stdm.Predecessor is CP3Msgs.SlideInformationMessage) &&
         (stdm.Predecessor.Child is CP3Msgs.TableOfContentsEntryMessage) &&
         (stdm.Parent != null) && (stdm.Parent is CP3Msgs.DeckInformationMessage))
     {
         //Debug.WriteLine(stdm.Parent.Parent.ToString(), "*****");
         //We get one of these when a deck is first opened, but before the instructor navigates to it.  Filter it out
         //by remembering the current deck ID and ignoring messages for decks other than the current.
         if ((currentDeckId == Guid.Empty) || (currentDeckId.Equals((Guid)stdm.Parent.TargetId)))
         {
             currentSlideId = (Guid)stdm.Predecessor.TargetId;
             currentDeckId  = (Guid)stdm.Parent.TargetId;
             RTUpdate rtu = this.toc.GetRtUpdate((Guid)stdm.Predecessor.Child.TargetId);
             return(rtu);
         }
     }
     else
     {
         //No warning because this happens when a new SS deck is created upon receipt of the first submission.
         Debug.WriteLine("A SlideDeckTraversal is being ignored because it lacks the expected message graph.");
     }
     return(null);
 }
 internal RTUpdate GetRtUpdate(Guid tocKey)
 {
     if (this.toc.ContainsKey(tocKey))
     {
         TocEntry entry = (TocEntry)toc[tocKey];
         if (entry.DeckType == DeckTypeEnum.QuickPoll)
         {
             Debug.WriteLine("");
         }
         if (!UpdateAssociation(entry))
         {
             //If we failed to map a student submission to a deck it could be because the SS is on
             // a whiteboard page, or it could be that we don't yet have a TOC entry for the
             // original slide.  For now, just treat both cases as if
             // they were whiteboard.
             // P2: Make a way to mark the message as SS and leave the deck association empty, and
             // have that be understood as a SS on a WB.  I think this requires an update to WebViewer?
             RTUpdate rtu = entry.ToRtUpdate();
             rtu.DeckType = (int)DeckTypeEnum.Whiteboard;
             return(rtu);
         }
         return(entry.ToRtUpdate());
     }
     return(null);
 }
예제 #4
0
 private void UpdateRTUpdate(RTUpdate rtu)
 {
     lock (rtUpdate) {
         rtUpdate         = rtu;
         rtUpdate.BaseUrl = baseUrl;
         rtUpdate.Extent  = extent;
     }
 }
예제 #5
0
        private void enqueueMain(WorkItem wi, RTUpdate rtu)
        {
            lock (mainQueue)
                mainQueue.Add(wi);

            if (wi.Type != "URL")
            {
                if (OnEnqueue != null)
                {
                    ScriptEventArgs dea = new ScriptEventArgs(Convert.ToBase64String(wi.BC.Buffer, wi.BC.Index, wi.BC.Length), wi.Type);
                    OnEnqueue(this, dea);
                }
            }
        }
예제 #6
0
        /// <summary>
        /// Fetch image from web for Presenter 2 data
        /// </summary>
        /// <param name="o"></param>
        private void GetWebImageThread2(object o)
        {
            RTUpdate rtu = (RTUpdate)o;

            if ((rtu.BaseUrl == null) || (rtu.Extent == null))
            {
                return;
            }

            if (rtu.DeckGuid == Guid.Empty)
            {
                return;
            }

            System.Drawing.Image tmpImage;

            string imgURL;

            Guid pDeckGuid    = rtu.DeckGuid;
            int  pSlideNumber = rtu.SlideIndex + 1;

            /// Student submissions need to map to a presentation deck and slide.
            if ((rtu.DeckType == (Int32)DeckTypeEnum.StudentSubmission) ||
                (rtu.DeckType == (Int32)DeckTypeEnum.QuickPoll))
            {
                pDeckGuid    = rtu.DeckAssociation;
                pSlideNumber = rtu.SlideAssociation + 1;
            }

            //implicit assumption that rtu.BaseUrl includes the trailing '/'.
            imgURL = rtu.BaseUrl + pDeckGuid.ToString() + "/slide" + pSlideNumber.ToString() + "." + rtu.Extent;

            try
            {
                tmpImage = System.Drawing.Image.FromStream(webClient.OpenRead(imgURL));
            }
            catch
            {
                parent.LoggerWriteInvoke("Exception while opening image url: " + imgURL);
                return;
            }
            //put the new image in the SlideDeck.
            int internalIndex = slideMap.GetMapping(rtu.SlideIndex, rtu.DeckGuid);

            slideDeck.SetSlide(internalIndex, new Slide(new Bitmap(tmpImage), "Slide " + internalIndex.ToString()));

            // display the newly loaded slide.
            DisplaySlideIndex(internalIndex);  // the latter two params are not relevant here.
        }
            internal RTUpdate ToRtUpdate()
            {
                RTUpdate rtu = new RTUpdate();

                rtu.DeckGuid   = deckId;
                rtu.SlideIndex = slideIndex;
                rtu.DeckType   = (int)deckType;
                rtu.SlideSize  = slideSize;

                //background colors can be attached to any slide and deck, but they only show
                //on whiteboards, or on the area around a minimized slide.
                if (!backgroundColor.Equals(Color.Empty))
                {
                    //Explicitly set slide color has top priority
                    rtu.BackgroundColor = backgroundColor;
                }
                else if ((TableOfContents.TheInstance.DeckBackgroundColors.ContainsKey(deckId)) &&
                         (!TableOfContents.TheInstance.DeckBackgroundColors[deckId].Equals(Color.Empty)))
                {
                    //DeckBackground color is checked next.
                    rtu.BackgroundColor = TableOfContents.TheInstance.DeckBackgroundColors[deckId];
                }
                else
                {
                    //If none are set, default to white.
                    rtu.BackgroundColor = Color.White;
                }

                if ((deckType == DeckTypeEnum.StudentSubmission) ||
                    (deckType == DeckTypeEnum.QuickPoll))
                {
                    rtu.DeckAssociation     = deckAssociation;
                    rtu.SlideAssociation    = slideAssociation;
                    rtu.DeckTypeAssociation = (int)deckTypeAssociation;
                }
                return(rtu);
            }
예제 #8
0
        public static void CopyRtUpdate(RTUpdate from, ref RTUpdate to)
        {
            if (from == null)
            {
                return;
            }

            if (to == null)
            {
                return;
            }

            to.BackgroundColor  = from.BackgroundColor;
            to.BaseUrl          = from.BaseUrl;
            to.DeckAssociation  = from.DeckAssociation;
            to.DeckGuid         = from.DeckGuid;
            to.DeckType         = from.DeckType;
            to.Extent           = from.Extent;
            to.ScrollExtent     = from.ScrollExtent;
            to.ScrollPosition   = from.ScrollPosition;
            to.SlideAssociation = from.SlideAssociation;
            to.SlideIndex       = from.SlideIndex;
            to.SlideSize        = from.SlideSize;
        }
예제 #9
0
        public static bool RtUpdatesEqual(RTUpdate rtu1, RTUpdate rtu2)
        {
            if ((rtu1 == null) || (rtu2 == null))
            {
                return(false);
            }

            if ((rtu1.BackgroundColor != rtu2.BackgroundColor) ||
                (rtu1.BaseUrl != rtu2.BaseUrl) ||
                (rtu1.DeckAssociation != rtu2.DeckAssociation) ||
                (rtu1.DeckGuid != rtu2.DeckGuid) ||
                (rtu1.DeckType != rtu2.DeckType) ||
                (rtu1.Extent != rtu2.Extent) ||
                (rtu1.ScrollExtent != rtu2.ScrollExtent) ||
                (rtu1.ScrollPosition != rtu2.ScrollPosition) ||
                (rtu1.SlideAssociation != rtu2.SlideAssociation) ||
                (rtu1.SlideIndex != rtu2.SlideIndex) ||
                (rtu1.SlideSize != rtu2.SlideSize))
            {
                return(false);
            }

            return(true);
        }
예제 #10
0
        /// <summary>
        /// This is a message used in the beacon, and it is also the message that indicates when
        /// an instructor navigates to an initial slide in a newly opened deck.
        /// </summary>
        /// <param name="icdtcm"></param>
        /// <returns></returns>
        /// This is the beacon message graph we expect:
        /// {InstructorMessage;
        ///     child={InstructorCurrentDeckTraversalChangedMessage;
        ///         pred={SlideInformationMessage;
        ///             pred={InstructorCurrentPresentationChangedMessage;};
        ///             child={TableOfContentsEntryMessage;};};};}
        internal List <object> AddInstructorCurrentDeckTraversalChanged(UW.ClassroomPresenter.Network.Messages.Network.InstructorCurrentDeckTraversalChangedMessage icdtcm)
        {
            //If m.Predecessor.Child is a TOC entry, return a RTUPdate.
            List <object> outputMessages = null;

            if ((icdtcm.Predecessor != null) && (icdtcm.Predecessor.Child != null) &&
                (icdtcm.Predecessor is CP3Msgs.SlideInformationMessage) &&
                (icdtcm.Predecessor.Child is CP3Msgs.TableOfContentsEntryMessage))
            {
                if (icdtcm.Predecessor.Predecessor == null)   //This effectively filters out the beacon messages.
                //In some late-joiner scenarios, we may not have the TOC entry.
                {
                    if (!toc.ContainsEntry((Guid)icdtcm.Predecessor.Child.TargetId))
                    {
                        string err;
                        Guid   slideId = toc.AddTableOfContentsEntry((CP3Msgs.TableOfContentsEntryMessage)icdtcm.Predecessor.Child,
                                                                     icdtcm.DeckId, icdtcm.Dispositon, out err);
                        if (err != null)
                        {
                            Debug.WriteLine(err);
                        }
                        else if (!slideId.Equals(Guid.Empty))
                        {
                            outputMessages = this.getCachedStrokes(slideId);
                        }
                    }
                    if (currentDeckId.Equals(icdtcm.DeckId) && currentSlideId.Equals((Guid)icdtcm.Predecessor.TargetId))
                    {
                        Debug.WriteLine("***Ignoring InstructorCurrentDeckTraversalChanged because it matches the current slide and deck");
                        return(outputMessages);
                    }
                    RTUpdate rtu = this.toc.GetRtUpdate((Guid)icdtcm.Predecessor.Child.TargetId);
                    if (rtu != null)
                    {
                        currentDeckId  = icdtcm.DeckId;
                        currentSlideId = (Guid)icdtcm.Predecessor.TargetId;
                    }
                    else
                    {
                        Debug.WriteLine("Warning: Navigation failure.");
                    }
                    if (outputMessages == null)
                    {
                        outputMessages = new List <object>();
                    }
                    outputMessages.Add(rtu);
                    return(outputMessages);
                }
                else
                {
                    //The beacon also causes navigation in some cases, eg. the initial slide after we join.
                    //If the beacon has a toc entry we don't already have, add it.
                    if (!toc.ContainsEntry((Guid)icdtcm.Predecessor.Child.TargetId))
                    {
                        string err;
                        Guid   slideId = toc.AddTableOfContentsEntry((CP3Msgs.TableOfContentsEntryMessage)icdtcm.Predecessor.Child,
                                                                     icdtcm.DeckId, icdtcm.Dispositon, out err);
                        if (err != null)
                        {
                            Debug.WriteLine(err);
                        }
                        else if (!slideId.Equals(Guid.Empty))
                        {
                            outputMessages = this.getCachedStrokes(slideId);
                        }
                    }
                    //if the beacon indicates a slide other than the current slide, navigate there.
                    if ((!currentSlideId.Equals((Guid)icdtcm.Predecessor.TargetId)) ||
                        (!currentDeckId.Equals(icdtcm.DeckId)))
                    {
                        currentSlideId = (Guid)icdtcm.Predecessor.TargetId;
                        RTUpdate rtu = this.toc.GetRtUpdate((Guid)icdtcm.Predecessor.Child.TargetId);
                        currentDeckId  = (Guid)icdtcm.DeckId;
                        currentSlideId = (Guid)icdtcm.Predecessor.TargetId;
                        if (outputMessages == null)
                        {
                            outputMessages = new List <object>();
                        }
                        outputMessages.Add(rtu);
                        return(outputMessages);
                    }
                }
            }
            else
            {
                warning += "Warning: Found InstructorCurrentDeckTraversalChangedMessage message without a TOC Entry.  ";
            }
            return(outputMessages);
        }
예제 #11
0
        /// <summary>
        /// Filter the contents of the existing tempdir using the specified
        /// difference threshold.  Rewrite tempdir, metadata and differenceMetrics.
        /// </summary>
        /// <param name="threshold"></param>
        internal void Refilter(double threshold)
        {
            //Make an imageFilter instance and configure with the new threshold.
            ImageFilter.ImageFilter imgFilter = null;
            try {
                imgFilter = new ImageFilter.ImageFilter();
                imgFilter.DifferenceThreshold = threshold;
            }
            catch {
                return;
            }
            Console.WriteLine("Refiltering framegrabs using threshold: " + threshold.ToString());

            List <double> newDifferenceMetrics          = new List <double>();
            List <PresentationMgr.DataItem> newMetadata = new List <PresentationMgr.DataItem>();
            RTUpdate rtu = new RTUpdate();

            this.deckGuid = Guid.NewGuid();
            rtu.DeckGuid  = this.deckGuid;
            rtu.SlideSize = 1.0;
            rtu.DeckType  = (Int32)DeckTypeEnum.Presentation;
            string filebase  = "slide";
            string extent    = ".jpg";
            int    fileindex = 1;

            try {
                string newDir = Utility.GetTempDir();
                Directory.CreateDirectory(newDir);
                string[] files = Directory.GetFiles(this.tempdir, "*.jpg");
                Array.Sort(files, new PMPComparer());
                string previous = null;
                for (int i = 0; i < files.Length; i++)
                {
                    string file = files[i];
                    string msg;
                    bool   error = false;
                    double metric;

                    if (imgFilter.ImagesDiffer(previous, file, out msg, out error, out metric))
                    {
                        string newFilename = filebase + fileindex.ToString() + extent;
                        File.Copy(file, Path.Combine(newDir, newFilename));
                        rtu.SlideIndex = fileindex - 1; // This is a zero-based index
                        newMetadata.Add(new PresentationMgr.DataItem(metadata[i].Timestamp, PresentationMgr.CopyRTUpdate(rtu)));
                        newDifferenceMetrics.Add(metric);
                        DateTime t = new DateTime(metadata[i].Timestamp);
                        Console.WriteLine("Refilter keeping old index: " + (i + 1).ToString() +
                                          "; new index: " + fileindex.ToString() +
                                          "; difference: " + metric.ToString() + "; time: " + t.ToString());
                        fileindex++;
                        previous = file;
                    }
                }
                this.metadata          = newMetadata;
                this.differenceMetrics = newDifferenceMetrics;
                Directory.Delete(this.tempdir, true);
                this.tempdir = newDir;
            }
            catch (Exception) {
                return;
            }
        }
예제 #12
0
        /// <summary>
        /// Capture stills from the video stream.  Create a temporary directory and save the images there.
        /// Compile a list of DataItem objects to indicate when the slide transitions should take place.
        /// </summary>
        /// <returns></returns>
        public string Process()
        {
            ImageFilter.ImageFilter imgFilter = null;
            try {
                imgFilter = new ImageFilter.ImageFilter();
                imgFilter.DifferenceThreshold = this.differenceThreshold;
            }
            catch {
                this.errorLog.Append("Video capture images in the presentation will not be filtered probably " +
                                     "because ImageMagick is not available in the configuration.\r\n");
            }

            this.differenceMetrics = new List <double>();
            metadata = new List <PresentationMgr.DataItem>();
            RTUpdate rtu = new RTUpdate();

            this.deckGuid = Guid.NewGuid();
            rtu.DeckGuid  = this.deckGuid;
            rtu.SlideSize = 1.0;
            rtu.DeckType  = (Int32)DeckTypeEnum.Presentation;

            this.videoStream = new StreamMgr(videoDescriptor.VideoCname, videoDescriptor.VideoName, new  DateTime(this.start), new DateTime(this.end), false, PayloadType.dynamicVideo);
            this.videoStream.ToRawWMFile(this.progressTracker);
            MediaTypeVideoInfo mtvi = videoStream.GetUncompressedVideoMediaType();

            this.tempdir = Utility.GetTempDir();
            Directory.CreateDirectory(this.tempdir);
            string filebase  = "slide";
            string extent    = ".jpg";
            int    fileindex = 1;

            BufferChunk bc;
            long        time;
            bool        newStream;
            string      previousFile = null;

            this.stopNow = false;
            while ((!stopNow) && (videoStream.GetNextSample(out bc, out time, out newStream)))
            {
                if ((time - lastFramegrab) >= (long)(this.frameGrabIntervalMs * Constants.TicksPerMs))
                {
                    DateTime dt = new DateTime(time);
                    Debug.WriteLine("time=" + dt.ToString() + ";length=" + bc.Length.ToString());
                    lastFramegrab = time;
                    string      filepath    = Path.Combine(tempdir, filebase + fileindex.ToString() + extent);
                    PixelFormat pixelFormat = subtypeToPixelFormat(mtvi.SubType);
                    Bitmap      bm          = new Bitmap(mtvi.VideoInfo.BitmapInfo.Width, mtvi.VideoInfo.BitmapInfo.Height, pixelFormat);
                    BitmapData  bd          = bm.LockBits(new Rectangle(0, 0, mtvi.VideoInfo.BitmapInfo.Width, mtvi.VideoInfo.BitmapInfo.Height), ImageLockMode.ReadWrite, pixelFormat);
                    Marshal.Copy(bc.Buffer, 0, bd.Scan0, bc.Length);
                    bm.UnlockBits(bd);
                    bm.RotateFlip(RotateFlipType.RotateNoneFlipY);
                    if ((SCALE) && (mtvi.VideoInfo.BitmapInfo.Width >= 1280))
                    {
                        int    w      = mtvi.VideoInfo.BitmapInfo.Width / 2;
                        int    h      = mtvi.VideoInfo.BitmapInfo.Height / 2;
                        Bitmap scaled = new Bitmap(bm, new Size(w, h));
                        scaled.Save(filepath, ImageFormat.Jpeg);
                    }
                    else
                    {
                        bm.Save(filepath, ImageFormat.Jpeg);
                    }
                    if (imgFilter != null)
                    {
                        string filterMsg;
                        bool   filterError;
                        double metric;
                        bool   differ = imgFilter.ImagesDiffer(filepath, previousFile, out filterMsg, out filterError, out metric);
                        if (filterError)
                        {
                            //this.errorLog.Append(filterMsg);
                            Console.WriteLine(filterMsg);
                        }
                        if (!differ)
                        {
                            continue;
                        }
                        this.differenceMetrics.Add(metric);
                        Console.WriteLine("Framegrab slide index: " + fileindex.ToString() +
                                          "; difference: " + metric.ToString() + "; time: " + dt.ToString());
                    }
                    rtu.SlideIndex = fileindex - 1; // This is a zero-based index
                    metadata.Add(new PresentationMgr.DataItem(time - this.offset, PresentationMgr.CopyRTUpdate(rtu)));
                    fileindex++;
                    previousFile = filepath;
                }
            }
            return(null);
        }