-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FileTransfer #8
Comments
Indented is stream from client, non-indented is stream from Denon Prime 4
|
Some things I observed:
and that last pattern repeats indefinitely every 2 or so seconds. Strangely I've also observed the following repeating pattern:
so it has
|
Full output of the above example when I also subscribe to FileTransfer in my code:
|
@icedream would it be possible to send such a network trace for fetching a simple image (album art for instance)? Unfortunately I don't have any devices here other than the Denon Prime Go, so it's impossible for me to make such a trace myself. |
If I can make it happen in Resolume Arena I can try and trigger such a download and record it later... not sure if it even displays album art at all, so I would have to look at that. |
Also I thought I replied to this a long while ago already, strange. Anyways,
Interval was only my best guess at what that field is for, as I thought it was different between SoundSwitch and Resolume Arena and may coincide with how often they polled/received updates. I can't verify my thoughts on this anymore and the probability of that being anywhere close to true is low enough that I would rather ignore that. 😅 Also I thought I had a case where that number was 0x17 or 0x1d instead but I couldn't find it in any of the network recordings I still have. Consider it guesswork. |
Thanks for your reply! That changing number seems to be indeed something time based. It increased every so many seconds. Another thing I observed is when sending
I always seem to get the location of the currently available sources (in my case I have 2 sources (indicated by the 0x02 that follows the 0x03). They simply append the names of the sources after each other):
After the last name they append 3 times Then in a separate message they send the following:
(also some kind of end marker?) So those last two uint32s: Interesting :) EDIT: I'll update this message when I find more info |
Hi @icedream, I managed to take the FileSystem service a few steps further. WIP code can be found here: https://github.com/MarByteBeep/StageLinq/blob/main/services/FileTransfer.ts When changing const ftx = await controller.connectToService(FileTransfer);
const sources = await ftx.requestSources();
console.log(sources); I get the following output:
It's a start :) Up next, requesting that file. I noticed that when sending a file transfer request, we need to send a starting chunk id and an ending chunk id. Then the device will send back those chunks. I'll let you know when I get that working. EDIT: I noticed that all Album Art is stored as blobs in that database (SQLite). So once we're able to download the library database, it'll be quite easy to match the album art with the currently active songs. |
The capture I posted at the beginning of the issue involved downloading an SQLite database at least, so maybe that helps? I had to cut some of the data off since it's of course way too big. Awesome progress so far! |
Yes, that capture really helps. I think I now more or less figured out on how to retrieve that database myself. I think later today I'll be able to share it with you! |
Hi @icedream FileTransfer is now able to retrieve the source database and display its number of album arts using SQLite. I pushed the changes. From here it is easy to get the album art binary data (is a blob in the DB), so we can display that for instance when a song is playing. Perhaps you can test it in Go as well using your equipment? |
I think Prime supports audio portion of mp4 playback, right? Do any VJ stagelinq integrations today automatically download the currently playing track (assuming it is mp4) and playback a timeline (via Beatinfo) sync'ed video output of the same file (as downloaded on demand)? I can't remember what software product video demo I looked at, but it seemed the VJ side had to drag their own copy of the video on to the timeline. Seems like that manual process could be avoided if you can just D/L a track from a player on demand (and it is an mp4). Just thinking out loud! Very ignorant on how existing Stagelinq integrations work so excuse my silly questions. |
I think you're right, any video synchronization is basically just done through positions from what I know and avoid downloading video data off the players – DJs bringing along their own video for playback to any event simply isn't a common thing after all. 😄 Since video files can be pretty big, I wonder about performance (CPU or I/O load) on the devices for both decoding the audio part and transmitting the whole file over the network while simultaneously playing it back. Sounds extreme, but at least the Prime 4 may be able to deal with it just fine…? |
I am just daydreaming on the possibilities if CPU and I/O were not a barrier. 🙃 I guess you could "pre-sync" data between DJ and VJ by just exporting the same library to two different USB sticks using Engine DJ. At that point, you can try to do something intelligent to select the right file on the VJ side based upon Stagelinq track metadata. It would be interesting if there was a VJ software that tried to intelligently and intentionally do this to bring a simple VJ experience to Denon Prime. Also, I know very little about the VJ software market. But when I saw the manual file fiddling in the demo video (maybe it was Resolume?) I thought... ugh, what a terrible workflow. |
@icedream - Any thoughts or musings on how to implement this in the go-stagelinq framework? Since this is a request/response type service that seems to have multiple types of requests that can be sent (ie. filestat, getsources, getchunks, etc.) and response types that can be received (corresponding to requests) on the same TCP socket, how can we best orchestrate this on a hypothetical |
Depending on how we want to abstract it, it may be enough to have one method sending and then receiving messages, expecting certain types. In that case we would want a mutex, too, for methods to not step on each other. If you want to deal with file transfer asynchronously, as in multiple parallel requests are possible, then you basically got something that requires output channels and a goroutine to sort the messages to the correct output channels. I don't know if you already know whether the protocol allows for this to happen. Then the pattern above for things that are not asynchronous streams (file stat sounds like that) would not directly apply to the messageConnection, but to messages filtered out to not belong to any other output channel. The |
@MarByteBeep - I think this might be the Denon Stagelinq device initiating a "GetSources" request (0x07d2) over the established connection towards us (the client). I think the first 4 bytes represent a request ID which we would include in our response. I think it might just want to keep sending this, with an incrementing request ID (?), until we actually respond or hangup. |
Another interesting bit -- I think there is another message ID of 9 which represents "unknown/malformed request." Not 100% sure. |
So... stat - 0x7d1 seems like 0x7d3 should be something :) I tried a few payloads thinking perhaps it was a way to issue an |
These "Unknown" 8 messages .... dying to know what the heck they are! payload:
4 bytes - uint big endian length - 00 00 00 12 |
@mhite Not sure if it's helpful, but I can tell you that I get a different response from the OfflineAnalyzers compared to the players. Their Message8 only has 4 bytes after code, not 6. |
@honusz - Oh, I see what you mean! It's 4 bytes of 0xFF for me on the OfflineAnalyzers. (This is on a Prime4, btw.) So much for that theory on it being a length or record count! |
@icedream - Thanks for the guidance. I've got the very rough beginnings of something. There's a lot to unwind with FileTransfer! |
Now this has me thinking that if we are able to respond properly that we could actually show up as a source on the Denon gear and serve up a remote file system. Crazy talk? |
... and literally after typing this in, I tested a new hypothesis as to what the problem might be, and managed to get a response. Lol! New discoveries and details to come... |
Ok, some background:
|
With regards to the sources response message (message id 0x3), this is kind of interesting: In @icedream's capture, it has an example response with 0x1, 0x1, 0x0 at end:
...but another with 0x1, 0x1, 0x1:
As another data point, it also looks like @MarByteBeep 's implementation always expects 0x1, 0x1, 0x1: https://github.com/MarByteBeep/StageLinq/blob/main/services/FileTransfer.ts#L73 The only thing that jumps out is that the response in @icedream 's capture with 0x1, 0x1, 0x0 is a response with 0 sources. Although in my own capture of a Prime4 OfflineAnalyzer FIleTransfer service, a response with 0 sources can also have 0x1, 0x1, 0x1 at the end of a response:
|
I've only ever seen That being said, MarByte's implementation (with its triple 0x1 assertion) has been used in the wild quite a bit. I feel like we likely would have caught any messages that broke this pattern. |
Yeah, I've noticed this. I suspect it's used to have handle multiple transfers. I can send you the capture I'm referring to. It's complex (multiple filetransfer services due to offline analyzers), but contains a lot of info that may be helpful in your endeavours. I don't want to post it publicly out of respect to the artists as it contains the data for purchase tracks, but if you give a contact (email, twitter, discord) I'll hook you up. |
Yeah, I think @icedream took that capture from Resolume Arena to a Prime 4. So perhaps this might be something we see for certain classes of clients like Resolume. It also makes me think think these values are probably booleans of some sort. And just to toss out untested hypotheticals, I wonder if it also is some of signal to say "yeah don't ask me for my sources again because I will always have none." It is good to know that we can expect 0x1 0x1 0x1 when dealing with players that we've observed so far! |
I've been trying to craft a response to the List command we receive from the JC11 to get it to stop asking. Haven't figured that out yet unfortunately. |
Interesting. I always assumed these were flags (7F as half-state between 00 and FF), but I suppose it definitely could be the broadcast address.
It's basically just the player sending the standard first inquiry players send to one another.
|
Yes, that's what I was looking at. Not sure if we see the pattern in other stuff, though.
Hmm. I'm sending that with no luck.
|
Are you including the txid from the request message the device sent? |
Yeah, I reflect the txid in my response. |
Only asking because the wireshark packet you quoted has 00 for txid |
I should clarify that the last two fields are this first-last-dir flags, obviously I wrote this before you discovered their meaning, lol So I guess that last field should be 0x1, but it doesn't seem to matter. It's weird, it definitely works for me; once I send that message, no more RequestSources / Dir messages from the unit. |
Ok, I went back and crawled through the code that handles the initial message parsing of the received message. There's a problem in my parser for fileTransferListRequestMessage and it wasn't correctly obtaining the transaction ID. Thanks for pushing me in the right direction! |
No prob! I take back what I said about protobufs, that would make this a lot easier |
Ok, so even after I fixed the txid reflection issue, it was still giving me grief. What I did discover is that if I send the 0x2 command after the 0x3 (sources/list) response I can get it to shut up. You aren't doing this? 😕
If I flip that "UnknownFlag" boolean in the For example:
It stops after the follow-up request for "/cowpoop". You can see I just synthetically generate only a "cowpoop" response. It is probably looking for an "Engine Library" directory would be my guess? I will have to code up a full-fledged response handler to traverse a file system to play with this more. That will take some time, but it's on my list of things to play with. I have a hunch we can get a remote computer to show up as a source on the Denon. (I haven't tried doing any packet captures on how Engine DJ exposes this today given it provides new functionality like this...) BTW, that |
Lol, it's a new rabbit hole! 🐰 |
Hahaha, start serving it tunes!! |
You know what... they behaviour may actually be different, seeing as in my method the players initiate the connection to me. |
@mhite okay, I think it is different because you are the player initiating the connection. Helpfully, because I do it differently I get the EndOfMessage response back from the players. The player with media attached sends back (after the Magic>TxId>MessageId preamble) |
@honusz - ah, very interesting. so you have a network listener waiting to receive connections initiated from standalone players? |
Indeed, it's the method I posted about a while ago here. Somethings about it make it much easier - it's less of a state-headache to have a server with multiple clients re/connecting to it, rather than manage a client connected to multiple servers. |
@honusz - Ah, now I remember! And your approach definitely makes sense. Made some more progress -- I can now browse folders on my local hard drive via the Denon device. I wish I knew more about the unknown byte blob in the Stat command responses, though. |
@honusz - have you ever spotted this
It was sent in response to me sending back a transfer ID that was requested:
It never does request a chunk from me, so maybe it doesn't like my response. And perhaps |
I have seen Player1 Requests a Transfer Id:
Player2 replies with the 0x4 TransferId message:
Player1 sends some requests about another txid.... Player1 sends
Player1 sends some more requests about another txid.... Player1 returns to this txid, and sends a
My best guess:
I suspect in the examples I posted above, the player meant to send the One interesting thing I noticed while looking for these,
This might be a "hey, are we still doing anything with this txid?" |
Very interesting. In digging through the packet captures between the players, it seems like 0x7d9 is a pause request and the 0x7 is a pause response. The players seem to look for a bunch of different .db files and start downloading them all, but basically immediately puts each of those db transfers into a pause state. I haven't made much sense of the x7d8, though :( It doesn't even contain a transferid so I'm a little suspicious there. |
I've definitely hit a wall. Here's a few thoughts/theories:
If anyone has made new progress on decoding that stat message, let me know. I feel like it's what is probably holding me back. |
Ughh, this is driving me nuts too. The Fstat responses are just such a weird structure.
Of those 45 remaining bytes: 4-bytes: So that leaves us with 39 bytes, which is an odd number, in every sense of the word. Now, bear with me, I'm going to go a bit Carrie-from-Homeland here, lol..... We see what looks like three repetitions of data, and hey! 39/3 = 13! So what we may be looking for is 13 bytes. Note: I actually messed up here; I included the length-bytes in the byte-count, but with @mhite's observation about 8-byte filesize, we're in business! Okay, so, now with it formatted a bit, it starts to make a bit more sense. Here's two valid(?) and one invalid(?) responses:
Interestingly, when I look at my capture between the players, they return slightly differently:
Not sure what to make of that. Anyway, not exactly a silver bullet, but we can say a few things about these 13-byte repetitions:
Not sure if any of this is helpful. Perhaps you could put the file on a usb, do 0x7d1 request on it, get the response, then try serving it to the player from your HD? |
Great summary.
BTW -- The first byte is a boolean representing "Exists" truthiness. |
I actually tried this. :( Sadly didn't do any good. So now I'm not sure of anything. :( :( |
I was also pondering that! But I'm not sure the first digit makes any sense. For mounted filesystems like USB volumes, you would expect the mount to supply the default permissions since FAT32 has no inherent permissions concept. So it would make sense for USB volumes to have the same permissions across files and directories. I like your approach. Perhaps a tool that recursively crawls the directory path and records the stat bytes for each file and directory. |
A thought on the three similar chunks -- perhaps access, modification, and change time? That being said, I tried a million different ways of decoding them or portions of them as timestamps with nothing sane found. |
Other things that might show up:
|
stat from original file:
copy of file:
another copy of the original, made a few seconds later:
copy 1 and copy 2 share those last bytes:
|
The text was updated successfully, but these errors were encountered: