Unity Bink player

I have been asked to play a bink video in Unity. I googled a lot and couldn’t find any solution. Then I decided to write my own one and here is my solution to integrate Bink video into Unity.

BinkNativeMethods

    internal static class BinkNativeMethods
    {
        //=======================================================================

        public const ulong BINKOLDFRAMEFORMAT = 0x00008000L; // using the old Bink frame format (internal use only)
        public const ulong BINKCUSTOMCOLORSPACE = 0x00010000L; // file contains a custom colorspace matrix (only internal now)

        public const ulong BINKNOYPLANE = 0x00000200L; // Don't decompress the Y plane (internal flag)
        public const ulong BINKWILLLOOP = 0x00000080L; // You are planning to loop this Bink.

        public const ulong BINKYCRCBNEW = 0x00000010L; // File uses the new ycrcb colorspace (usually Bink 2)
        public const ulong BINKHDR = 0x00000004L; // Video is an HDR video
        public const ulong BINK_SLICES_2 = 0x00000000L; // Bink 2 file has two slices
        public const ulong BINK_SLICES_3 = 0x00000001L; // Bink 2 file has three slices
        public const ulong BINK_SLICES_4 = 0x00000002L; // Bink 2 file has four slices
        public const ulong BINK_SLICES_8 = 0x00000003L; // Bink 2 file has eight slices
        public const ulong BINK_SLICES_MASK = 0x00000003L; // mask against openflags to get the slice flags

        [DllImport("bink2w64")]
        internal static extern void BinkClose(IntPtr Bink);

        [DllImport("bink2w64")]
        internal static extern IntPtr BinkOpen(string name, BINK_OPEN_FLAGS flags);

        [DllImport("bink2w64")]
        internal static extern string BinkGetError();

        [DllImport("bink2w64")]
        internal static extern IntPtr BinkDoFrame(IntPtr Bink);

        [DllImport("bink2w64")]
        internal static extern int BinkWait(IntPtr bink);

        [DllImport("bink2w64")]
        internal static extern void BinkNextFrame(IntPtr bink);

        [DllImport("bink2w64")]
        internal static extern int BinkCopyToBuffer(IntPtr bink, byte[] dest_addr, int dest_pitch, uint dest_height, uint dest_x, uint dest_y, BINK_COPY_FLAGS copy_flags);

        public struct BINK
        {
            public int Width;
            public int Height;
            public uint Frames;
            public uint FrameNum;
            public uint FrameRate;
            public uint FrameRateDiv;
            public uint ReadError;
            public BINK_OPEN_FLAGS OpenFlags;
            public BINKRECT FrameRects;
            public uint NumRects;
            public uint FrameChangePercent;
        };

        public struct BINKRECT
        {
            public int Left;
            public int Top;
            public int Width;
            public int Height;
        };

        public enum BINK_OPEN_FLAGS : ulong
        {
            BINKFILEOFFSET = 0x00000020L, // Use file offset specified by BinkSetFileOffset
            BINKFILEHANDLE = 0x00800000L, // Use when passing in a file handle
            BINKFROMMEMORY = 0x04000000L, // Use when passing in a pointer to the file
            BINKNOFRAMEBUFFERS = 0x00000400L, // Don't allocate internal frame buffers - application must call BinkRegisterFrameBuffers
            BINKUSETRIPLEBUFFERING = 0x00000008L, // Use triple buffering in the framebuffers
            BINKSNDTRACK = 0x00004000L, // Set the track number to play
            BINKDONTCLOSETHREADS = 0x00000040L, // Don't close threads on BinkClose (call BinkFreeGlobals to close threads)
            BINKGPU = 0x00000100L, // Open Bink in GPU mode
            BINKNOSKIP = 0x00080000L, // Don't skip frames if falling behind
            BINKPRELOADALL = 0x00002000L, // Preload the entire animation
            BINKALPHA = 0x00100000L, // Decompress alpha plane (if present)
            BINKGRAYSCALE = 0x00020000L, // Force Bink to use grayscale
            BINKFRAMERATE = 0x00001000L, // Override fr (call BinkFrameRate first)
            BINKSIMULATE = 0x00400000L, // Simulate the speed (call BinkSim first)
            BINKIOSIZE = 0x01000000L, // Set an io size (call BinkIOSize first)
            BINKNOFILLIOBUF = 0x00200000L, // Don't Fill the IO buffer (in BinkOpen and BinkCopyTo)
            BINKIOPROCESSOR = 0x02000000L, // Set an io processor (call BinkIO first)
            BINKNOTHREADEDIO = 0x08000000L // Don't use a background thread for IO
        }

        public enum BINK_COPY_FLAGS : ulong
        {
            BINKSURFACE32BGRx = 3,
            BINKSURFACE32RGBx = 4,
            BINKSURFACE32BGRA = 5,
            BINKSURFACE32RGBA = 6,
            BINKSURFACE5551 = 8,
            BINKSURFACE555 = 9,
            BINKSURFACE565 = 10,
            BINKSURFACE32ARGB = 12,
            BINKSURFACEMASK = 15,
            BINKGRAYSCALE = 0x00020000L, // Force Bink to use grayscale
            BINKNOSKIP = 0x00080000L, // Don't skip frames if falling behind
            BINKYAINVERT = 0x00000800L // Reverse Y and A planes when blitting (for debugging)
        }
    }

BinkPlayer

public class BinkPlayer : MonoBehaviour
{
    BinkNativeMethods.BINK bk;
    Texture2D texture;
    IntPtr bink;
    byte[] buffer;
    // Start is called before the first frame update
    void Start()
    {
        bink = BinkNativeMethods.BinkOpen("sample.bk2", 0);

        bk = Marshal.PtrToStructure<BinkNativeMethods.BINK>(bink);

        texture = new Texture2D(bk.Width, bk.Height, TextureFormat.RGBA32, false);
        var renderer = GetComponent<SpriteRenderer>();
        renderer.sprite = Sprite.Create(texture, new Rect(0, 0, bk.Width, bk.Height), Vector2.zero);
        buffer = new byte[bk.Width * 4 * bk.Height];
    }

    private void Update()
    {
        if (bink != IntPtr.Zero && BinkNativeMethods.BinkWait(bink) == 0)
        {
            BinkNativeMethods.BinkDoFrame(bink);
            // do you stuff here
            BinkNativeMethods.BinkCopyToBuffer(bink, buffer, bk.Width * 3, (uint)bk.Height, 0, 0, BinkNativeMethods.BINK_COPY_FLAGS.BINKSURFACE32RGBA);

            texture.LoadRawTextureData(buffer);
            texture.Apply();
            BinkNativeMethods.BinkNextFrame(bink);
        }
    }
}

The complete Unity project also can be found at my github.

4 thoughts on “Unity Bink player”

  1. Is there a way to get this working for Bunk 1? I’ve tried adapting it for binkw64, but the script is looking for a .Bk file and Bink 1 seems to only support .bik. Thanks in advance.

    1. As long as you have Bink1 SDK, you should be able to hook up with PInvoke similar in my post.

  2. Would you please explain why you multiply the width by 4 when you set the buffer variable, but multiply the width by 3 when you call the native BinkCopyToBuffer() function? And, more generally why do the BINK and BINKRECT structs have their own values for width and height?

    1. The bink video we used are RGBA format, and it takes 4 bytes for each pixel.
      It could be a mistake here.

      The BINK and BINKRECT are the internal data definitions of Bink.

Leave a Reply

Your email address will not be published.