Save video frame in JPEG XR (UWP C#)

Viewed 30

I'm new in C# coding and I would like to save a video frame in Jpeg-XR (16-bits image). I wrote some lines of code that should be close to the solution but I'm only able to correctly save a 8-bits image.

//Use Windows.Media.Editing to get ImageStream
            var clip = await MediaClip.CreateFromFileAsync(file);
            var composition = new MediaComposition();
            composition.Clips.Add(clip);

            var imageStream = await composition.GetThumbnailAsync(Position, (int)frameWidth, (int)frameHeight, VideoFramePrecision.NearestFrame);

            //generate bitmap 
            var writableBitmap = new WriteableBitmap((int)frameWidth, (int)frameHeight);
            writableBitmap.SetSource(imageStream);

            //generate some random name for file in PicturesLibrary
            var name = Position.Minutes.ToString() + "_" + Position.Seconds.ToString() + "_"+ Position.Milliseconds.ToString();
            var saveAsTarget = await KnownFolders.PicturesLibrary.CreateFileAsync(name + file.DisplayName + ".jxr");

            //get stream from bitmap
            const int BytesPerPixel = 4;
            Stream stream = writableBitmap.PixelBuffer.AsStream();
            byte[] pixels = new byte[(int)frameWidth * (int)frameHeight * BytesPerPixel];

            await stream.ReadAsync(pixels, 0, pixels.Length);

            using (var writeStream = await saveAsTarget.OpenAsync(FileAccessMode.ReadWrite))
            {
                var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegXREncoderId, writeStream);
                encoder.SetPixelData(
                    BitmapPixelFormat.Bgra8,
                    BitmapAlphaMode.Premultiplied,
                    (uint)writableBitmap.PixelWidth,
                    (uint)writableBitmap.PixelHeight,
                    96.0,
                    96.0,
                    pixels);
                await encoder.FlushAsync();

                using (var outputStream = writeStream.GetOutputStreamAt(0))
                {
                    await outputStream.FlushAsync();
                }
            }

I tried to set the BitmapPixelFormat to Rgba16 but the results is a wrongly encoded image. Any clue on how to solve my problems?

Alternative solutions to my current code are welcome. Thank you :)

1 Answers

You should not need to do that much pixel copying by hand, this example uses the System.Windows.Media.Imaging namespace. I have no idea if that is available in uwp or not:

public static void SaveJxr(this BitmapSource bitmap, Stream stream, byte qualityLevel = 80)
{
    var encoder = new WmpBitmapEncoder();
    encoder.QualityLevel = qualityLevel;
    encoder.Frames.Add(BitmapFrame.Create(bitmap));
    encoder.Save(stream);
}

This uses the Windows Media Photo encoder, and but as far as I can tell this is just an older name for Jpeg XR. I'm also fairly sure the produced images was actually 16bits when I tested this. But you also need a tool that can decode 16 bit images, and these do not seem very common.

If you control both the saving and reading side you might consider using JpegLS instead, at least that seem to give better results for mono16 images when I tested it.

Related