Skip to content
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

Matroska : Auto-populate SeekHead when adding new children under Segment #5

Open
Zeugma440 opened this issue Aug 18, 2024 · 7 comments
Assignees
Labels
bug Something isn't working enhancement New feature or request

Comments

@Zeugma440
Copy link
Contributor

Right now, when adding brand new children under the Segment node (e.g. adding Tags or Attachments to an untagged file), one needs to manually handle the creation of its index under SeekHead.

Problem is, when adding a new node from scratch, the client app has no idea what its position should be, as SpawnDev.EBML handles low-level I/O.

I can't find a proper way to get around it. My most recent attempt is as follows :
1- Add new Segment child node (e.g. Tags)
2- Add new Seek node under SeekHead with its SeekPosition valued to TEMP_INT64
3- Save the file
4- Reopen it
5- Read the position of the node created at Step 1 using BaseElement.Stream.Offset
6- Write that position on the SeekPosition node created at Step 2 instead of TEMP_INT64

There are two issues with that method :

  • The client app ventures way too low for comfort
  • I can't find a proper value for TEMP_INT64 because of VINT encoding again
    • If TEMP_INT64 is valued to zero at step 2, it will be encoded with one single byte. Using its actual value at step 6 will inflate its size, shifting the position of the node created at Step 1 as a consequence... and making the position we've just written invalid 🤦
    • If TEMP_INT64 is valued to the size of the Matroska file, there's no way of telling where the new node will be created. We could encounter the opposite case where TEMP_INT64 takes up, say, 4 bytes as a VINT and gets rewritten with 3 bytes only during Step 6

Some of the spec does give suggestions to make files easier to edit after they are created. Leaving the SeekPosition the max (8 bytes) may be one of those suggestions.

Wouldn't that case be a solid argument to apply this suggestion? 😁

@LostBeard
Copy link
Owner

I should have Seek elements working correctly by tomorrow. 👍

I have rewritten a lot of the library and added a Blazor EBML Editor demo.

The library now uses (and includes) XML schema files:

It can now add and CRC-32 values thanks to Crc32.NET

@LostBeard LostBeard self-assigned this Aug 20, 2024
@LostBeard LostBeard added the bug Something isn't working label Aug 20, 2024
@LostBeard
Copy link
Owner

LostBeard commented Aug 20, 2024

Just figured it out.

Apparently the value written to \Segment\SeekHead\Seek'SeekPosition is the position of the target element not including the size of (any?) the SeekHead's size. This way, you don't have to take into account the size of the elements you are going to write to SeekHead.

SeekPosition = SeekIDelement.Position - SeekHead.Size

SeekIDelement.Position = SeekPosition + SeekHead.Size

This helps. 👍

@LostBeard LostBeard added the enhancement New feature or request label Aug 20, 2024
@Zeugma440
Copy link
Contributor Author

I should have Seek elements working correctly by tomorrow. 👍

I have rewritten a lot of the library and added a Blazor EBML Editor demo.

Awesome! Thanks a bunch for your efforts 🤝 I can't wait to try it 😀

Just figured it out [...]

A-ha! I've been focused on the definition of Segment Position and didn't realize SeekPosition followed another logic. Man, the Matroska format is one hell of a beast...

@LostBeard
Copy link
Owner

LostBeard commented Aug 20, 2024

A-ha! I've been focused on the definition of Segment Position and didn't realize SeekPosition followed another logic.

Nice, you appear to have been on the right track and I was off last night with my conclusion. I was seeing a pattern but I mixed up the numbers (I mixed up SeekHead position and size.) Segment Position appears to be the answer.

Here is a reference image using the Blazor EBML viewer I linked to.

EBML SeekPosition view

Red links SeekID and its target: 0x1549A966 (\Segment\Info)
Yellow is the start position of Segment element data: 52
Purple is the position of the SeekID target: 213
Green is the SeekPosition: 161

So the SeekID target's position is:
SeekID target position = SeekPosition + Segment data start position
213 = 161 + 52
SeekPosition = SeekID target position - Segment data start position
161 = 213 - 52

👍

Matroska format is one hell of a beast...

lol I agree.

@LostBeard
Copy link
Owner

LostBeard commented Aug 20, 2024

First preview of the editor with automatic SeekHead updating is up. When using the Blazor Editor, if you open devtools with the current build, there is some debug output indicating when the library checks SeekHead and updates it. CRC auto updating is also implemented and logs to console.

What app are you using to view .mka cover art?

You can click an element's value cell to edit it. Binary types can be saved to a file and a file can be loaded into a binary element's value.

I also added support for viewing images that are in binary elements. Just click on the binary element row and if the data is an image, it will try to show it in the right bottom preview pane.

@LostBeard
Copy link
Owner

LostBeard commented Aug 21, 2024

Okay, I just updated the Blazor editor. SeekHead will now be auto updated and auto populated if it exists in a document. Auto populating means that if SeekHead exists and any of these top level elements, (Info, Tracks, Chapters, Cues, Attachments) also exist, a Seek entry will be created for each top level element if it does not exist. Document changes will cause the values to be updated if needed.

To demo:

  • Open the Blazor EBML Editor
  • File > New > matroska (a minimal matroska EBML will be created)
  • Enter the Segment element (double click)
  • + > SeekHead (adds an empty Seekhead element)
  • + > Info

You can then go into SeekHead and see the auto generated Seek element which contains SeekID and SeekPosition that target the Info element.

There are API settings to disable/enable auto-populate, crc auto update, etc. I will update the README with example code soon.

@Zeugma440
Copy link
Contributor Author

First of all thanks a bunch for all the hard work 💪

I'm done merging your updates with mine, and am still in the process of rewriting what I've done client-side to adapt to v2 API changes.

I'll get back to you soon regarding the two issues I posted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants