* read video metadata * from ComfyUI_VideoHelperSuite * @param file * @returns
(file: File)
| 5 | * @returns |
| 6 | */ |
| 7 | function getVideoMetadata(file: File) { |
| 8 | return new Promise((resolve) => { |
| 9 | const reader = new FileReader(); |
| 10 | reader.onload = (event: ProgressEvent<FileReader>) => { |
| 11 | const videoData = new Uint8Array(event.target!.result as ArrayBuffer); |
| 12 | const dataView = new DataView(videoData.buffer); |
| 13 | |
| 14 | let decoder = new TextDecoder(); |
| 15 | // Check for known valid magic strings |
| 16 | if (dataView.getUint32(0) == 0x1A45DFA3) { |
| 17 | //webm |
| 18 | //see http://wiki.webmproject.org/webm-metadata/global-metadata |
| 19 | //and https://www.matroska.org/technical/elements.html |
| 20 | //contrary to specs, tag seems consistently at start |
| 21 | //COMMENT + 0x4487 + packed length? |
| 22 | //length 0x8d8 becomes 0x48d8 |
| 23 | // |
| 24 | //description for variable length ints https://github.com/ietf-wg-cellar/ebml-specification/blob/master/specification.markdown |
| 25 | let offset = 4 + 8; //COMMENT is 7 chars + 1 to realign |
| 26 | while (offset < videoData.length - 16) { |
| 27 | //Check for text tags |
| 28 | if (dataView.getUint16(offset) == 0x4487) { |
| 29 | //check that name of tag is COMMENT |
| 30 | const name = String.fromCharCode(...videoData.slice(offset - 7, offset)); |
| 31 | if (name === "COMMENT") { |
| 32 | let vint = dataView.getUint32(offset + 2); |
| 33 | let n_octets = Math.clz32(vint) + 1; |
| 34 | if (n_octets < 4) {//250MB sanity cutoff |
| 35 | let length = (vint >> (8 * (4 - n_octets))) & ~(1 << (7 * n_octets)); |
| 36 | const content = decoder.decode(videoData.slice(offset + 2 + n_octets, offset + 2 + n_octets + length)); |
| 37 | const json = JSON.parse(content); |
| 38 | resolve(json); |
| 39 | return; |
| 40 | } |
| 41 | } |
| 42 | } |
| 43 | offset += 1; |
| 44 | } |
| 45 | } else if (dataView.getUint32(4) == 0x66747970 && dataView.getUint32(8) == 0x69736F6D) { |
| 46 | //mp4 |
| 47 | //see https://developer.apple.com/documentation/quicktime-file-format |
| 48 | //Seems to make no guarantee for alignment |
| 49 | let offset = videoData.length - 4; |
| 50 | while (offset > 16) {//rough safe guess |
| 51 | if (dataView.getUint32(offset) == 0x64617461) {//any data tag |
| 52 | if (dataView.getUint32(offset - 8) == 0xa9636d74) {//cmt data tag |
| 53 | let type = dataView.getUint32(offset + 4); //seemingly 1 |
| 54 | let locale = dataView.getUint32(offset + 8); //seemingly 0 |
| 55 | let size = dataView.getUint32(offset - 4) - 4 * 4; |
| 56 | const content = decoder.decode(videoData.slice(offset + 12, offset + 12 + size)); |
| 57 | const json = JSON.parse(content); |
| 58 | resolve(json); |
| 59 | return; |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | offset -= 1; |
| 64 | } |