bdogan / go-atem

BlackMagicDesign ATEM switcher protocol implementation. Written in go.
13 stars 4 forks source link

API for sending AtemCmd to connected ATEM #1

Open chxmbley opened 5 years ago

chxmbley commented 5 years ago

There doesn't appear to be a way to construct an AtemCmd and send it to the switcher as an AtemPacket.

Something like:

func (a *Atem) SendCmd(c *AtemCmd) error {
    // Hypothetical AtemCmd to AtemPacket conversion function
    p, err := packet.CreatePacketFromCmd(c)
    if err != nil {
        return err
    }
    a.writePacketQueue(p)
    return nil
}

Having this function would open the door for expanding the API to create functions that provide abstractions for switcher functionality, like atem.PerformCut() and atem.SetPreviewInput(video_source.Input1), that more closely resembles the official ATEM SDK.

bdogan commented 5 years ago

First of all thanks for the support. 👍

I haven't finished this project yet. I'm still working on it. I don't have much free time. There is not enough documentation about protocol. I'm trying to solve the protocol with using wireshark and this. I have 1/ME Panel. I couldn't send command to Atem yet. But i am trying 😄 to send. There are some timing issues about communication. Atem hasn't listened to me yet. I wanted to write in go for cross compile to arm and similar cpus.

If you have any experience about atem protocol in binary level for sendin command. Can you share with me ?

chxmbley commented 5 years ago

No worries, I'm excited that a Go library is being built for ATEM. Thanks for doing it.

I've forked this project and will also work toward opening up two-way communication. My experience is limited, but I've worked with this Node.js repo and the official ATEM SDK for C# by Blackmagic Design. The Node.js repo is a good place to start reverse-engineering to see about sending a command.

chxmbley commented 5 years ago

If you haven't already, check out this Wireshark plugin

bdogan commented 5 years ago

I finally managed to send the command to Atem. (on develop branch) Can you test it?

chxmbley commented 5 years ago

I had one command successfully go through to the switcher, but unfortunately none after that. Are you having better luck with your 1 M/E?

chxmbley commented 5 years ago

Running in debug mode, it looks like communication between client and switcher stops altogether if more than one command is sent. I've added a simple routine in examples/control.go that reproduces this behavior.

bdogan commented 5 years ago

On event callback if we use infinite loop it hangs all communication. If you put test code in go func()

    go func() {
        for {
            time.Sleep(time.Second)
            log.Println("Cut!")
            at.atemClient.PerformCut()
        }
    }()

it works again :)

But there is no guarantee to everyone use it by same. So I maked emitting functions async on last commit. I have tested my 1 M/E. It looks work again. ( sorry for my bad english :( )

You look good planner. What is our next move in this project. What is your opinion?

chxmbley commented 5 years ago

Ah, such a simple and effective solution. Great work on this!

The only problem I'm having now is sending commands rapidly. It seems to only update every second, which may be related to this this line, but changing the duration seems to cause I/O timeouts more often.

I've pushed another commit to examples/control.go that demonstrates this behavior.

I think now that control is coming together nicely, we can start working on exposing an easy-to-use control API in control_api.go. I can start on that today.

chxmbley commented 5 years ago

The command buffer was handled only when a sync request came from the ATEM (roughly once per second), causing several commands to send all at once instead of sending immediately.

Handled in this commit by writing the command as a packet to the packet queue as soon as sendCommand() is called. This bypasses the command buffer, so we'll need to look into cleaning up the associated code if this works as expected.

We may also need to go back and check the data that's being sent to make sure it follows protocol, but it seems to work well through the control.go example. I'll let the test run for the next several hours to see how it does.

bdogan commented 5 years ago

i knew this situation. First i try to send commands when needed but atem wouldn't accept any commands by this way so then i turn back to reverse eng.. I discovered atem keep alive connection when client side send repetitive sync packet.

Atem Sync -> ClientAck -> ClientSync -> AtemAck

This is my temporarily solution.

We need to break sync packet write process from packet read process. I will write another channel for commands ( not packet because there is also need multiple commands sending to atem ). Write sequence run in a loop by holding a timer about 100ms and should check command buffer is there any commands available to send. Also we should check atem ack packets for this commands for verify.

There is also multiple flag containing packets available (ack & sync same time).

bdogan commented 5 years ago

You use official Atem SDK. Did you ever test lag for command sending.

chxmbley commented 5 years ago

A separate channel for handling the command queue sounds like a good idea,

The lag between sending a command and when the ATEM sends the update back to the client is 40ms or less with the SDK. The Node.js library is a little over 40ms, sometimes 50ms.