QuantumEntangledAndy / neolink

An RTSP bridge to Reolink IP cameras
GNU Affero General Public License v3.0
257 stars 41 forks source link

Feature/SD Card #104

Open QuantumEntangledAndy opened 1 year ago

QuantumEntangledAndy commented 1 year ago

This will RE the SD card and file listing features of the camera

QuantumEntangledAndy commented 1 year ago

First the client requests the HDDInfoList with message ID 0x66

The camera replies with:

If no SD Card:

<?xml version="1.0" encoding="UTF-8" ?>
<body>
<HddInfoList version="1.1" />
</body>

If there is a SD Card

<?xml version="1.0" encoding="UTF-8" ?>
<body>
<HddInfoList version="1.1">
<HddInfo>
<number>0</number>
<capacity>30</capacity>
<format>1</format>
<mount>1</mount>
<remainSize>29</remainSize>
<remainSizeM>551</remainSizeM>
</HddInfo>
</HddInfoList>
</body>
QuantumEntangledAndy commented 1 year ago

Client then requests day records with msg ID: 0x8e and the payload:

<?xml version="1.0" encoding="UTF-8" ?>
<body>
<DayRecords version="1.1">
<startTime>
<year>2023</year>
<month>6</month>
<day>1</day>
<hour>0</hour>
<minute>0</minute>
<second>0</second>
</startTime>
<endTime>
<year>2023</year>
<month>6</month>
<day>30</day>
<hour>23</hour>
<minute>59</minute>
<second>59</second>
</endTime>
<DayRecordList>
<DayRecord>
<index>0</index>
<channelId>0</channelId>
</DayRecord>
</DayRecordList>
</DayRecords>
</body>

Client replies with:

<?xml version="1.0" encoding="UTF-8" ?>
<body>
<DayRecords version="1.1">
<startTime>
<year>2023</year>
<month>6</month>
<day>1</day>
<hour>0</hour>
<minute>0</minute>
<second>0</second>
</startTime>
<endTime>
<year>2023</year>
<month>6</month>
<day>30</day>
<hour>23</hour>
<minute>59</minute>
<second>59</second>
</endTime>
<DayRecordList>
<DayRecord>
<index>0</index>
<channelId>0</channelId>
<dayTypeList>
<dayType>
<index>6</index>
<type>normal</type>
</dayType>
<dayType>
<index>7</index>
<type>normal</type>
</dayType>
<dayType>
<index>8</index>
<type>normal</type>
</dayType>
</dayTypeList>
</DayRecord>
</DayRecordList>
</DayRecords>
</body>

This seems to reply with indexes where 0 is the 0th day from the range chosen. So in this case as the range starts with 01/06 then the indexes 6, 7 and 8 correspond to the days 7th, 8th and 9th of June

QuantumEntangledAndy commented 1 year ago

Next the client requests FileInfoList with msg ID 0x0e and the payload:

<?xml version="1.0" encoding="UTF-8" ?>
<body>
<FileInfoList version="1.1">
<FileInfo>
<channelId>0</channelId>
<streamType>subStream</streamType>
<recordType>manual, sched, io, md, people, face, vehicle, dog_cat, visitor</recordType>
<startTime>
<year>2023</year>
<month>6</month>
<day>9</day>
<hour>0</hour>
<minute>0</minute>
<second>0</second>
</startTime>
<endTime>
<year>2023</year>
<month>6</month>
<day>9</day>
<hour>23</hour>
<minute>59</minute>
<second>59</second>
</endTime>
</FileInfo>
</FileInfoList>
</body>

This payload picks up one of the days from the above DayRecords

The camera replies with:

<?xml version="1.0" encoding="UTF-8" ?>
<body>
<FileInfoList version="1.1">
<FileInfo>
<channelId>0</channelId>
<handle>1</handle>
<name></name>
<recordType>manual, io, md, sched</recordType>
<supportSub>1</supportSub>
<sizeL>0</sizeL>
<sizeH>0</sizeH>
<startTime>
<year>2023</year>
<month>6</month>
<day>9</day>
<hour>0</hour>
<minute>0</minute>
<second>0</second>
</startTime>
<endTime>
<year>2023</year>
<month>6</month>
<day>9</day>
<hour>23</hour>
<minute>59</minute>
<second>59</second>
</endTime>
</FileInfo>
</FileInfoList>
</body>

This gives a handle and the types of records and the date range that handle belongs too (handle will be used in next call)

QuantumEntangledAndy commented 1 year ago

The client then requests FileInfoList on message ID 0x0f with the payload:

<?xml version="1.0" encoding="UTF-8" ?>
<body>
<FileInfoList version="1.1">
<FileInfo>
<channelId>0</channelId>
<handle>1</handle>
</FileInfo>
</FileInfoList>
</body>

This uses the handle from before

The camera replies with:

<?xml version="1.0" encoding="UTF-8" ?>
<body>
<FileInfoList version="1.1">
<FileInfo>
<channelId>0</channelId>
<handle>1</handle>
<name>0120230609072812</name>
<streamType>subStream</streamType>
<containsAudio>1</containsAudio>
<fileType>mp4</fileType>
<recordType>md</recordType>
<sizeL>2100542</sizeL>
<sizeH>0</sizeH>
<supportSub>1</supportSub>
<startTime>
<year>2023</year>
<month>6</month>
<day>9</day>
<hour>7</hour>
<minute>28</minute>
<second>12</second>
</startTime>
<endTime>
<year>2023</year>
<month>6</month>
<day>9</day>
<hour>7</hour>
<minute>28</minute>
<second>39</second>
</endTime>
</FileInfo>
<FileInfo>
<channelId>0</channelId>
<handle>1</handle>
<name>0120230609073619</name>
<streamType>subStream</streamType>
<containsAudio>1</containsAudio>
<fileType>mp4</fileType>
<recordType>md</recordType>
<sizeL>2605156</sizeL>
<sizeH>0</sizeH>
<supportSub>1</supportSub>
<startTime>
<year>2023</year>
<month>6</month>
<day>9</day>
<hour>7</hour>
<minute>36</minute>
<second>19</second>
</startTime>
<endTime>
<year>2023</year>
<month>6</month>
<day>9</day>
<hour>7</hour>
<minute>36</minute>
<second>52</second>
</endTime>
</FileInfo>
<FileInfo>
<channelId>0</channelId>
<handle>1</handle>
<name>0120230609073919</name>
<streamType>subStream</streamType>
<containsAudio>1</containsAudio>
<fileType>mp4</fileType>
<recordType>md</recordType>
<sizeL>5097061</sizeL>
<sizeH>0</sizeH>
<supportSub>1</supportSub>
<startTime>
<year>2023</year>
<month>6</month>
<day>9</day>
<hour>7</hour>
<minute>39</minute>
<second>19</second>
</startTime>
<endTime>
<year>2023</year>
<month>6</month>
<day>9</day>
<hour>7</hour>
<minute>40</minute>
<second>27</second>
</endTime>
</FileInfo>
<FileInfo>
<channelId>0</channelId>
<handle>1</handle>
<name>0120230609074023</name>
<streamType>subStream</streamType>
<containsAudio>1</containsAudio>
<fileType>mp4</fileType>
<recordType>md</recordType>
<sizeL>8586753</sizeL>
<sizeH>0</sizeH>
<supportSub>1</supportSub>
<startTime>
<year>2023</year>
<month>6</month>
<day>9</day>
<hour>7</hour>
<minute>40</minute>
<second>23</second>
</startTime>
<endTime>
<year>2023</year>
<month>6</month>
<day>9</day>
<hour>7</hour>
<minute>42</minute>
<second>30</second>
</endTime>
</FileInfo>
<FileInfo>
<channelId>0</channelId>
<handle>1</handle>
<name>0120230609074226</name>
<streamType>subStream</streamType>
<containsAudio>1</containsAudio>
<fileType>mp4</fileType>
<recordType>md</recordType>
<sizeL>364907</sizeL>
<sizeH>0</sizeH>
<supportSub>1</supportSub>
<startTime>
<year>2023</year>
<month>6</month>
<day>9</day>
<hour>7</hour>
<minute>42</minute>
<second>26</second>
</startTime>
<endTime>
<y·wGûæIKNó3«SX(ä±Lv[æìNZ
Pó½Q2[RóYØXGÚYç_Q§[ýPGn¨mîPGj¨WîXGÞYØRRù¦pªH¡VMPâ4§AXlQâ W2OG¨mîZP¤WàZI[G÷ì)ZMLâìZIOMøºlZFCïYëIC[¨mîK P¡[ýK   P[¿JVG¨TëIKLã·lZQGõ¼GXW¹·@ F[ýPPV¿FXlGøJ(ª·BX¤TîCP¨mîN  VJ¨Qî LVþYØ[¯[ýG(ª½VX¹½VX(û¼V¦[ýNWVóYØAMøìQZ
Qó½MX(¹¼G2OG¨mî NGß    ´LXldÿ·jM[±KLGú.¶VZ
Aþ¼M
kF¨mîKFNóYãICLò·lZLCûìWT¥Wä_V¦UáICOóYØPG÷
ZQãWCOªH¡WCO¢FXlAù ¦BQcã»LXW
õ¼WLQ׶J    X(ð¾F2RG¨
¢ZIDKúZ(ª·@ Fvï·
ä±Lv[æì)ZKXó+ìS^£TîXGÚYØXGÞYâIKXó/ì)ZWRæ W5@§[ýPRMäVX(å³Q2KOóYØCP¨UâUZ
[ó l

This is the largest XML record to date and the current decrytor used in wireshark is FAILING. There may be some refinements needed in order to decrypt large messages like this

QuantumEntangledAndy commented 1 year ago

The client then sends a FileInfoList on msg ID 0x10 with the payload:

<?xml version="1.0" encoding="UTF-8" ?>
<body>
<FileInfoList version="1.1">
<FileInfo>
<channelId>0</channelId>
<handle>1</handle>
</FileInfo>
</FileInfoList>
</body>

The camera just replies with the standard 200 status OK header

No idea what this is for. Seems to be the same as 0x0f but with no real reply from the camera. Maybe its for a different type of data storage or file type that I don't have

QuantumEntangledAndy commented 1 year ago

Client then sends ReplaySeek on msgID 0x7b with the payload:

<?xml version="1.0" encoding="UTF-8" ?>
<body>
<ReplaySeek version="1.1">
<channelId>0</channelId>
<seq>1686278241</seq>
<seekTime>
<year>2023</year>
<month>6</month>
<day>9</day>
<hour>7</hour>
<minute>28</minute>
<second>12</second>
</seekTime>
</ReplaySeek>
</body>

seq is the EPOCH time stamp and seek time is the times from the previous info file list

Camera replies with standard OK header

QuantumEntangledAndy commented 1 year ago

The client then sends FileInfoList on msg ID 0x0d with payload:

<?xml version="1.0" encoding="UTF-8" ?>
<body>
<FileInfoList version="1.1">
<FileInfo>
<channelId>0</channelId>
<name>0120230609072812</name>
<supportSub>1</supportSub>
<playSpeed>1</playSpeed>
<streamType>subStream</streamType>
</FileInfo>
</FileInfoList>
</body>

This uses the name from the 0x0f FileInfoList

Camera replies with

<?xml version="1.0" encoding="UTF-8" ?>
<body>
<FileInfoList version="1.1">
<FileInfo>
<channelId>0</channelId>
<handle>0</handle>
<name>0120230609072812</name>
<containsAudio>1</containsAudio>
<fileType>mp4</fileType>
<recordType>none</recordType>
<sizeL>2100542</sizeL>
<sizeH>0</sizeH>
<supportSub>1</supportSub>
<startTime>
<year>2023</year>
<month>6</month>
<day>9</day>
<hour>7</hour>
<minute>28</minute>
<second>12</second>
</startTime>
<endTime>
<year>2023</year>
<month>6</month>
<day>9</day>
<hour>7</hour>
<minute>28</minute>
<second>39</second>
</endTime>
</FileInfo>
</FileInfoList>
</body>

This seems to give details such as the file size and type

QuantumEntangledAndy commented 1 year ago

Now we get to the actual data. This is done though the FileInfoList command again... with the msg ID 0x05 and the payload:

<?xml version="1.0" encoding="UTF-8" ?>
<body>
<FileInfoList version="1.1">
<FileInfo>
<channelId>0</channelId>
<supportSub>1</supportSub>
<streamType>subStream</streamType>
<startTime>
<year>2023</year>
<month>6</month>
<day>9</day>
<hour>7</hour>
<minute>28</minute>
<second>12</second>
</startTime>
<playSpeed>1</playSpeed>
</FileInfo>
</FileInfoList>
</body>

This seems to just be a play start time rather then a file, I presume the other commands before were about loading the file and preparing it for this

There are multiple messages from the camera now and things got weird.. I'll show the whole header so we can see it

Firstly this one:

Magic ID Len Key Handle Status Payload
f0debc0a 05000000 8a000000 1800 0000 c8000000 6a000000

Is a normal header and the first packet from the camera in reply to FileInfoList 0x05

It has the extension of

<?xml version="1.0" encoding="UTF-8" ?>
<Extension version="1.1">
<binaryData>1</binaryData>
</Extension>

Followed by a binary payload in BCMedia format. This it the format that the camera uses for everything (it's not a mp4 like the FileInfoList data says) fortunaly we already know how to decode these as they are used for the live stream.

The packets after this get weird though

Magic ID Len Key Handle Status Payload
f0debc0a 05000000 1a550000 1800 0000 61908264 6a000000

That status is no longer the usual status code stuff but instead the number 1686278241, which suspiciously looks like seconds from the epoch which corresponds to 09 June 2023 09:37:21.000 +07:00 which seems to be almost but not quite the time in the previous xml

So lets rename this as

Magic ID Len Key Handle Secs Payload
f0debc0a 05000000 1a550000 1800 0000 61908264 6a000000

This actually brings about another issue. The current neolink implementation uses the bytes in the status code to determine the header type. We will need to reimplement the whole concept of message class....

The payload for this is the extension xml

<?xml version="1.0" encoding="UTF-8" ?>
<Extension version="1.1">
<binaryData>1</binaryData>
</Extension>

with the continue binary BCMedia stream

This packet repeats indefintly until the whole stream is finished (the seconds header remains the same number throughout)

QuantumEntangledAndy commented 1 year ago

The last message comes from the client as FileInfoList with id 0x07 and the payload

<?xml version="1.0" encoding="UTF-8" ?>
<body>
<FileInfoList version="1.1">
<FileInfo>
<channelId>0</channelId>
<name>0120230609072812</name>
</FileInfo>
</FileInfoList>
</body>

The camera just replies OK with the standard header

This seems to be the STOP command for the replay

QuantumEntangledAndy commented 1 year ago

Those are the messages that need to be implemented and sent. The difficult one will come with the reworking of the message class. We may no longer be able to relay on it as a source of the message format. May need to rework how this is implemented. Maybe with different header kinds as a enum

May take awhile to redo and I will wait for the other current PRs to close as it will likely need a refractor

MasterPlexus commented 11 months ago

I'm willing to help, if I could, on this. I have 4 Argus 2 in use, and could setup may one for permanent test during Development.

QuantumEntangledAndy commented 11 months ago

Just busy with work at the moment. If you can code rust it might be helpful if you could work up a basic PR for at least the messages structures I think it's in src/crates/core/bc/xml.rs

MasterPlexus commented 7 months ago

I have no knowledge in rust. Only in other languages, but I'm more a script Copy paste guy ;) So not sure, if I could help in that. I had a short look to the xml, but this looks like not so clear to me.