Ejemplo n.º 1
0
        public async override Task<BitmapSource> LoadBitmapAsync(string imageFolderPath, Nullable<int> desiredWidth)
        {
            string path = this.GetFilePath(imageFolderPath);
            if (!File.Exists(path))
            {
                return Constant.Images.FileNoLongerAvailable.Value;
            }

            BitmapSource firstFrame = await Task.Run(() =>
            {
                for (int renderAttempt = 0; renderAttempt < Constant.ThrottleValues.MaximumRenderAttempts; ++renderAttempt)
                {
                    MediaPlayer mediaPlayer = new MediaPlayer();
                    mediaPlayer.Volume = 0.0;
                    try
                    {
                        mediaPlayer.Open(new Uri(path));
                        mediaPlayer.Play();

                        // MediaPlayer is not actually synchronous despite exposing synchronous APIs, so wait for it get the video loaded.  Otherwise
                        // the width and height properties are zero and only black pixels are drawn.  The properties will populate with just a call to
                        // Open() call but without also Play() only black is rendered.  It would be preferable to hook, say, mediaPlayer.MediaOpened
                        // for this purpose but the event doesn't seem to be fired.
                        while ((mediaPlayer.NaturalVideoWidth < 1) || (mediaPlayer.NaturalVideoHeight < 1))
                        {
                            // back off briefly to let MediaPlayer do its loading, which typically takes perhaps 75ms
                            // a brief Sleep() is used rather than Yield() to reduce overhead as 500k to 1M+ yields typically occur
                            Thread.Sleep(Constant.ThrottleValues.PollIntervalForVideoLoad);
                        }

                        // sleep one more time as MediaPlayer has a tendency to still return black frames for a moment after the width and height have populated
                        Thread.Sleep(Constant.ThrottleValues.PollIntervalForVideoLoad);

                        int pixelWidth = mediaPlayer.NaturalVideoWidth;
                        int pixelHeight = mediaPlayer.NaturalVideoHeight;
                        if (desiredWidth.HasValue)
                        {
                            double scaling = (double)desiredWidth.Value / (double)pixelWidth;
                            pixelWidth = (int)(scaling * pixelWidth);
                            pixelHeight = (int)(scaling * pixelHeight);
                        }

                        // set up to render frame from the video
                        mediaPlayer.Pause();
                        mediaPlayer.Position = TimeSpan.FromMilliseconds(1.0);

                        // render and check for black frame
                        // it's assumed the camera doesn't yield all black frames
                        DrawingVisual drawingVisual = new DrawingVisual();
                        for (int blackFrameAttempt = 1; blackFrameAttempt <= Constant.ThrottleValues.MaximumBlackFrameAttempts; ++blackFrameAttempt)
                        {
                            // try render
                            // creating the DrawingContext insie the loop but persisting the DrawingVisual seems to produce the highest success rate.
                            using (DrawingContext drawingContext = drawingVisual.RenderOpen())
                            {
                                drawingContext.DrawVideo(mediaPlayer, new Rect(0, 0, pixelWidth, pixelHeight));
                                RenderTargetBitmap renderBitmap = new RenderTargetBitmap(pixelWidth, pixelHeight, 96, 96, PixelFormats.Default);
                                renderBitmap.Render(drawingVisual);
                                renderBitmap.Freeze();

                                // check if render succeeded
                                // hopefully it did and most of the overhead here is WriteableBitmap conversion though, at 2-3ms for a 1280x720 frame, black 
                                // checking is not an especially expensive operation relative to the O(175ms) cost of this function
                                WriteableBitmap writeableBitmap = renderBitmap.AsWriteable();
                                if (writeableBitmap.IsBlack() == false)
                                {
                                    // Debug.Print("Video frame succeeded: render attempt {0}, black frame attempt {1}.", renderAttempt, blackFrameAttempt);
                                    // If the media player is closed before Render() only black is rendered.
                                    // If the WriteableBitmap isn't cast the compiler can't figure out the delegate's return type.
                                    mediaPlayer.Close();
                                    return (BitmapSource)writeableBitmap;
                                }
                            }

                            // black frame was rendered; apply linear backoff and try again
                            Thread.Sleep(TimeSpan.FromMilliseconds(Constant.ThrottleValues.RenderingBackoffTime.TotalMilliseconds * renderAttempt));
                        }
                    }
                    catch (Exception exception)
                    {
                        Debug.Fail(String.Format("Loading of {0} failed.", this.FileName), exception.ToString());
                        return Constant.Images.CorruptFile.Value;
                    }

                    mediaPlayer.Close();
                }

                throw new ApplicationException(String.Format("Limit of {0} render attempts reached.", Constant.ThrottleValues.MaximumRenderAttempts));
            });

            return firstFrame;
        }