{"id":13907652,"url":"https://github.com/Eyevinn/mp4ff","last_synced_at":"2025-07-18T06:30:34.541Z","repository":{"id":37003803,"uuid":"237257326","full_name":"Eyevinn/mp4ff","owner":"Eyevinn","description":"Library and tools for working with MP4 files containing video, audio, subtitles, or metadata. The focus is on fragmented files. Includes mp4ff-info, mp4ff-encrypt, mp4ff-decrypt and other tools.","archived":false,"fork":false,"pushed_at":"2025-07-16T20:46:14.000Z","size":4765,"stargazers_count":537,"open_issues_count":0,"forks_count":99,"subscribers_count":19,"default_branch":"master","last_synced_at":"2025-07-17T12:18:42.745Z","etag":null,"topics":["aac","ac-3","adts","avc","cmaf","ec-3","fmp4","fragmented-mp4-files","h264","hevc","isobmff","mp4","mpeg-dash","pps","sei","sps","stpp","video","vp9","wvtt"],"latest_commit_sha":null,"homepage":"https://dev.to/video/mp4ff-beyond-mp4-boxes-2bee","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Eyevinn.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-01-30T16:41:52.000Z","updated_at":"2025-07-16T20:46:17.000Z","dependencies_parsed_at":"2023-12-27T08:28:08.112Z","dependency_job_id":"135fa54d-cbf4-4294-aa3b-e0c64dc42a1e","html_url":"https://github.com/Eyevinn/mp4ff","commit_stats":{"total_commits":690,"total_committers":21,"mean_commits":"32.857142857142854","dds":0.4217391304347826,"last_synced_commit":"506e8aabc8a4391d99e77be2158c464b13471318"},"previous_names":["edgeware/mp4ff","edgeware/gomp4"],"tags_count":56,"template":false,"template_full_name":null,"purl":"pkg:github/Eyevinn/mp4ff","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Eyevinn%2Fmp4ff","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Eyevinn%2Fmp4ff/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Eyevinn%2Fmp4ff/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Eyevinn%2Fmp4ff/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Eyevinn","download_url":"https://codeload.github.com/Eyevinn/mp4ff/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Eyevinn%2Fmp4ff/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265710530,"owners_count":23815373,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aac","ac-3","adts","avc","cmaf","ec-3","fmp4","fragmented-mp4-files","h264","hevc","isobmff","mp4","mpeg-dash","pps","sei","sps","stpp","video","vp9","wvtt"],"created_at":"2024-08-06T23:02:04.174Z","updated_at":"2025-07-18T06:30:34.530Z","avatar_url":"https://github.com/Eyevinn.png","language":"Go","readme":"![Logo](images/logo.png)\n\n![Test](https://github.com/Eyevinn/mp4ff/workflows/Go/badge.svg)\n[![Coverage Status](https://coveralls.io/repos/github/Eyevinn/mp4ff/badge.svg?branch=master)](https://coveralls.io/github/Eyevinn/mp4ff?branch=master)\n[![GoDoc](https://godoc.org/github.com/Eyevinn/mp4ff?status.svg)](http://godoc.org/github.com/Eyevinn/mp4ff)\n[![Go Report Card](https://goreportcard.com/badge/github.com/Eyevinn/mp4ff)](https://goreportcard.com/report/github.com/Eyevinn/mp4ff)\n[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#video)\n[![license](https://img.shields.io/github/license/Eyevinn/mp4ff.svg)](https://github.com/Eyevinn/mp4ff/blob/master/LICENSE)\n[![Badge OSC](https://img.shields.io/badge/Evaluate-24243B?style=for-the-badge\u0026logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGNpcmNsZSBjeD0iMTIiIGN5PSIxMiIgcj0iMTIiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl8yODIxXzMxNjcyKSIvPgo8Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSI3IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utd2lkdGg9IjIiLz4KPGRlZnM%2BCjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQwX2xpbmVhcl8yODIxXzMxNjcyIiB4MT0iMTIiIHkxPSIwIiB4Mj0iMTIiIHkyPSIyNCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSIjQzE4M0ZGIi8%2BCjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzREQzlGRiIvPgo8L2xpbmVhckdyYWRpZW50Pgo8L2RlZnM%2BCjwvc3ZnPgo%3D)](https://app.osaas.io/browse/eyevinn-mp4ff)\n\nModule mp4ff implements MP4 media file parsing and writing for AVC and HEVC video, AAC and AC-3 audio, stpp and wvtt subtitles, and\ntimed metadata tracks.\nIt is focused on fragmented files as used for streaming in MPEG-DASH, MSS and HLS fMP4, but can also decode and encode all\nboxes needed for progressive MP4 files.\n\n## Command Line Tools\n\nSome useful command line tools are available in [cmd](cmd) directory.\n\n1. [mp4ff-info](cmd/mp4ff-info) prints a tree of the box hierarchy of a mp4 file with information\n    about the boxes.\n2. [mp4ff-pslister](cmd/mp4ff-pslister) extracts and displays SPS and PPS for AVC or HEVC in a mp4 or a bytestream (Annex B) file.\n    Partial information is printed for HEVC.\n3. [mp4ff-nallister](cmd/mp4ff-nallister) lists NALUs and picture types for video in progressive or fragmented file\n4. [mp4ff-subslister](cmd/mp4ff-subslister) lists details of wvtt or stpp (WebVTT or TTML in ISOBMFF) subtitle samples\n5. [mp4ff-crop](cmd/mp4ff-crop) crops a **progressive** mp4 file to a specified duration\n6. [mp4ff-encrypt](cmd/mp4ff-encrypt) encrypts a fragmented file using cenc or cbcs Common Encryption scheme\n7. [mp4ff-decrypt](cmd/mp4ff-decrypt) decrypts a fragmented file encrypted using cenc or cbcs Common Encryption scheme\n\nYou can install these tools by going to their respective directory and run `go install .` or directly from the repo with\n\n    go install github.com/Eyevinn/mp4ff/cmd/mp4ff-info@latest\n    go install github.com/Eyevinn/mp4ff/cmd/mp4ff-encrypt@latest\n    ...\n\nfor each individual tool.\n\n## Open Source Cloud\n\nYou can also run the tools as a job in [Eyevinn Open Source Cloud](https://app.osaas.io/dashboard/service/eyevinn-mp4ff). Here is an example using the `mp4ff-crop` command and the Open Source Cloud CLI.\n\n```bash\n% export OSC_ACCESS_TOKEN=\u003cyour-personal-access-token\u003e\n% npx -y @osaas/cli@latest create eyevinn-mp4ff test \\\n  -o awsAccessKeyId=\u003cs3-access-key-id\u003e \\\n  -o awsSecretAccessKey=\u003cs3-secret-key\u003e \\\n  -o s3EndpointUrl=https://eyevinnlab-birme.minio-minio.auto.prod.osaas.io \\\n  -o cmdLineArgs=\"mp4ff-crop s3://input/VINN.mp4 s3://output/VINN-crop2.mp4\"\n```\n\nThe file VINN.mp4 on the bucket called \"input\" on the MinIO server at https://eyevinnlab-birme.minio-minio.auto.prod.osaas.io is processed and output uploaded to bucket \"output\" on the same MinIO server.\n\n## Example code\n\nExample code for some common use cases is available in the [examples](examples) directory.\nThe examples and their functions are:\n\n1. [initcreator](examples/initcreator) creates typical init segments (ftyp + moov) for different video and\n    audio codecs\n2. [resegmenter](examples/resegmenter) reads a segmented file (CMAF track) and resegments it with other\n    segment durations using `FullSample`\n3. [segmenter](examples/segmenter) takes a progressive mp4 file and creates init and media segments from it.\n    This tool has been extended to support generation of segments with multiple tracks as well\n    as reading and writing `mdat` in lazy mode\n4. [multitrack](examples/multitrack) parses a fragmented file with multiple tracks\n5. [combine-segs](examples/combine-segs) combines single-track init and media segments into multi-track segments\n6. [add-sidx](examples/add-sidx) adds a top-level sidx box describing the segments of a fragmented files.\n\n## Packages\n\nThe top-level packages in the mp4ff module are\n\n1. [mp4](mp4) provides support for for parsing (called Decode) and writing (Encode) a plethor of mp4 boxes.\n   It also contains helper functions for extracting, encrypting, dectrypting samples and a lot more.\n2. [avc](avc) deals with AVC (aka H.264) video in the `mp4ff/avc` package including parsing of SPS and PPS,\n    and finding start-codes in Annex B byte streams.\n3. [hevc](hevc) provides structures and functions for dealing with HEVC video and its packaging.\n4. [vvc](vvc) provides structures and functions for dealing with VVC video and its packaging.\n5. [sei](sei) provides support for handling  Supplementary Enhancement Information (SEI) such as timestamps\n   for AVC and HEVC video.\n6. [av1](av1) provides basic support for AV1 video packaging\n7. [aac](aac) provides support for AAC audio. This includes handling ADTS headers which is common\n   for AAC inside MPEG-2 TS streams.\n8. [bits](bits) provides bit-wise and byte-wise readers and writers used by the other packages.\n\n## Structure and usage\n\n### mp4.File and its composition\n\nThe top level structure for both non-fragmented and fragmented mp4 files is `mp4.File`.\n\nIn a progressive (non-fragmented) `mp4.File`, the top-level attributes Ftyp, Moov, and Mdat point to the corresponding boxes.\n\nA fragmented `mp4.File` can be more or less complete, like a single init segment,\none or more media segments, or a combination of both, like a CMAF track which renders\ninto a playable one-track asset. It can also have multiple tracks.\nFor fragmented files, the following high-level attributes are used:\n\n* `Init` contains a `ftyp` and a `moov` box and provides the general metadata for a fragmented file.\n   It corresponds to a CMAF header. It can also contain one or more `sidx` boxes.\n* `Segments` is a slice of `MediaSegment` which start with an optional `styp` box, possibly one or more `sidx`\n   boxes and then one or more`Fragment`s.\n* `Fragment` is a mp4 fragment with exactly one `moof` box followed by a `mdat` box where the latter\n   contains the media data. It can have one or more `trun` boxes containing the metadata\n   for the samples. The fragment can start with one or more `emsg` boxes.\n\nIt should be noted that it is sometimes hard to decide what should belong to a Segment or Fragment.\n\nAll child boxes of container boxes such as `MoovBox` are listed in the `Children` attribute, but the\nmost prominent child boxes have direct links with names which makes it possible to write a path such\nas\n\n```go\nfragment.Moof.Traf.Trun\n```\n\nto access the (only) `trun` box in a fragment with only one `traf` box, or\n\n```go\nfragment.Moof.Trafs[1].Trun[1]\n```\n\nto get the second `trun` of the second `traf` box (provided that they exist). Care must be\ntaken to assert that none of the intermediate pointers are nil to avoid `panic`.\n\n### Creating new fragmented files\n\nA typical use case is to generate a fragmented file consisting of an init segment\nfollowed by a series of media segments.\n\nThe first step is to create the init segment. This is done in three steps as can be seen in\n`examples/initcreator`:\n\n```go\ninit := mp4.CreateEmptyInit()\ninit.AddEmptyTrack(timescale, mediatype, language)\ninit.Moov.Trak.SetHEVCDescriptor(\"hvc1\", vpsNALUs, spsNALUs, ppsNALUs)\n```\n\nHere the third step fills in codec-specific parameters into the sample descriptor of the single track.\nMultiple tracks are also available via the slice attribute `Traks` instead of `Trak`.\n\nThe second step is to start producing media segments. They should use the timescale that\nwas set when creating the init segment. Generally, that timescale should be chosen so that the\nsample durations have exact values without rounding errors, e.g. 48000 for 48kHz audio.\n\nA media segment contains one or more fragments, where each fragment has a `moof` and a `mdat` box.\nIf all samples are available before the segment is created, one can use a single\nfragment in each segment. Example code for this can be found in `examples/segmenter`.\nFor low-latency MPEG-DASH generation, short-duration fragments are added to the segment as the\ncorresponding media samples become available.\n\nA simple, but not optimal, way of creating a media segment is to first create a slice of `FullSample` with the data needed.\nThe definition of `mp4.FullSample` is\n\n```go\nmp4.FullSample{\n Sample: mp4.Sample{\n  Flags uint32 // Flag sync sample etc\n  Dur   uint32 // Sample duration in mdhd timescale\n  Size  uint32 // Size of sample data\n  Cto   int32  // Signed composition time offset\n },\n DecodeTime uint64 // Absolute decode time (offset + accumulated sample Dur)\n Data       []byte // Sample data\n}\n```\n\nThe `mp4.Sample` part is what will be written into the `trun` box.\n`DecodeTime` is the media timeline accumulated time.\nThe `DecodeTime` value of the first sample of a fragment, will\nbe set as the `BaseMediaDecodeTime` in the `tfdt` box.\n\nOnce a number of such full samples are available, they can be added to a media segment like\n\n```go\nseg := mp4.NewMediaSegment()\nfrag := mp4.CreateFragment(uint32(segNr), mp4.DefaultTrakID)\nseg.AddFragment(frag)\nfor _, sample := range samples {\n frag.AddFullSample(sample)\n}\n```\n\nThis segment can finally be output to a `w io.Writer` as\n\n```go\nerr := seg.Encode(w)\n```\n\nor to a `sw bits.SliceWriter` as\n\n```go\nerr := seg.EncodeSW(sw)\n```\n\nFor multi-track segments, the code is a bit more involved. Please have a look at `examples/segmenter`\nto see how it is done. A more optimal way of handling media sample is\nto handle them lazily, or using intervals, as explained next.\n\n### Lazy decoding and writing of mdat data\n\nFor video and audio, the dominating part of a mp4 file is the media data which is stored\nin one or more `mdat` boxes. In some cases, for example when segmenting large progressive\nfiles, it is much more memory efficient to just read the movie or fragment metadata\nfrom the `moov` or `moof` box and defer the reading of the media data from the `mdat` box\nto later.\n\nFor decoding, this is supported by running `mp4.DecodeFile()` in lazy mode as\n\n```go\nparsedMp4, err = mp4.DecodeFile(ifd, mp4.WithDecodeMode(mp4.DecModeLazyMdat))\n```\n\nIn this case, the media data of the `mdat` box will not be read, but only its size is being saved.\nTo read or copy the actual data corresponding to a sample, one must calculate the\ncorresponding byte range and either call\n\n```go\nfunc (m *MdatBox) ReadData(start, size int64, rs io.ReadSeeker) ([]byte, error)\n```\n\nor\n\n```go\nfunc (m *MdatBox) CopyData(start, size int64, rs io.ReadSeeker, w io.Writer) (nrWritten int64, err error)\n```\n\nExample code for this, including lazy writing of `mdat`, can be found in `examples/segmenter`\nwith the `lazy` mode set.\n\n### More efficient I/O using SliceReader and SliceWriter\n\nThe use of the interfaces `io.Reader` and `io.Writer` for reading and writing boxes gives a lot of\nflexibility, but is not optimal when it comes to memory allocation. In particular, the\n`Read(p []byte)` method needs a slice `p` of the proper size to read data, which leads to a\nlot of allocations and copying of data.\nIn order to achieve better performance, it is advantageous to read the full top level boxes into\none, or a few, slices and decode these.\n\nTo enable that mode, version 0.27 of the code introduced `Decode\u003cX\u003eSR(sr bits.SliceReader)`\nmethods to every box `\u003cX\u003e` where `mp4ff.bits.SliceReader` is an interface.\nFor example, the `TrunBox` gets the method `DecodeTrunSR(sr bits.SliceReader)` in addition to its old\n`DecodeTrun(r io.Reader)` method. The `bits.SliceReader` interface provides methods to read all kinds\nof data structures from an underlying slice of bytes. It has an implementation `bits.FixedSliceReader`\nwhich uses a fixed-size slice as underlying slice, but one could consider implementing a growing version\nwhich would get its data from some external source.\n\nThe memory allocation and speed improvements achieved by this may vary, but should be substantial,\nespecially compared to versions before 0.27 which used an extra `io.LimitReader` layer.\n\nFur further reduction of memory allocation, use a buffered top-level reader, especially when\nwhen reading the `mdat` box of a progressive file.\n\n#### Benchmarks\n\nTo investigate the efficiency of the new SliceReader and SliceWriter methods, benchmarks have been done.\nThe benchmarks are defined in\nthe file `mp4/benchmarks_test.go` and `mp4/benchmarks_srw_test.go`.\nFor `DecodeFile`, one can see a big improvement by going from version\n0.26 to version 0.27 which both use the `io.Reader` interface\nbut another big increase by using the `SliceReader` source.\nThe latter benchmarks are called `BenchmarkDecodeFileSR` but have\nhere been given the same name, for easy comparison.\nNote that the allocations here refers to the heap allocations\nthat are done inside the benchmark loop. Outside that loop,\na slice is allocated to keep the input data.\n\nFor `EncodeFile`, one can see that v0.27 is actually worse\nthan v0.26 when used with the `io.Writer` interface. That is\nbecause the code was restructured so that all writes go\nvia the `SliceWriter` layer in order to reduce code duplication.\nHowever, if instead using the `SliceWriter` methods directly,\nthere is a big relative gain in allocations as can be seen in\nthe last column.\n\n| name \\ time/op           |  v0.26 |  v0.27 | v0.27-srw |\n| ------------------------ | ------ | ------ | --------- |\n|DecodeFile/1.m4s-16       | 21.9µs |  6.7µs |    2.6µs  |\n|DecodeFile/prog_8s.mp4-16 |  143µs |   48µs |     16µs  |\n|EncodeFile/1.m4s-16       | 1.70µs | 2.14µs |   1.50µs  |\n|EncodeFile/prog_8s.mp4-16 | 15.7µs | 18.4µs |   12.9µs  |\n\n| name \\ alloc/op          | v0.26 |  v0.27 | v0.27-srw |\n| ------------------------ |------ | ------ | --------- |\nDecodeFile/1.m4s-16     |    120kB |   28kB |       2kB |\nDecodeFile/prog_8s.mp4-16 |  906kB |  207kB |      12kB |\nEncodeFile/1.m4s-16       | 1.16kB | 1.39kB |    0.08kB |\nEncodeFile/prog_8s.mp4-16 | 6.84kB | 8.30kB |    0.05kB |\n\n| name \\ allocs/op         | v0.26 | v0.27 | v0.27-srw |\n| ------------------------ |------ | ----- | --------- |\n|DecodeFile/1.m4s-16       |  98.0 |  42.0 |      34.0 |\n|DecodeFile/prog_8s.mp4-16 |   454 |   180 |       169 |\n|EncodeFile/1.m4s-16       |  15.0 |  15.0 |       3.0 |\n|EncodeFile/prog_8s.mp4-16 |   101 |    86 |         1 |\n\n## More about mp4 boxes\n\nThe `mp4ff.mp4` contains a lot of box implementations.\n\n### Box structure and interface\n\nMost boxes have their own file named after the box, but in some cases, there may be multiple boxes\nthat have the same content, and the code file then has a generic name like\n`mp4/visualsampleentry.go`.\n\nThere is an interface for boxes: `Box` specificied in `mp4.box.go`,\n\nThe interfaces define common Box methods including encode (writing),\nbut not the decode (parsing) methods which have distinct names for each box type and are\ndispatched from the parsed box name.\n\nThat dispatch based on box name is defined by the tables `mp4.decodersSR` and `mp4.decoders`\nfor the functions `mp4.DecodeBoxSR()` and `mp4.DecodeBox()`, respectively.\nThe `SR` variant should normally be used for better performance.\nIf a box name is unkonwn, it will result in an `UnknownBox` being created.\n\n### How to implement a new box\n\nTo implement a new box `fooo`, the following is needed.\n\nCreate a file `fooo.go` and create a struct type `FoooBox`.\n\n`FoooBox` must implement the Box interface methods:\n\n```go\nType()\nSize()\nEncode(w io.Writer)\nEncodeSW(sw bits.SliceWriter)\nInfo()\n```\n\nIt also needs its own decode methods `DecodeFoooSR` and  `DecodeFooo`,\nwhich must be added in the `decodersSR` map and `decoders` map, respectively\nFor a simple example, look at the `PrftBox` in `prft.go`.\n\nA test file `fooo_test.go` should also have a test using the method `boxDiffAfterEncodeAndDecode`\nto check that the box information is equal after encoding and decoding.\n\n## Direct changes of attributes\n\nMany attributes are public and can therefore be changed in freely.\nThe advantage of this is that it is possible to write code that can manipulate boxes\nin many different ways, but one must be cautious to avoid breaking links to sub boxes or\ncreate inconsistent states in the boxes.\n\nAs an example, container boxes such as `TrafBox` have a method `AddChild` which\nadds a box to `Children`, its slice of children boxes, but also sets a specific\nmember reference such as `Tfdt` to point to that box. If `Children` is manipulated\ndirectly, that link may no longer be valid.\n\n## Encoding modes and optimizations\n\nFor fragmented files, one can choose to either encode all boxes in a `mp4.File`, or only code\nthe ones which are included in the init and media segments. The attribute that controls that\nis called `FragEncMode`.\nAnother attribute `EncOptimize` controls possible optimizations of the file encoding process.\nCurrently, there is only one possible optimization called `OptimizeTrun`.\nIt can reduce the size of the `TrunBox` by finding and writing default\nvalues in the `TfhdBox` and omitting the corresponding values from the `TrunBox`.\nNote that this may change the size of all ancestor boxes of `trun`.\n\n## Sample Number Offset\n\nFollowing the ISOBMFF standard, sample numbers and other numbers start at 1 (one-based).\nThis applies to arguments of functions and methods.\nThe actual storage in slices is zero-based, so sample nr 1 has index 0 in the corresponding slice.\n\n## Contributing\n\nWhen contributing to this project, please ensure that commit messages follow the [Conventional Commits](https://www.conventionalcommits.org/) specification. This helps maintain a consistent and readable commit history.\n\nExamples of conventional commit messages:\n- `feat: add support for VVC video codec`\n- `fix: resolve memory leak in fragment processing`\n- `docs: update API documentation for mp4.File`\n- `chore: update dependencies to latest versions`\n\n## Stability\n\nThe APIs should be fairly stable, but minor non-backwards-compatible changes may happen until version 1.\n\n## Specifications\n\nThe main specification for the MP4 file format is the ISO Base Media File Format (ISOBMFF) standard\nISO/IEC 14496-12 7th edition 2021. Some boxes are specified in other standards, as should be commented\nin the code.\n\n## LICENSE\n\nMIT, see [LICENSE](LICENSE).\n\nSome code in pkg/mp4, comes from or is based on \u003chttps://github.com/jfbus/mp4\u003e which has\n`Copyright (c) 2015 Jean-François Bustarret`.\n\nSome code in pkg/bits comes from or is based on \u003chttps://github.com/tcnksm/go-casper/tree/master/internal/bits\u003e\n`Copyright (c) 2017 Taichi Nakashima`.\n\n## ChangeLog and Versions\n\nSee [CHANGELOG.md](CHANGELOG.md).\n\n## Support\n\nJoin our [community on Slack](http://slack.streamingtech.se) where you can post any questions regarding any of our open source projects. Eyevinn's consulting business can also offer you:\n\n* Further development of this component\n* Customization and integration of this component into your platform\n* Support and maintenance agreement\n\nContact [sales@eyevinn.se](mailto:sales@eyevinn.se) if you are interested.\n\n## About Eyevinn Technology\n\n[Eyevinn Technology](https://www.eyevinntechnology.se) is an independent consultant firm specialized in video and streaming. Independent in a way that we are not commercially tied to any platform or technology vendor. As our way to innovate and push the industry forward we develop proof-of-concepts and tools. The things we learn and the code we write we share with the industry in [blogs](https://dev.to/video) and by open sourcing the code we have written.\n\nWant to know more about Eyevinn and how it is to work here. Contact us at work@eyevinn.se!\n","funding_links":[],"categories":["Go","Video","HarmonyOS","视频"],"sub_categories":["Utility/Miscellaneous","Windows Manager","实用程序/Miscellaneous"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FEyevinn%2Fmp4ff","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FEyevinn%2Fmp4ff","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FEyevinn%2Fmp4ff/lists"}