Exemplo n.º 1
0
        // Get the bitmap representing a video file
        // Note that displayIntent is ignored as it's specific to interaction with WCF's bitmap cache, which doesn't occur in rendering video preview frames
        public static BitmapSource GetBitmapFromVideoFile(string filePath, Nullable <int> desiredWidthOrHeight, ImageDisplayIntentEnum displayIntent, ImageDimensionEnum imageDimension, out bool isCorruptOrMissing)
        {
            if (!System.IO.File.Exists(filePath))
            {
                isCorruptOrMissing = true;
                return(Constant.ImageValues.FileNoLongerAvailable.Value);
            }
            // Our FFMPEG installation is the 64 bit version. In case someone is using a 32 bit machine, we use the MediaEncoder instead.
            if (Environment.Is64BitOperatingSystem == false)
            {
                // System.Diagnostics.Debug.Print("Can't use ffmpeg as this is a 32 bit machine. Using MediaEncoder instead");
                return(BitmapUtilities.GetVideoBitmapFromFileUsingMediaEncoder(filePath, desiredWidthOrHeight, displayIntent, imageDimension, out isCorruptOrMissing));
            }
            try
            {
                //Saul TO DO:
                // Note: not sure of the cost of creating a new converter every time. May be better to reuse it?
                Stream          outputBitmapAsStream = new MemoryStream();
                FFMpegConverter ffMpeg = new NReco.VideoConverter.FFMpegConverter();
                ffMpeg.GetVideoThumbnail(filePath, outputBitmapAsStream);

                // Scale the video to the desired dimension
                outputBitmapAsStream.Position = 0;
                BitmapImage bitmap = new BitmapImage();
                bitmap.BeginInit();
                if (desiredWidthOrHeight != null)
                {
                    if (imageDimension == ImageDimensionEnum.UseWidth)
                    {
                        bitmap.DecodePixelWidth = desiredWidthOrHeight.Value;
                    }
                    else
                    {
                        bitmap.DecodePixelHeight = desiredWidthOrHeight.Value;
                    }
                }
                bitmap.CacheOption  = BitmapCacheOption.None;
                bitmap.StreamSource = outputBitmapAsStream;
                bitmap.EndInit();
                bitmap.Freeze();
                isCorruptOrMissing = false;
                return(bitmap);
            }
            catch // (FFMpegException e)
            {
                // Couldn't get the thumbnail using FFMPEG. Fallback to try getting it using the MediaEncoder
                return(GetVideoBitmapFromFileUsingMediaEncoder(filePath, desiredWidthOrHeight, displayIntent, imageDimension, out isCorruptOrMissing));
                // We don't print the exception // (Exception exception)
                // TraceDebug.PrintMessage(String.Format("VideoRow/LoadBitmap: Loading of {0} failed in Video - LoadBitmap. {0}", imageFolderPath));
            }
        }
Exemplo n.º 2
0
        // All bitmap laoding eventually invokes this static function
        public static BitmapSource GetBitmapFromImageFile(string filePath, Nullable <int> desiredWidthOrHeight, ImageDisplayIntentEnum displayIntent, ImageDimensionEnum imageDimension, out bool isCorruptOrMissing)
        {
            isCorruptOrMissing = true;

            // BitmapCacheOption.None is significantly faster than other options.
            // However, it locks the file as it is being accessed (rather than a memory copy being created when using a cache)
            // This means we cannot do any file operations on it (such as deleting the currently displayed image) as it will produce an access violation.
            // This is ok for TransientLoading, which just temporarily displays the image
            BitmapCacheOption bitmapCacheOption = (displayIntent == ImageDisplayIntentEnum.Ephemeral) ? BitmapCacheOption.None : BitmapCacheOption.OnLoad;

            if (!System.IO.File.Exists(filePath))
            {
                return(Constant.ImageValues.FileNoLongerAvailable.Value);
            }
            try
            {
                // Exception workarounds to consider: see  http://faithlife.codes/blog/2010/07/exceptions_thrown_by_bitmapimage_and_bitmapframe/
                if (desiredWidthOrHeight.HasValue == false)
                {
                    // returns the full size bitmap
                    BitmapFrame frame = BitmapFrame.Create(new Uri(filePath), BitmapCreateOptions.None, bitmapCacheOption);
                    frame.Freeze();
                    isCorruptOrMissing = false;
                    return(frame);
                }

                BitmapImage bitmap = new BitmapImage();
                bitmap.BeginInit();
                if (imageDimension == ImageDimensionEnum.UseWidth)
                {
                    bitmap.DecodePixelWidth = desiredWidthOrHeight.Value;
                }
                else
                {
                    bitmap.DecodePixelHeight = desiredWidthOrHeight.Value;
                }
                bitmap.CacheOption = bitmapCacheOption;
                bitmap.UriSource   = new Uri(filePath);
                bitmap.EndInit();
                bitmap.Freeze();
                isCorruptOrMissing = false;
                return(bitmap);
            }
            catch (Exception exception)
            {
                // Optional messages for eventual debugging of catch errors,
                if (exception is InsufficientMemoryException)
                {
                    TracePrint.PrintMessage(String.Format("ImageRow/LoadBitmap: exception getting bitmap from file: {0}\n.** Insufficient Memory Exception: {1}.\n--------------\n**StackTrace: {2}.\nXXXXXXXXXXXXXX\n\n", filePath, exception.Message, exception.StackTrace));
                }
                else
                {
                    // TraceDebug.PrintMessage(String.Format("ImageRow/LoadBitmap: General exception: {0}\n.**Unkonwn exception getting bitmap from file: {1}.\n--------------\n**StackTrace: {2}.\nXXXXXXXXXXXXXX\n\n", filePath, exception.Message, exception.StackTrace));
                }
                isCorruptOrMissing = true;
                return(Constant.ImageValues.Corrupt.Value);
            }
        }
Exemplo n.º 3
0
 /// <summary>
 //// Load: Full form
 /// Get a bitmap of the desired width. If its not there or something is wrong it will return a placeholder bitmap displaying the 'error'.
 /// Also sets a flag (isCorruptOrMissing) indicating if the bitmap wasn't retrieved (signalling a placeholder bitmap was returned)
 /// </summary>
 public virtual BitmapSource LoadBitmap(string rootFolderPath, Nullable <int> desiredWidthOrHeight, ImageDisplayIntentEnum displayIntent, ImageDimensionEnum imageDimension, out bool isCorruptOrMissing)
 {
     // Invoke the static version. The only change is that we get the full file path and pass that as a parameter
     return(BitmapUtilities.GetBitmapFromImageFile(this.GetFilePath(rootFolderPath), desiredWidthOrHeight, displayIntent, imageDimension, out isCorruptOrMissing));
 }
Exemplo n.º 4
0
        // This alternate way to get an image from a video file used the media encoder.
        // While it works, its ~twice as slow as using NRECO FFMPeg.
        // We do include it as a fallback for the odd case where ffmpeg doesn't work (I had that with a single video).
        public static BitmapSource GetVideoBitmapFromFileUsingMediaEncoder(string filePath, Nullable <int> desiredWidth, ImageDisplayIntentEnum displayIntent, ImageDimensionEnum _, out bool isCorruptOrMissing)
        {
            isCorruptOrMissing = true;
            // System.Diagnostics.Debug.Print("FFMPEG failed for some reason, so using MediaEncoder Instead on " + filePath);

            if (!System.IO.File.Exists(filePath))
            {
                return(Constant.ImageValues.FileNoLongerAvailable.Value);
            }

            MediaPlayer mediaPlayer = new MediaPlayer
            {
                Volume = 0.0
            };

            try
            {
                // In this method, we open  mediaplayer and play it until we actually get a video frame.
                // Unfortunately, its very time inefficient...
                mediaPlayer.Open(new Uri(filePath));
                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

                // TODO Rapidly show videos as it is too slow now, where:
                // - ONLOAD It currently loads a blank video image when scouring thorugh the videos
                // - Rapid navigation: loads a blank video image in the background, then the video on pause
                // - Multiview: very slow as only loads the  video.
                // This will be fixed when we pre-process thumbnails
                int timesTried = (displayIntent == ImageDisplayIntentEnum.Persistent) ? 1000 : 0;
                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);
                    if (timesTried-- <= 0)
                    {
                        isCorruptOrMissing = false;
                        mediaPlayer.Stop();
                        return(BitmapUtilities.GetBitmapFromFileWithPlayButton("pack://application:,,,/Resources/BlankVideo.jpg", desiredWidth));
                    }
                }

                // 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 = 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);

                DrawingVisual drawingVisual = new DrawingVisual();
                using (DrawingContext drawingContext = drawingVisual.RenderOpen())
                {
                    drawingContext.DrawVideo(mediaPlayer, new Rect(0, 0, pixelWidth, pixelHeight));
                }

                // render and check for black frame
                // it's assumed the camera doesn't yield all black frames
                for (int renderAttempt = 1; renderAttempt <= Constant.ThrottleValues.MaximumRenderAttempts; ++renderAttempt)
                {
                    // try render
                    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, this
                    // is not an especially expensive operation relative to the  O(175ms) cost of this function
                    WriteableBitmap writeableBitmap = renderBitmap.AsWriteable();
                    if (writeableBitmap.IsBlack() == false)
                    {
                        // if the media player is closed before Render() only black is rendered
                        // TraceDebug.PrintMessage(String.Format("Video render returned a non-black frame after {0} times.", renderAttempt - 1));
                        mediaPlayer.Close();
                        isCorruptOrMissing = false;
                        mediaPlayer.Stop();
                        return(writeableBitmap);
                    }
                    // black frame was rendered; backoff slightly to try again
                    Thread.Sleep(TimeSpan.FromMilliseconds(Constant.ThrottleValues.VideoRenderingBackoffTime.TotalMilliseconds));
                }
                // We failed, so just return a blank video.
                mediaPlayer.Stop();
                return(BitmapUtilities.GetBitmapFromFileWithPlayButton("pack://application:,,,/Resources/BlankVideo.jpg", desiredWidth));
                //throw new ApplicationException(String.Format("Limit of {0} render attempts was reached.", Constant.ThrottleValues.MaximumRenderAttempts));
            }
            catch
            {
                // We don't print the exception // (Exception exception)
                // TraceDebug.PrintMessage(String.Format("VideoRow/LoadBitmap: Loading of {0} failed in Video - LoadBitmap. {0}", imageFolderPath));
                mediaPlayer.Stop();
                return(BitmapUtilities.GetBitmapFromFileWithPlayButton("pack://application:,,,/Resources/BlankVideo.jpg", desiredWidth));
            }
        }
Exemplo n.º 5
0
 /// <summary>
 /// Async Wrapper for LoadBitmap
 /// </summary>
 /// <returns>Tuple of the BitmapSource and boolean isCorruptOrMissing output of the underlying load logic</returns>
 public virtual Task <Tuple <BitmapSource, bool> > LoadBitmapAsync(string baseFolderPath, ImageDisplayIntentEnum imageExpectedUsage, ImageDimensionEnum imageDimension)
 {
     // 'out' arguments not allowed in tasks, so it returns a tuple containg the bitmap and the isCorruptOrMissingflag flag indicating bitmap retrieval state
     return(Task.Run(() =>
     {
         BitmapSource bitmap = this.LoadBitmap(baseFolderPath, imageExpectedUsage == ImageDisplayIntentEnum.Ephemeral ? (int?)Constant.ImageValues.PreviewWidth128 : null,
                                               imageExpectedUsage,
                                               ImageDimensionEnum.UseWidth,
                                               out bool isCorruptOrMissing);
         return Tuple.Create(bitmap, isCorruptOrMissing);
     }));
 }