{"id":29906848,"url":"https://github.com/olliejones/h264-interp-utils","last_synced_at":"2025-08-01T21:14:40.359Z","repository":{"id":42666597,"uuid":"338117009","full_name":"OllieJones/h264-interp-utils","owner":"OllieJones","description":"H.264 data streams are tricky to interpret. This Javascript package helps.","archived":false,"fork":false,"pushed_at":"2023-03-05T20:51:46.000Z","size":1977,"stargazers_count":13,"open_issues_count":6,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-07-24T18:49:34.066Z","etag":null,"topics":["golomb","h264","h264-parser","nalu-utilities","variable-length-encoding"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/OllieJones.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-02-11T18:32:36.000Z","updated_at":"2024-08-03T11:47:14.000Z","dependencies_parsed_at":"2023-02-06T12:46:16.162Z","dependency_job_id":null,"html_url":"https://github.com/OllieJones/h264-interp-utils","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/OllieJones/h264-interp-utils","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OllieJones%2Fh264-interp-utils","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OllieJones%2Fh264-interp-utils/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OllieJones%2Fh264-interp-utils/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OllieJones%2Fh264-interp-utils/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/OllieJones","download_url":"https://codeload.github.com/OllieJones/h264-interp-utils/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OllieJones%2Fh264-interp-utils/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268297626,"owners_count":24228128,"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","status":"online","status_checked_at":"2025-08-01T02:00:08.611Z","response_time":67,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["golomb","h264","h264-parser","nalu-utilities","variable-length-encoding"],"created_at":"2025-08-01T21:14:20.600Z","updated_at":"2025-08-01T21:14:40.342Z","avatar_url":"https://github.com/OllieJones.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# h264-interp-utils [![Tests](https://github.com/OllieJones/h264-interp-utils/actions/workflows/node.js.yml/badge.svg)](https://github.com/OllieJones/h264-interp-utils/actions/workflows/node.js.yml) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com) \n\nH.264 bitstreams are tricky to handle. This Javascript package helps.\n\nIt handles the creation and parsing of H.264's codec-private data. This codec-private data is \nstored in `'avcC'` atoms in MPEG-4 streams, and \nin [`Tracks/Track/CodecPrivate` elements](https://www.matroska.org/technical/elements.html#codecprivate-element) \nin some [Matroska streams](https://www.matroska.org/technical/elements.html) (aka webm or EBML files). \nIt is sometimes necessary to re-create this codec-private data from elements in a\ncompressed video bitstream.\n\nIt handles the parsing of sequences of H.264 Network Access Layer Units (NALUs), formatted either in packet-transport or \nstreaming Annex B format.\n\nIt offers functions for reading H.264's variable-length\n[Exponential Golomb codes](https://en.wikipedia.org/wiki/Exponential-Golomb_coding)\nfrom its bitstream.\nWith those functions it handles the parsing of Sequence Parameter Set (SPS) and \nPicture Parameter Set (PPS) NALUs.\n\n## Install\n\nInstall with [npm](https://www.npmjs.com/):\n\n```sh\n$ npm install --save h264-interp-utils\n```\n\nInstallation with other package managers works similarly.\n\n## Why this package\n\nThe original reason to develop this package is to allow the reconstruction of\n`'avcC'` atom data from \n[MediaRecorder](https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder) -emitted \ndata. When using MediaRecorder with a MIME type like `video/webm; codecs=\"avc1.42C01E\"`,\nit generates a data stream without placing \ncodec-private data in \n[`Tracks/Track/CodecPrivate` elements](https://www.matroska.org/technical/elements.html#codecprivate-element).\nBut, the experimental \n[WebCodecs browser API](https://github.com/WICG/web-codecs/blob/master/explainer.md) \nrequires that data to be passed to it in a `config.description` \nelement. Hence the need to reconstruct it.\n\nMediaRecorder-emitted video streams repeat \nthe H.264 SPS and PPS NALUs at the beginning of the data for each intraframe.\nIn Matroska parlance, these are `keyframe`s. In H.264 parlance they are I frames. \nEach intraframe in simple low-latency MediaEncoder-emitted video streams \nalso happens to be an Instantaneous Decoder Refresh (IDR) frame; \ndecoding can begin at that point in the video stream without\nreference to any previous data.\n\nThe `AvcC` class in this package reconstructs the codec-private data from MediaRecorder's \nintraframe data  stream, by interpreting the SPS and PPS NALUs in that datastream.\n\n \n## Usage\n\nStart by including the module in your program.\n\n```js\nconst H264Util = require('h264-interp-utils')\n```\n\n### Bitstream\n\nThe Bitstream object allows its user to retrieve data bit-by-bit from arrays of data.\nIt's used to parse NALUs, and supports the H.264 variable-length\nexponential Golomb coding for \nsigned and unsigned integers. \n\nYou give the constructor an array containing a single NALU, \nwithout any leading NALU delimiter.\n\n\n```js\nconst bitstream = new H264Util.Bitstream(array)\nconst aBit = bitstream.u_1()\nconst nextBit = bitstream.u_1()\nconst twoBits = bitstream.u_2()\nconst fiveBits = bitstream.u(5)\nconst aByte = bitstream.u_8()\n\n/* variable-length exponential-Golomb coded integers */\nconst unsignedInt = bitstream.ue_v()\nconst signedInt = bitstream.se_v()\n```\n\nYou may retrieve the number of remaining bits in your array with `const bitCount = bitstream.remaining`.\nLikewise, you may retrieve the number of bits already consumed with `const bitsUsed = bitstream.consumed`.\n\nFor debugging convenience, the `bitstream.peek16` getter shows, in a text string, the next 16 bits.\n\nBitstream's constructor removes \n[*emulation prevention bytes*](https://en.wikipedia.org/wiki/Network_Abstraction_Layer#NAL_Units_in_Byte-Stream_Format_Use)\nfrom the array.\n\nYou may retrieve the stream, with emulation prevention bytes,  with\n```js\nconst originalStream = bitstream.stream\n```\n\nYou may write bits into the stream:\n\n```js\nconst dest = new Bitstream (4096)\ndest.put_u_1(1)           /* one bit */\ndest.put_u_1(0)           /* another bit */\nconst threebits = 6  \ndest.put_u(threebits, 3)  /* three bits from a number */\nconst val = 7\nlet bitcount = dest.put_ue_v(val)/* exponential Golomb coded value, unsigned */\nbitcount = dest.put_se_v(-val)   /* exponential Golomb coded value, signed */\n\nbitccount = dest.put_complete()  /* done writing to Bitstream, tie it off */\n```\n\nYou may copy bits from some other stream into the stream with `copyBits()`:\n\n```js\nconst source = new Bitstream (nalu)\n/* empty bitstream, same size as source */\nconst dest = new Bitstream(source.remaining) \nconst startAtBit = 0\nconst copyBitCount = source.remaining\ndest.copyBits(source, startAtBit, copyBitCount)\n```\n\n`copyBits()` is written to be reasonably efficient even for randomly\nbit-aligned copies.\n\n#### NALUStream\n\nNALUStream accepts a buffer containing a sequence of NALUs.\nThey may be in\n\n* packet format, separated by 4, 3, or 2-byte NALU lengths\n* AnnexB stream format, separated by four-byte `00 00 00 01` or three byte `00 00 01` delimiters.\n\nNALUStream's constructor takes both an array and an options object.\nThe options object many contain any of these properties:\n\n* `type`, if present, has the value `'packet'` or `'annexB'`.\nUse it to declare the format of your sequence of NALUs. \nIf you omit `type`, \nNALUStream attempts to determine the format by examining the first few NALUs.\nTry to avoid attempting that whenever possible.\n\n* `boxSize`, if present, can have values 4,3, or 2 for `'packet'` streams, and 4 or 3 for `'annexB'` streams.\nIf you omit `boxSize` NALUStream attempts to determine the boxSize by examining the first few NALUs.\nTry to avoid attempting that whenever possible.\n\n* `boxSizeMinusOne` can be provided in place of `boxSize` for compatibility with `'avcC'` atoms.\n\n* `strict`, if true, makes NALUStream throw more errors when it detects anomalous data.\n\n\nConstructing a NALUStream might look like this. It's wise to catch errors thrown by the constructor.\n\n```js\ntry {\nconst nalus = new H264Util.NALUStream(array, {type:'annexB', boxSize: 4, strict: true})\n} catch (error) {\n  console.error(error)\n}\n```\n\nYou may iterate over the NALUs in a NALUStream like this:\n\n```js\nfor (const nalu of nalus) {                           \n  /* handle each NALU */\n}\n```\nor this, if you want both the NALUs and \ntheir raw counterparts with the leading delimiters\nstill present\n```js\nfor (const n of nalus.nalus()) {\n  const { nalu, rawNalu } = n\n  /* handle each nalu */\n}\n```\n\nSomewhat less efficiently you can iterate like this:\n\n```js\nconst naluArray = nalus.packets\nfor (let i= 0; i \u003c naluArray.length; i++) {\n  /* handle each naluArray[i] */\n}\n```\n\nSome decoders (for example the VideoDecoder in WebCodecs) require their NALUs in\npacket format. You can convert a NALUStream to packet format like this. Notice that it\nchanges the contents of the array passed in the constructor.\n\n```js\ndecoder.decode(nalus.convertToPacket())\n```\n\nNALUStream objects have `type`, `boxSize`, and `boxSizeMinusOne` properties. \nIf you use the constructor to guess what sort of array you gave it, \nyou can retrieve its guesses with those properties.\n\nNALUStream objects have the `packetCount` property indicating how many NALUs are in the array.\n\n#### SPS\n\nSPS accepts a Stream Parameter Set NALU, and offers properties describing it.\nTo construct an SPS object, give it an array containing a NALU. \n(It throws an error when you give it a NALU that's not an SPS, or that's garbled\nin a way that makes it impossible to decode.)\n\n```js\nconst sps = new H264Util.SPS(nalu)\n```\n\nSome of its useful properties are:\n\n* `MIME`: the MIME type of the video stream, a value like `'avc1.640029'`.\n* `profileName`: a human-readable value like `'BASELINE'` or `'EXTENDED'` indicating the codec profile.\n* `profile_idc`: the profile indicator. 66 means baseline, 77 means main, and 88 means extended.\n* `profile_compatibility`: the constraints.\n* `level_idc`: the level indicator for the codec level.\n* `picWidth`, `picHeight`: the width and height of the pictures in the video stream.\n* `cropRect`: a rectangle object with `x`, `y`, `width`, `height`. In the cases where the\npictures in the video stream have margins without imagery in them, the `cropRect` defines\nthe useful area. \n\n   Because H.264 streams have sizes that are multiples of 16x16 macroblocks,\nit can be necessary to crop the pictures when rendering them.\n\nIt has what can only be described as a mess of properties defined by the H.264 standard\nand needed by the H.264 decoder to make sense of the stream.\n\n#### PPS\n\nPPS accepts a Picture Parameter Set NALU, and offers properties describing it.\nTo construct an PPS object, give it an array containing a NALU. \n(It throws an error when you give it a NALU that's not an PPS, or that's garbled\nin a way that makes it impossible to decode.)\n\n```js\nconst pps = new H264Util.PPS(nalu)\n```\n\nMost PPS properties describe the format of the pictures in the video stream\nin a format useful to the H.264 decoder.\n\nTwo of its more useful properties are:\n\n* `entropy_coding_mode_flag`: 0 for CALVC Huffman-style entropy coding, and  1 for CABAC Arps-style arithmetic coding.\n* `entropyCodingMode`: `'CAVLC'` or `'CABAC'`, a human-readable description of the entropy coding.\n\nIt has what can only be described as a mess of properties defined by the H.264 standard\nand needed by the H.264 decoder to make sense of the stream.\n\n#### AvcC\n\nH.264 defines a set of codec-private data describing the data stream. Not all video data streams have a distinct\nset of codec-private data: it's optional in Matroska / webm / .mkv video streams.\n\nIt contains, embedded in it, one or more SPS and PPS elements. It can be reconstructed by parsing an SPS and including a PPS.\n\nThe AvcC class parses and reconstructs the codec-private data.\n\nA typical use case is, given arrays containing SPS and PPS NALUs, create an avcC object.\n\n```js\nconst avcCObject = new H264Util.AvcC({pps:ppsArray, sps:spsArray})\nconst mime = avcCObject.MIME\n/* this is the binary array to put into the `'avcC'` atom. */\nconst codecPrivateDataArray = avCObject.avcC\n```\n\nAnother typical use case is, given a key frame payload from a [Matroska SimpleBlock](https://www.matroska.org/technical/basics.html#simpleblock-structure), create the codec-private data.\n\n```js\nconst avcCObject = new H264Util.AvcC({bitstream: payload})\nconst mime = avcCObject.MIME\n/* this is the binary array to put into the `'avcC'` atom. */\nconst codecPrivateDataArray = avCObject.avcC\n```\n\n#### Slice\n\nSlice accepts I-frame (type 5) and P-frame (type 1) NALUs,\nand offers a few properties from decoding them.\n\nTo construct a Slice object, give it an array containing a NALU,\nand optionally an AvcC object created from the same data stream.\n\n(It throws an error when you give it a NALU that's not type 1 or 5.)\n\n```js\nconst slice = new H264Util.Slice(nalu, avcC)\n```\n\nSome of its useful properties are:\n\n* `first_mb_in_slice`: the number of the first macroblock in the present slice.\n  This is zero for the first slice of a new frame.\n* `slice_type`: The [type](https://en.wikipedia.org/wiki/Video_compression_picture_types#Macroblocks) of this slice. 0,5: P slice. 1,6: B slice, 2,7: I slice, 3, 8: SP slice, 4,9: SI slice.\n* `frame_num`: The number of this frame in sequence after the most recent I-frame. This is only \n  available if you provide an `avcC` object.\n* `pic_parameter_set_id`: the index of the PPS describing this slice\n\nIt is sometimes necessary to change a slice's `pic_parameter_set_id`. (A bug in Chrome's encoder makes it use\nmultiple different values.)\n\n```js\nconst fixedNalu = slice.setPPSId(0)\nconst fixedSlice = new H264Util.Slice(nalu, avcC)\nconst ppsId = fixedSlice.pic_parameter_set_id /* will be 0 */\n```\n\n\n## Still to do\n\n* Rework Bitstream and NALUStream to handle Javascript streams, not just static arrays of data.\n\n## Credits\n\n* [Alex Izvorksi](https://github.com/aizvorski) for his [C++ h264bitstream](https://github.com/aizvorski/h264bitstream) code.\n\n* Of course, the legions of people who created H.264. ","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Folliejones%2Fh264-interp-utils","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Folliejones%2Fh264-interp-utils","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Folliejones%2Fh264-interp-utils/lists"}