Wiretap API - create clip help needed

Hi guys,

It seem to be another quite rare case but I’m trying to use wiretap api for the next verson of TimewarpML in order to avoid exporting sequences and importing it back.
I can read image data well but really struggle to create correct buffer in order to write one.

I do have a little code snippet that should create an 8-bit red square:

import os
import sys

from adsk.libwiretapPythonClientAPI import WireTapClient
from adsk.libwiretapPythonClientAPI import WireTapServerHandle
from adsk.libwiretapPythonClientAPI import WireTapNodeHandle
from adsk.libwiretapPythonClientAPI import WireTapStr
from adsk.libwiretapPythonClientAPI import WireTapInt
from adsk.libwiretapPythonClientAPI import WireTapClipFormat

import ctypes
import flame

# assuming you have numpy unpacked in /var/tmp/numpy
# one can just unzip wheel

sys.path.insert(0, '/var/tmp/numpy')
import numpy as np
del sys.path[0]

python_api = ctypes.CDLL(sys.executable)
python_api.PyUnicode_FromKindAndData.argtypes = [ctypes.c_int, ctypes.c_char_p, ctypes.c_ssize_t]
python_api.PyUnicode_FromKindAndData.restype = ctypes.py_object

class WireTapException(Exception):
    def __init__(self, msg):
        flame.messages.show_in_dialog(
            title = 'flameTimewrarpML',
            message = msg,
            type = 'error',
            buttons = ['Ok']
        )

server_handle = WireTapServerHandle('localhost')

num_frames = 4

library = flame.projects.current_project.create_shared_library('twml')
parent_node_handle = WireTapNodeHandle(server_handle, flame.PyClip.get_wiretap_node_id(library))

new_clip_node_handle = WireTapNodeHandle()
clip_format = WireTapClipFormat(
    24,
    24,  # width, height
    3 * 8,  # bits per pixel
    3,  # number of channels
    24,  # frame rate
    1,  # pixel ratio
    WireTapClipFormat.ScanFormat.SCAN_FORMAT_PROGRESSIVE,
    WireTapClipFormat.FORMAT_RGB(),
)

if not parent_node_handle.createClipNode(
    "MyNewClip",  # display name
    clip_format,  # clip format
    "CLIP",  # extended (server-specific) type
    new_clip_node_handle,  # created node returned here
):
    raise WireTapException(
        "Unable to create clip node: %s." % parent_node_handle.lastError()
    )

if not new_clip_node_handle.setNumFrames(int(num_frames)):
    raise WireTapException(
        "Unable to set the number of frames: %s." % new_clip_node_handle.lastError()
    )

new_fmt = WireTapClipFormat()
if not new_clip_node_handle.getClipFormat(new_fmt):
    raise WireTapException(
        "Unable to obtain clip format: %s." % new_clip_node_handle.lastError()
    )

# array of red
pattern = np.array([0xff, 0x00, 0x00], dtype=np.uint8)
repeated_array = np.tile(pattern, new_fmt.frameBufferSize() // len(pattern))
remainder = new_fmt.frameBufferSize() % len(pattern)
if remainder > 0:
    arr = np.concatenate([repeated_array,
                            np.zeros(remainder, dtype=np.uint8)])


for frame_number in range(0, num_frames):

    # method 1
    buff_out = str(arr.tobytes(), 'latin-1')
    # method 2
    buff_out = python_api.PyUnicode_FromKindAndData(ctypes.c_int(1), arr.tobytes(), arr.nbytes)

    print (":".join("{:02x}".format(ord(c)) for c in buff_out))

    if not new_clip_node_handle.writeFrame(frame_number, buff_out, new_fmt.frameBufferSize()):
        raise WireTapException(
            "Unable to obtain write frame %i: %s."
            % (frame_number, new_clip_node_handle.lastError())
        )
    print("Successfully wrote frame %i." % frame_number)

And I’m getting quite interesting clip instead:

Screenshot 2023-05-24 at 01.28.59

you can check it out assuming you have numpy installed or unpacked somewhere.

Giving it a buffer read from another clip works just well, but I need to be able to write an amended image data here.

Any ideas and help are greatly appreciated!

Thank you
Andriy,

2 Likes

Eventually the only workaround here I could find was to save the image data into an image file and then use gateway server handle in order to read it back to a frame buffer and then save it to and actual clip. It is a bit silly but seem to work for now though might need a bit wider testing.

the code loos something like this:

                gateway_server_id = WireTapServerId("Gateway", "localhost")
                gateway_server_handle = WireTapServerHandle(gateway_server_id)
                clip_node_handle = WireTapNodeHandle(gateway_server_handle, file_path + '@CLIP')
                fmt = WireTapClipFormat()
                if not clip_node_handle.getClipFormat(fmt):
                    raise Exception("Unable to obtain clip format: %s." % clip_node_handle.lastError())
                
                buff = "0" * fmt.frameBufferSize()

                if not clip_node_handle.readFrame(0, buff, fmt.frameBufferSize()):
                    raise Exception(
                        'Unable to obtain read frame %i: %s.' % (frame_number, clip_node_handle.lastError())
                    )
                
                server_handle = WireTapServerHandle('localhost')
                destination_node_handle = WireTapNodeHandle(server_handle, self.destination_node_id)
                dest_fmt = WireTapClipFormat()
                if not destination_node_handle.getClipFormat(dest_fmt):
                    raise Exception('Unable to obtain clip format: %s.' % clip_node_handle.lastError())

                if not destination_node_handle.writeFrame(
                    frame_number, buff, dest_fmt.frameBufferSize()
                ):
                    raise Exception(
                        "Unable to obtain write frame %i: %s."
                        % (frame_number, destination_node_handle.lastError())
                    )

                os.remove(file_path)
1 Like