StorageItemThumbnail to BitmapImage converter fails with MediaCapture mp4

0

I am using a ValueConverter to get the thumbnail for an m4 file that was recorded by directly with WinRT's MediaCapture. After much debugging and alternate approaches, I've settle on the converter code below. I am getting the following error The component cannot be found. (Exception from HRESULT: 0x88982F50) on the GetThumbnailAsync method.

I have confirmed that the thumbnail is being shown for the video in the Xbox Video app and the file explorer app when I use CopyTo(KnownFolders.VideosLibrary).

The converter seems to work fine when it's an external video file, but not with one of my app's mp4s. Is there something wrong with my converter or can you reproduce this?

SEE UPDATE 1 I try to get the thumbnail when the file is first created, same error occurs.

public class ThumbnailToBitmapImageConverter : IValueConverter
{
    readonly StorageFolder localFolder = ApplicationData.Current.LocalFolder;
    BitmapImage image;

    public object Convert(object value, Type targetType, object parameter, string language)
    {
        if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
            return "images/ExamplePhoto2.jpg";

        if (value == null)
            return "";

        var fileName = (string)value;

        if (string.IsNullOrEmpty(fileName))
            return "";

        var bmi = new BitmapImage();
        bmi.SetSource(Thumb(fileName).Result);
        return bmi;
    }

    private async Task<StorageItemThumbnail> Thumb(string fileName)
    {
        try
        {
            var file = await localFolder.GetFileAsync(fileName)
                .AsTask().ConfigureAwait(false);
            var thumbnail = await file.GetScaledImageAsThumbnailAsync(ThumbnailMode.ListView, 90, ThumbnailOptions.UseCurrentScale)
                .AsTask().ConfigureAwait(false);
            return thumbnail;
        }
        catch (Exception ex)
        {
            new MessageDialog(ex.Message).ShowAsync();
        }
        return null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

UPDATE 1

I decided to go back to where I save the video to a file and grab the thumbnail there, then save it to an image for use later. I get the same error, here is the code for grabbing and saving the thumbnail after the video is saved:

var thumb = await videoStorageFile.GetThumbnailAsync(ThumbnailMode.ListView);
var buffer = new Windows.Storage.Streams.Buffer(Convert.ToUInt32(thumb.Size));
var thumbBuffer = await thumb.ReadAsync(buffer, buffer.Capacity, InputStreamOptions.None);
using (var str = await thumbImageFile.OpenAsync(FileAccessMode.ReadWrite))
{
      await str.WriteAsync(thumbBuffer);
}
windows-runtime
winrt-xaml
windows-phone-8.1
asked on Stack Overflow Aug 25, 2014 by Lance McCarthy • edited Aug 26, 2014 by Lance McCarthy

1 Answer

2

I have not tested this out, but It should work. In your model that you are binding to, replace the property for your thumbnail with a new class named Thumbnail. Bind to that property rather than your video location. When the video location changes, create a new thumbnail.

public class VideoViewModel : INotifyPropertyChanged
{
    public string VideoLocation
    {
        get { return _location; }
        set
        {
            _location = value;
            Thumbnail = new Thumbnail(value);
            OnPropertyChanged();
        }
    }

    public Thumbnail Thumbnail
    {
        get { return _thumbnail; }
        set
        {
            _thumbnail = value;
            OnPropertyChanged();
        }
    }
}

The Thumbnail class. This is just a shell, ready for you to fill out the rest

public class Thumbnail : INotifyPropertyChanged
{
    public Thumbnail(string location)
    {
        Image = GetThumbFromVideoAsync(location);
    }

    private Task<BitMapSource> GetThumbFromVideoAsync(string location)
    {
        BitMapSource result;
        // decode

        // set it again to force
        Image = Task.FromResult(result);
    }

    public Task<BitMapSource> Image
    {
        get { return _image; }
        private set
        {
            _image = value;
            OnPropertyChanged();
        }
    }
}

You can still have a value converter in place. It would check if the task has completed, if it has not, then show some default image. If the task has faulted, it can show some error image:

public class ThumbnailToBitmapImageConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        var thumbnail = value as Thumbnail;
        if (thumbnail == null) return GetDefaultBitmap();

        if (thumbnail.Image.IsCompleted == false) return GetDefaultBitmap();
        if (thumbnail.Image.IsFaulted) return GetBadImage();

        return thumbnail.Image.Result;
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }

    private BitMapSource GetDefaultBitmap()
    {
        // load a default image
    }

    private BitMapSource GetBadImage()
    {
        // load a ! image
    }
}
answered on Stack Overflow Aug 25, 2014 by Shawn Kendrot

User contributions licensed under CC BY-SA 3.0