-
Notifications
You must be signed in to change notification settings - Fork 335
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
Port to TypeScript #424
base: master
Are you sure you want to change the base?
Port to TypeScript #424
Conversation
simplifies consuming the types
(and other type fixes) this allows for greater type narrowing on consumer's end.
NOTE: this box previously did not have a constructor, so I guessed the box-type to extend from.
and update trefBox to extend from ContainerBox instead of Box
- keep only BoxRegistry and DescriptorRegistry - offer tighter typing to compensate - export BoxRegistry as BoxParser in all/simple
- move all/simple-entries to ./entries - move src/lib to src keeps the diff closer to the original
replace MP4BoxBuffer-type with MP4BoxBuffer-class
Hi @bigmistqke, I haven't gone through the code but the description is very well written. We have been wanting to port to TypeScript for quite some time now. I guess this is a really good start. I have some ideas on testing, especially with the new conformance framework. https://mpeggroup.github.io/FileFormatConformance/ Also for tooling, I was thinking about tsup + vitest instead of vite. I will go through this PR in more detail soon. Thank you for the efforts. |
Hi @DenizUgur Great to hear there is interest from the core team too to push this further! Regarding |
- cleanup package.json - generate sourcemaps Opted for scripts instead of tsup.config.ts, as otherwise tsup would also generate .d.cts files
@DenizUgur btw I am up for having a videochat, going through the code together high level, if that would be helpful for you. |
instead of relying on SampleEntry.type
before - super('hldr', ...) after - super('hdlr', ...) bug came apparent when porting samples/audio-video-player from w3c/webcodecs see https://github.com/bigmistqke/webcodecs-vite/tree/main/samples/audio-video-player
and do not register these in BoxRegistry
in the javascript source (see commit 43c8b7a) createBoxCtor adds the given type-argument to `BoxParser.boxCodes.push(type);`, similar pushes were commented out in other createXBoxCtor and still present in createSampleEntry. this was ported to a call in the constructor, but this is not functionally identical: the calls in the factory-functions were only called when declaring the class, not during each instance creation. currently I removed this functionality and did not replace it, if we want to re-introduce this functionality I would implement it via exports.
before - BoxBase has a type-property declared as a public argument - all classes extending from BoxBase pass type through super(type) - this makes that for each class-instance the type of .type is 'string' after - BoxBase has a type-property but it is not initialised in its constructor - each class-instance declares its type in the declaration: p.ex class moovBox { type = 'moov' ... } - UUIDBoxes has their uuid inlined as well - remove redundant UUIDBox-base - this way we can narrow depending on the BoxKind.type - refactored ISOFile.parse to make use of this additional type-information: `switch (box.type) { case 'mdat': ...` would narrow the type of box to `mdatBox` - added BoxKind to @types: infers all classes that extend Box from BoxRegistry - added SampleEntryKind to @types: infers all classes that extend SampleEntry from BoxRegistry
before - in iinfBox.parse we would check if result of parseOneBox is instanceof infeBox - this would fail in case BoxRegistry does not have infeBox, in which case generic Box would be created with Box.type = 'infe' after - cast result of parseOneBox to BoxKind - check if box.type === 'infe'
several import fixes
Played around a bit more with it today:
Screen.Recording.2024-12-02.at.22.17.53.mov |
Hi @bigmistqke, that sounds great. I'm preoccupied with other things right now, but I might be able to allocate some time for this in early January. In the meantime, I'll list a few ideas I had for a partial rewrite. Note that this is not an exhaustive list:
There might be other things to address, but the most important thing is to stay within the scope of this PR's goal: transitioning to TypeScript. There's always more to implement :) Thank you again for your ongoing efforts! |
add tsup config, remove concurrently
infer values from given StructDefinitions
before - Box.addBox(boxClass) does 2 actions: - sets this[boxClass.type] to boxClass - pushes boxClass to this[boxClass.type + 's'] - this[boxClass.type] was already typed after - type each this[boxClass.type + "s"]
- add dev-directory - port filereader-example to typescript - add partial typings for jquery, jquery.fancytree, jquery-ui and d3 (chatgpt generated, so we can see the code through the red squiggles)
filereader-demo does not work otherwise, unclear why
It's a bit annoying that it creates a declaration-file for both
Not yet immediately clear to me how to implement this. Would we then also need to pass a map of all boxes and to which dynamically loaded bundle they belong? If we want to handle dynamically loading during parsing, everything would suddenly become async too, which would complicate things. Maybe it could be done during initialisation, instead? import BoxesA from "mp4box/a"
import BoxesB from "mp4box/b"
import { register } from "mp4box"
register(BoxesA, BoxesB) Then it's upto the user to determine which box-collections they want to include.
Went
Ported the file inspection demo to Ported file inspection demo with
|
also added typechecking to writeStruct.mp4 |
Hi 👋
I have been exploring WebCodec API for the past weeks and during it I came across this library.
It bothered me that I wasn't able to explore its api easily, because of the pre-module bundling strategy and lack of types.
I had seen that there was interest from @cconcolato to port to typescript, but that this was unlikely to happen soon due to time constraints, so I have been exploring this option. This PR is a result of this exploration.
The changes have been quite drastic, as the javascript version relied on a lot of type-unsafe, pre-es6 coding styles and bundler. I understand that this PR might be a bit overwhelming as it touches a lot of code and I needed to make a lot of choices. It's a bit rude to barge into a project like this and change the code so drastically, my bad.
Main changes
vite
instead ofgrunt
to bundle the files.BoxParser
(prevents accessing boxes that are not exported by the entry)src/write
andsrc/parse
), but are written in 1 file as 1 class declaration (src/boxes
).all
/simple
-entry is done through exporting 2 different entries, instead of grunt-config (see entries-directory)StructDefinition
is refactored from[key, value, key, value, ...]
to[[key, value], [key, value], ...]
as this is a lot clearer to type.Box.add(boxName)
andBox.set(propertyName, value)
toBox.addBox(new BoxClass())
and setting the properties directly on the class-instance.var
refactored toconst/let
and inlined variable declarations instead of declaring all variables up-frontinstanceof
checks instead of checking the.type
property.Circular Dependencies
With the introduction of import-paths also the potential for circular dependencies is introduced which occurred in an early version of the port. To prevent circular dependencies while still allowing for allowing
all
/simple
or other customised bundles, there is aregister
-pattern:BoxRegistry
-object.BoxRegistry[...]
.Partial<AllBoxes>
, whereAllBoxes
are all the boxes exported from theall
-entry.This would make it possible too for third parties to make their own treeshakable bundles too.
We do the same for
Descriptor
withDescriptorRegistry
.I added
dpdm
to the dev-dependencies to assist with keeping an eye on this: withnpm run circular
a check is done for circular dependencies.Tests
I have tried to port the tests too, but a lot seem to rely on video content that is not available anymore. The only proof I have that this port is somehow successful is my working port of the video-decode-display sample of the
w3c/webcodec
repo. [EDIT]: theaudio-video-player
sample is now working too.Bundle Size
The size grew quite a bit:
150 kB
or33 kB (gzip)
(from bundlejs)221 kB
or51 kB (gzip)
Conclusion
Screen.Recording.2024-11-20.at.20.30.03.mov
Pretty fun to be able to explore the boxes like this! The typescript declaration file is pretty impressive
I will now stop with exploring this port further. The project is in a workable state as far as I can tell, but I have no way to properly test this. I personally don't have an interest in maintaining a typescript fork, because I know little to nothing about mp4 codecs. I am open for continuing if there is an interest in mergin this PR eventually from the core contributors.
Open Questions
// NOTE
comments.unknowns
in the codebase and// @ts-expect-error
, but most of it has been typed.parse()
expects: it seems at some places it is also passed aMP4BoxStream
, butwriteHeader
expects a.writeUInt32
method that isn't present onMP4BoxStream
.Descriptor.parseRemainingDescriptors
MPEG4DescriptorParser
(extends Descriptor's prototype inside MPEG4DescriptorParser's constructor, together with a bunch of other methods onDescriptor
andES_Descriptor
)MPEG4DescriptorParser
-specific methods insideDescriptor.parseRemainingDescriptors
:var desc = that.parseOneDescriptor(stream);
(withthat
being a reference toMPEG4DescriptorParser
)parseOneDescriptor
method fromMPEG4DescriptorParser
toDescriptor
, but not 100% sure this covers the original intent.ac-3
becomesac_3SampleEntry
UUIDBoxes
are defined in an objectUUID_BOXES = { ... }
because some of them start with a number.strict: false
. if we would want to enforce strict typescript we will have to either reconsider some of the API-choices (p.exBox.addBox
) or cast a lot more to any :-)