Add disk management, SD card replay/download, and alarm search#396
Add disk management, SD card replay/download, and alarm search#396lorek123 wants to merge 19 commits into
Conversation
- Add MSG_ID_HDD_INFO_LIST (102) and MSG_ID_HDD_INIT_LIST (103) in bc/model - Add HddInfoList, HddInfo, FormatExpandCfg, HddInitList, HddInit XML types - Add bc_protocol/disk.rs: get_hdd_list(), format_disk(init_ids, full_format) - Add disk subcommand: 'neolink disk [list]' and 'neolink disk format --disk N [--full]' - Default subcommand is list when omitted - Fix UnintelligibleReply/UnintelligibleXml display and unused_assignments warnings - Fix BcSubscription and MqttReplyRef lifetime elision warnings
Implement SD card replay and download for E1/E320 cameras: - New `replay` subcommand with search, list, download, and play modes - BC protocol replay flow (MSG 5/7/8/13-16/123) with BcMedia stream parsing - GStreamer-based MP4 muxing with per-frame PTS for variable frame rate - Fix E1 decryption: always read payload_offset for v2 protocol packets - Fix over-decryption: only decrypt [encryptPos, encryptPos+encryptLen) - Enhanced Wireshark dissector with replay message types and encryption fields - BcMedia format documentation (BCMEDIA_REPLAY_FORMAT.md) Co-Authored-By: Claude Opus 4.6 <[email protected]>
The camera's recordType field carries AI detection labels (people, vehicle, face, dog_cat, package, visitor, etc.) alongside trigger types (md, sched, manual, pir). These are now embedded as MP4 tags: - GStreamer: Keywords (AI tags), Comment (full recordType), Description (start/end time), Title (camera name), Encoder (neolink replay) - ffmpeg fallback: -metadata comment and description flags - File listing: AI detections highlighted with [AI: ...] suffix Co-Authored-By: Claude Opus 4.6 <[email protected]>
Tag events sent via appsrc.send_event() don't reliably propagate to the muxer. Use mp4mux's TagSetter interface (set before Playing state) to write tags directly into the MP4 udta/meta atoms. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Recordings triggered only by AI detection (without md/sched) were missed because the default record_type was "manual,sched,md". Now defaults to all known types. Adds --ai-filter to `replay files` for client-side filtering by AI detection tag (e.g. --ai-filter people,vehicle,dog_cat). Co-Authored-By: Claude Opus 4.6 <[email protected]>
Implements the findAlarmVideo protocol (MSG 175 / 0xAF) which allows searching recordings by alarm/detection type on the camera itself. Adds FindAlarmVideo and EventAlarmType XML types, alarm_video_search_start and alarm_video_search_next protocol methods, and the `alarm-search` CLI subcommand with --alarm-types filtering. Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Fix MSG 14 regression: separate FILE_SEARCH_RECORD_TYPES (manual, sched,md,pir,io) from ALL_RECORD_TYPES. E1 cameras reject unknown AI-specific tags in file search with error 400. - Handle MSG 14 error 400 gracefully as "no files for this day" - Handle MSG 175 error 405 gracefully as "not supported" - Downgrade per-packet replay logs from info to debug - Remove E1 strip diagnostic logging from replay stream - Reduce binary progress logging from every 20 to every 200 packets - Fix file size display: show KB for files under 1MB - Remove debug eprintln! from main.rs replay dispatch Co-Authored-By: Claude Opus 4.6 <[email protected]>
Upstream PR QuantumEntangledAndy#396. Adds three major capabilities: - Disk management (MSG 102/103): list and format SD cards - SD card replay & download (MSG 5/7/8/13-16/123/143): full playback pipeline with VFR-correct MP4 muxing and AI detection metadata - Alarm video search (MSG 175): server-side filtering by alarm type Also includes E1 camera fixes: protocol v2 header handling, partial-region AES decryption, and graceful error handling. Crypto refactored to store key+IV and create fresh cipher per packet for E1 replay compatibility. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
PR QuantumEntangledAndy#396's de.rs changes introduced a partial-decryption approach designed for E1 replay, but this broke live FullAes video streams. The camera only encrypts the first `encryptLen` bytes of each BC packet while continuation packets are plaintext. PR QuantumEntangledAndy#396's logic incorrectly applied full AES decryption to plaintext continuation packets, corrupting video data. Reverts to the original 6e05e78 binary handling: full decrypt + truncate for FullAes with encrypted_len, raw passthrough for all other binary packets. Adds StreamData::from_parts for replay compatibility. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Add a section crediting @wafgo, @Maaggs, @fromagge, @lorek123, and @joshkautz for their unmerged upstream PRs (QuantumEntangledAndy#373, QuantumEntangledAndy#389, QuantumEntangledAndy#394, QuantumEntangledAndy#395, QuantumEntangledAndy#396, QuantumEntangledAndy#398, QuantumEntangledAndy#399, QuantumEntangledAndy#400) that were ported into this fork. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
|
For pressed user as me and tester i produced draft release |
|
I just compiled with this pull, and could confirm i can connect with disk list to the sd-card on my argus 3. but unfortunately the replay command is unknown... Is the initial description may not correct and the command is now a different one? |
download by file worked for me on argus 3, i will work on my bash script #367 |
|
trying your link, but there are no releases... |
did you click on "asset 5" ? |
|
./neolink replay --config ./config.toml parking files --date 2026-02-13 |
ah sorry. its under tags, not releases... |
ok, it looks like i made a mistake during my clone. I rebuild now from scretch with a fresh clone... (there i see also the replay in main.rs, which was not the case in my old clone)... |
Why do you want duplicate? |
|
ok, so far the commands are working, but i got most likely service not available 400 responses on all trys (files, days, download). which commands are working for you currently? in your example you have "parking" as part of the command, but this is not here in the description, and so far not avalible in my build... may I will try your version... |
parking is my cam name |
|
neolink.zip |
|
Thanks. it now worked also for me. just no mp4 stream... to be rechecked ;) |
|
Hi @MasterPlexus, For the "no mp4 stream" issue — I think I know what's happening. The download code has a heuristic to detect whether the camera is sending an MP4 container (like the E1 does) vs. raw BcMedia frames (like older cameras). That heuristic is too aggressive and likely misidentifies your Argus 2's BcMedia stream as a To confirm, could you run the download command with debug logging and share the output? RUST_LOG=debug neolink replay --config=config.toml download --name --output test.mp4 2>&1 | head -100 I'm specifically looking for a log line that says something like: If you see that line, that confirms the bug. The hex bytes in brackets would also be helpful to include. Thanks for testing! |
|
Here it is my bash script , is dirty but work, you need to adjust save root path and cam |
|
My cams are |
|
ok. not sure why I thought we have argus 3 cams. I have argus 2: Anyhow, access possible, and I can download the files. but the files are scrambled. Even that I know they are stored on sd-card as mp4 files. So may the cam transcode it again, or the file will be krypted during transfer... I need to check that... |
|
@lorek123 perhaps enable "issue" on your fork could be more efficient. |
|
OK, no error on neolink side. was an error in my script not to take care to extract the real file name, and garbage was part of it (like the size, md, and so on). Now it worked. |
Detect video codec from BcMedia IFrame/PFrame video_type field and thread it through the entire mux pipeline. H.265 streams now use h265parse + video/x-h265 caps in GStreamer (previously hardcoded to h264parse), fixing "No valid frames found" / "non-existing SPS 32" errors on mainStream cameras. Also adds is_likely_valid_h265_nal() for H.265 NAL validation and updates ffmpeg fallback to use -f hevc / .replay.h265 extension. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
|
@surfzoid the mainStream issue is now fixed. The problem was that mainStream on cameras like Argus 3 / Argus PT uses H.265/HEVC video, but the code was hardcoded to pipe everything through The fix detects the codec from the BcMedia frame headers and automatically uses Please test with the latest build from the |
|
Some cameras (e.g. Reolink Argus PT) send H.265 video data but label it as "H264" in BcMedia frame headers — a firmware bug that caused h264parse and ffmpeg to fail with "No valid frames found" / "no frame!". Add detect_codec_from_nal_bytes() which identifies the real codec by looking for H.265-exclusive NAL types (VPS=32/SPS=33/PPS=34, byte0 0x40/0x42/0x44) or H.264 SPS/PPS/IDR. Used in both the buffered decode path (try_decode_bcmedia_nals) and the live streaming path to override the video_type field when it disagrees with the actual data. Also derive PartialEq+Eq on VideoType to enable comparison. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
|
@surfzoid found another issue — the Argus PT has a firmware bug where it labels H.265 video frames as New fix: instead of trusting the header label, we inspect the actual NAL bytes. H.265 VPS/SPS/PPS have NAL unit types 32–34 (first byte after start code = Please test with the latest |
|
@lorek123 wonderful, good job. I will fired an alarm on my third cam to full validate but i m optimistic :-) |
|
@lorek123 On my third cam, Argus 3 Firmware Version 0130_705_572_474 i got the video ok. |
On cameras like Argus 3, mainStream and subStream recordings are stored as separate files. Listing with --stream subStream (old default) and then downloading with --stream mainStream would return 400 because the file names differ between streams. Now 'replay files' queries both streams when --stream is not specified, merging results in a single listing. Users can still pass --stream mainStream/subStream to query a specific stream only. The output already shows the stream type per file so users can pass the correct --stream to the download/play command. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
|
@surfzoid good catch on the stream-specific file listing! The Fixed: Please test with the latest build! |
|
That's only cosmetic, i'm not sure it's usefull, wait and see the build. |
|
@lorek123 your last comit is ok. |
|
Could it be that the responding list of files is stopping at 40 entrys? there are more videos on the cam, but files get only 40 entrys maximum back (I'm still one commit behind). |
40 per days ! you should adjust your setup! |
|
Tell that the hedgehogs :D |
@lorek123 I had a look and consulted Gemini, which explains to me, that in the get files handle no pagination is established like in the pub async fn alarm_video_search_next. Unfortunately I'm not really stable in Rust, so I could not not try to implement it... So would be great if you could have a look. Of course if you have time, thanks anyhow to all your efforts! |
get_file_list_by_handle now loops calling MSG 15 with the same handle until the camera returns an empty batch or a non-200 response, matching the alarm_video_search_next pagination pattern. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
|
@MasterPlexus The file list pagination is now fixed. Previously MSG 15 was only called once per handle, capping results at the camera's single-page limit (~40 files). The code now loops MSG 15 with the same handle until the camera returns an empty batch, so all files for the day will be returned regardless of count. The fix is in the latest commit on the branch. @surfzoid's build should pick it up automatically. |
I pulled, rebuild, and tested it: worked perfectly now! Thanks! For the direct download part, I'm thinking about to try to use network sn.if.fer on my android, to check if the app is able to send this kind of step, and to see how the payload may looks like. I assume a parameter is may missed for the trigger of the Download... Other way could be to de.com.pile a firmware where we know this functionality is part of. but currently I don't know which can do it, and not shure how hard the firmwares are ob#fus*cated. But of course this has time ;) |
|
@MasterPlexus I briefly used https://github.com/emanuele-f/PCAPdroid, I can recommend it if you want to analyse app network traffic. For RE I never had a chance to take a look at the firmware, but dlls and so's from from desktop/android apps are handled pretty well by Claude code + Ghidra MCP combo. I'll take a look in the meantime for the direct download option. |
|
I found on reolink support page, that the reolink PC client has direct download possibilities. So I guess the best is to try to sniff on that stage. I will give it a try (but it will take a Moment) If others will try: https://support.reolink.com/articles/900000645323-How-to-Download-All-Recordings-at-Once-for-Reolink-Devices/ |
|
Hi @lorek123 . I tried a little bit, not easy. Anyhow, just as an Idea, could it may be that the cam is expecting to have the UID set instead of zeros for the direct download? |
The BC_DOWNLOAD_BY_NAME_INFO struct has a 32-byte cUID field at offset 0x04 that was left zeroed. Some cameras (e.g. Argus 2) may require the actual UID to accept MSG 8. Now fetches the UID from the camera before building the payload, matching the layout of BC_DOWNLOAD_BY_TIME_INFO. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
|
@MasterPlexus Good call on the UID — the |
i had sync your commit with my fork, build is automatically started and should be ok in few minutes |
Thanks. I tied it after a pull, but unfortunately it does not change anything. I have some questions die to the sniffing: |
|
I have a new idea which I will test tomorrow. I need to check how the file name really looks like on the SD card. May the request did not accept the only numeric code as a request. I assume we need to send the real name of the file on the SD card... Will check tomorrow, and give a feedback. |
The Filenames are set on my argus 2 in this way: Rec_20260409_235436_151_M.mp4 wich looks like different then just the number code. anyhow, as the stream and convert approach is working it looks like this does not change something to use the real file names.. |
|
on Model Reolink Argus 3, sometime i get : Perhaps network micro-outage? |

Summary
neolink disk <camera> listanddisk formatto inspect and format SD cardsreplay alarm-searchNew subcommands
Details
--ai-filterfor client-side filteringencryptPos/encryptLenfrom Extension XML; protocol v2 payload_offset for continuation packets (class 0x698d)FILE_SEARCH_RECORD_TYPES(manual,sched,md,pir,io) for MSG 14 — E1 cameras reject AI-specific tags in file search requestsreplay downloadinsteadbaichuan.luawith replay message ID decodingTested on
disk list,days,files,download(MP4 with H.264 + AAC) all workingalarm-searchgracefully reports "not supported"download-by-timegracefully reports "not supported" (MSG 143 not implemented on E1)Test plan
neolink disk <camera> listshows SD card infoneolink replay <camera> days --start <date>lists recording daysneolink replay <camera> files --date <date>lists files with AI tags and correct sizesneolink replay <camera> download --name <file> --output test.mp4produces playable MP4replay alarm-searchreturns results or "not supported" messagereplay download-by-timereturns clear error on cameras that don't support MSG 143