radek-k / FFMediaToolkit

FFMediaToolkit is a cross-platform video decoder/encoder library for .NET that uses FFmpeg native libraries. It supports video frames extraction, reading stream metadata and creating videos from bitmaps in any format supported by FFmpeg.
MIT License
352 stars 56 forks source link

How to transfer ImageData to sprite or texture2D? #117

Closed VictorZhanUnity closed 2 years ago

VictorZhanUnity commented 2 years ago

I get frame data of the Video. It's type of ImageData.

and I want to show it in the scene of Unity. (Image, RawImage, Texture2D... etc)

How to transfer it to sprite or texture2D?

VictorZhanUnity commented 2 years ago

I try it with the code down below, It worked, but the color is not right and image is upside down.

image

        texture = new Texture2D(1920, 1038, TextureFormat.RGB24, false);
        var videoFile = MediaFile.Open(VideoURL);
        // Gets the frame at 5th second of the video.
        ImageData frame5s = videoFile.Video.GetFrame(TimeSpan.FromSeconds(sec));
        byte[] data = frame5s.Data.ToArray();
        texture.LoadRawTextureData(data);
        texture.Apply();
        meshRenderer.material.mainTexture = texture;
        rawImage.texture = texture;
radek-k commented 2 years ago

You have to set MediaOptions.VideoPixelFormat to Rgb24. To fix the flipped image, set texture tiling or scale (-1,1). This is a strange issue that only happens in Unity. You can also allocate memory for decoded video frames once to improve performance. Simple example:

using UnityEngine;
using FFMediaToolkit;
using FFMediaToolkit.Decoding;
using FFMediaToolkit.Graphics;
using System.Runtime.InteropServices;
using System;

public class VideoPlayerExample : MonoBehaviour
{
    Texture2D texture;
    MeshRenderer meshRenderer;

    IntPtr buffer;
    int bufferSize;
    int frameWidth;
    int frameHeight;

    MediaFile videoFile;
    bool canReadNextFrame = true;

    public string videoURL;
    public string ffmpegPath;

    void Start()
    {
        FFmpegLoader.FFmpegPath = ffmpegPath;
        FFmpegLoader.LoadFFmpeg();

        meshRenderer = GetComponent<MeshRenderer>();

        var options = new MediaOptions();
        options.VideoPixelFormat = ImagePixelFormat.Rgb24;
        videoFile = MediaFile.Open(videoURL, options);
        frameWidth = videoFile.Video.Info.FrameSize.Width;
        frameHeight = videoFile.Video.Info.FrameSize.Height;

        texture = new Texture2D(frameWidth, frameHeight, TextureFormat.RGB24, false);
        meshRenderer.material.mainTexture = texture;
        meshRenderer.material.mainTextureScale = new Vector2(-1, 1);

        bufferSize = frameWidth * frameHeight * 3;
        buffer = Marshal.AllocHGlobal(bufferSize);
    }

    void Update()
    {
        if (canReadNextFrame)
        {
            canReadNextFrame = videoFile.Video.TryGetNextFrame(buffer, bufferSize / frameHeight);
            texture.LoadRawTextureData(buffer, bufferSize);
            texture.Apply();
        }
    }

    private void OnDestroy()
    {
        Marshal.FreeHGlobal(buffer);
        videoFile.Dispose();
    }
}
VictorZhanUnity commented 2 years ago

Thanks a lot, it worked fine.