@poe-platform/safe-bash 0.1.830 → 0.1.832

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,6 +18,9 @@ export interface ParsedH264Sps {
18
18
  readonly sarHeight: number;
19
19
  readonly fps?: number | undefined;
20
20
  readonly maxNumRefFrames: number;
21
+ readonly maxNumReorderFrames?: number | undefined;
22
+ readonly colorRange?: string | undefined;
23
+ readonly colorSpace?: string | undefined;
21
24
  }
22
25
  export declare function parseH264Sps(nalu: Uint8Array): ParsedH264Sps;
23
26
  export declare function parseAvcC(payload: Uint8Array): Mp4AvcCConfig;
@@ -171,6 +171,7 @@ export interface MediaCodecDescription {
171
171
  readonly codecTagString?: string | undefined;
172
172
  readonly profile?: string | undefined;
173
173
  readonly level?: number | undefined;
174
+ readonly maxNumReorderFrames?: number | undefined;
174
175
  readonly width?: number | undefined;
175
176
  readonly height?: number | undefined;
176
177
  readonly pixFmt?: string | undefined;
@@ -331956,6 +331956,21 @@ var H264_PROFILES = {
331956
331956
  122: "High 4:2:2",
331957
331957
  244: "High 4:4:4 Predictive"
331958
331958
  };
331959
+ var H264_COLOR_SPACES = {
331960
+ 0: "gbr",
331961
+ 1: "bt709",
331962
+ 4: "fcc",
331963
+ 5: "bt470bg",
331964
+ 6: "smpte170m",
331965
+ 7: "smpte240m",
331966
+ 8: "ycgco",
331967
+ 9: "bt2020nc",
331968
+ 10: "bt2020c",
331969
+ 11: "smpte2085",
331970
+ 12: "chroma-derived-nc",
331971
+ 13: "chroma-derived-c",
331972
+ 14: "ictcp"
331973
+ };
331959
331974
  var H264_SAR_TABLE = [
331960
331975
  [1, 1],
331961
331976
  [1, 1],
@@ -332075,6 +332090,9 @@ function parseH264Sps(nalu) {
332075
332090
  let sarWidth = 1;
332076
332091
  let sarHeight = 1;
332077
332092
  let fps;
332093
+ let maxNumReorderFrames;
332094
+ let colorRange;
332095
+ let colorSpace;
332078
332096
  const vuiPresent = bits.readBit();
332079
332097
  if (vuiPresent && bits.bitsRemaining > 0) {
332080
332098
  const aspectRatioInfoPresent = bits.readBit();
@@ -332093,10 +332111,11 @@ function parseH264Sps(nalu) {
332093
332111
  const videoSignalTypePresent = bits.readBit();
332094
332112
  if (videoSignalTypePresent) {
332095
332113
  bits.readBits(3);
332096
- bits.readBit();
332114
+ colorRange = bits.readBit() ? "pc" : "tv";
332097
332115
  const colourDescriptionPresent = bits.readBit();
332098
332116
  if (colourDescriptionPresent) {
332099
- bits.readBits(24);
332117
+ bits.readBits(16);
332118
+ colorSpace = H264_COLOR_SPACES[bits.readBits(8)];
332100
332119
  }
332101
332120
  }
332102
332121
  const chromaLocInfoPresent = bits.readBit();
@@ -332108,10 +332127,34 @@ function parseH264Sps(nalu) {
332108
332127
  if (timingInfoPresent && bits.bitsRemaining >= 64) {
332109
332128
  const numUnitsInTick = bits.readBits(32);
332110
332129
  const timeScale = bits.readBits(32);
332130
+ bits.readBit();
332111
332131
  if (numUnitsInTick > 0 && timeScale > 0) {
332112
332132
  fps = timeScale / (2 * numUnitsInTick);
332113
332133
  }
332114
332134
  }
332135
+ let hrdPresent = false;
332136
+ for (let kind = 0; kind < 2; kind++) {
332137
+ if (!bits.readBit()) continue;
332138
+ hrdPresent = true;
332139
+ const cpbCount = bits.readUE() + 1;
332140
+ bits.readBits(8);
332141
+ for (let i = 0; i < cpbCount && bits.bitsRemaining > 0; i++) {
332142
+ bits.readUE();
332143
+ bits.readUE();
332144
+ bits.readBit();
332145
+ }
332146
+ bits.readBits(20);
332147
+ }
332148
+ if (hrdPresent) bits.readBit();
332149
+ bits.readBit();
332150
+ if (bits.readBit()) {
332151
+ bits.readBit();
332152
+ bits.readUE();
332153
+ bits.readUE();
332154
+ bits.readUE();
332155
+ bits.readUE();
332156
+ maxNumReorderFrames = bits.readUE();
332157
+ }
332115
332158
  }
332116
332159
  let profileName = H264_PROFILES[profileIdc] ?? `Profile ${profileIdc}`;
332117
332160
  if (profileIdc === 66 && (constraintFlags & 64) !== 0) {
@@ -332135,7 +332178,10 @@ function parseH264Sps(nalu) {
332135
332178
  sarWidth,
332136
332179
  sarHeight,
332137
332180
  fps,
332138
- maxNumRefFrames
332181
+ maxNumRefFrames,
332182
+ maxNumReorderFrames,
332183
+ colorRange,
332184
+ colorSpace
332139
332185
  };
332140
332186
  }
332141
332187
  function parseAvcC(payload) {
@@ -332895,6 +332941,9 @@ function applyCodecMetadata(state, child) {
332895
332941
  metadata.level = child.levelIdc;
332896
332942
  if (child.sps) {
332897
332943
  const sps = parseH264Sps(child.sps);
332944
+ metadata.maxNumReorderFrames = sps.maxNumReorderFrames;
332945
+ metadata.colorRange = sps.colorRange;
332946
+ metadata.colorSpace = sps.colorSpace;
332898
332947
  metadata.profile = sps.profileName;
332899
332948
  metadata.width = sps.width || metadata.width;
332900
332949
  metadata.height = sps.height || metadata.height;
@@ -333036,13 +333085,13 @@ function buildProbeStream(track, desc, stats, idx, containerFormat) {
333036
333085
  height: h,
333037
333086
  coded_width: w ? Math.ceil(w / 16) * 16 : void 0,
333038
333087
  coded_height: h ? Math.ceil(h / 16) * 16 : void 0,
333039
- has_b_frames: track.type === "video" ? stats.hasCts ? 1 : 0 : void 0,
333088
+ has_b_frames: track.type === "video" ? desc?.maxNumReorderFrames ?? (stats.hasReorderedSamples ? 1 : 0) : void 0,
333040
333089
  sample_aspect_ratio: track.type === "video" ? `${desc?.sarWidth ?? 1}:${desc?.sarHeight ?? 1}` : void 0,
333041
333090
  display_aspect_ratio: track.type === "video" && w && h ? `${w / darGcd}:${h / darGcd}` : void 0,
333042
333091
  pix_fmt: track.type === "video" ? desc?.pixFmt ?? "yuv420p" : void 0,
333043
333092
  level: desc?.level,
333044
- color_range: track.type === "video" ? "tv" : void 0,
333045
- color_space: track.type === "video" ? "bt709" : void 0,
333093
+ color_range: track.type === "video" ? desc?.colorRange : void 0,
333094
+ color_space: track.type === "video" ? desc?.colorSpace : void 0,
333046
333095
  sample_fmt: sampleFormat,
333047
333096
  sample_rate: track.type === "audio" ? String(desc?.sampleRate ?? track.timescale) : void 0,
333048
333097
  channels: track.type === "audio" ? desc?.channels ?? 2 : void 0,
@@ -334937,16 +334986,32 @@ function buildProbeResultFromDoc(doc, byteLength, filename = "input.mp4", option
334937
334986
  const streams = [];
334938
334987
  const packets = [];
334939
334988
  const frames = [];
334989
+ let formatStart = Infinity, formatEnd = -Infinity;
334940
334990
  for (let idx = 0; idx < doc.tracks.length; idx++) {
334941
334991
  const track = doc.tracks[idx];
334942
334992
  const materialized = materializeTrackSamples(track);
334943
334993
  const desc = materialized.codecDescriptions[0];
334944
- const stream = buildProbeStream(track, desc, {
334994
+ let firstPts = materialized.samples[0]?.pts ?? 0;
334995
+ if (track.editList?.length) {
334996
+ let delay = 0;
334997
+ for (const edit of track.editList) {
334998
+ if (edit.mediaTime >= 0) break;
334999
+ delay += edit.segmentDuration;
335000
+ }
335001
+ firstPts = Math.round(delay * track.timescale / Math.max(1, doc.timescale));
335002
+ }
335003
+ const duration2 = Math.max(0, track.duration - (track.editList?.length ? firstPts : 0));
335004
+ if (doc.containerFormat === "mp4" || doc.containerFormat === "mov") {
335005
+ const start = firstPts / Math.max(1, track.timescale);
335006
+ formatStart = Math.min(formatStart, start);
335007
+ formatEnd = Math.max(formatEnd, start + duration2 / Math.max(1, track.timescale));
335008
+ }
335009
+ const stream = buildProbeStream({ ...track, duration: duration2 }, desc, {
334945
335010
  totalBytes: materialized.samples.reduce((total, sample) => total + sample.size, 0),
334946
335011
  count: materialized.samples.length,
334947
335012
  ticks: materialized.samples.reduce((total, sample) => total + sample.duration, 0),
334948
- firstPts: materialized.samples[0]?.pts ?? 0,
334949
- hasCts: materialized.samples.some((sample) => sample.cts !== 0)
335013
+ firstPts,
335014
+ hasReorderedSamples: materialized.samples.some((sample, i, samples) => i > 0 && sample.pts < samples[i - 1].pts)
334950
335015
  }, idx, doc.containerFormat);
334951
335016
  streams.push(stream);
334952
335017
  const w = stream.width, h = stream.height, sampleFormat = stream.sample_fmt;
@@ -335008,7 +335073,9 @@ function buildProbeResultFromDoc(doc, byteLength, filename = "input.mp4", option
335008
335073
  const value = doc.metadata[key];
335009
335074
  if (value) formatTags[key] = value;
335010
335075
  }
335011
- const overallBitRate = doc.durationSeconds > 0 ? String(Math.round(byteLength * 8 / doc.durationSeconds)) : "0";
335076
+ const startTime = Number.isFinite(formatStart) ? formatStart : 0;
335077
+ const duration = Number.isFinite(formatEnd) ? Math.max(0, formatEnd - startTime) : doc.durationSeconds;
335078
+ const overallBitRate = duration > 0 ? String(Math.round(byteLength * 8 / duration)) : "0";
335012
335079
  return {
335013
335080
  streams,
335014
335081
  format: {
@@ -335017,8 +335084,8 @@ function buildProbeResultFromDoc(doc, byteLength, filename = "input.mp4", option
335017
335084
  nb_programs: 0,
335018
335085
  format_name: options.formatName ?? "mov,mp4,m4a,3gp,3g2,mj2",
335019
335086
  format_long_name: options.formatLongName ?? "QuickTime / MOV",
335020
- start_time: "0.000000",
335021
- duration: doc.durationSeconds.toFixed(6),
335087
+ start_time: startTime.toFixed(6),
335088
+ duration: duration.toFixed(6),
335022
335089
  size: String(byteLength),
335023
335090
  bit_rate: overallBitRate,
335024
335091
  probe_score: 100,
@@ -339872,7 +339939,7 @@ async function* scanProbeRecords(source, options, mode) {
339872
339939
  movieDuration = version === 1 ? movie.readU64BE() : movie.readU32BE();
339873
339940
  }
339874
339941
  const mvex = await first(moov, "mvex");
339875
- let trackIndex = 0, maxDuration = movieDuration / Math.max(1, movieTimescale);
339942
+ let trackIndex = 0, maxDuration = movieDuration / Math.max(1, movieTimescale), formatStart = Infinity, formatEnd = -Infinity;
339876
339943
  for await (const trak of scanMp4Boxes(source, { ...moov.children, ...work })) {
339877
339944
  if (trak.type !== "trak") continue;
339878
339945
  const index = trackIndex++;
@@ -339965,8 +340032,9 @@ async function* scanProbeRecords(source, options, mode) {
339965
340032
  }
339966
340033
  }
339967
340034
  }
339968
- let duration = 0, mediaEnd = 0, allPacked = true, count = 0;
340035
+ let duration = 0, mediaEnd = 0, allPacked = true, count = 0, firstPts = 0;
339969
340036
  for await (const sample of samples()) {
340037
+ if (!count) firstPts = sample.pts;
339970
340038
  count++;
339971
340039
  duration += sample.duration;
339972
340040
  mediaEnd = Math.max(mediaEnd, sample.pts + sample.duration);
@@ -339982,32 +340050,39 @@ async function* scanProbeRecords(source, options, mode) {
339982
340050
  const version = header2.readU8();
339983
340051
  header2.skip(3);
339984
340052
  const count2 = header2.readU32BE(), width2 = version === 1 ? 20 : 12;
339985
- let total = 0, entries = 0;
340053
+ let total = 0, entries = 0, delay = 0, foundMedia = false;
339986
340054
  for (let i = 0; i < count2 && 8 + i * width2 < elst.payloadSize; i++) {
339987
340055
  const row = new BinaryReader(yield { kind: "read", table: elst, offset: elst.payloadOffset + 8 + i * width2, length: Math.min(width2, elst.payloadSize - 8 - i * width2) });
339988
340056
  let segment = version === 1 ? row.readU64BE() : row.readU32BE();
339989
340057
  const mediaTime = version === 1 ? row.readI64BE() : row.readI32BE();
339990
340058
  if (fragmented && segment === 0 && mediaTime >= 0) segment = Math.round(Math.max(0, mediaEnd - mediaTime) * movieTimescale / timescale);
340059
+ if (mediaTime >= 0) foundMedia = true;
340060
+ else if (!foundMedia) delay += segment;
339991
340061
  total += segment;
339992
340062
  entries++;
339993
340063
  }
339994
- return entries ? Math.round(total * timescale / movieTimescale) : void 0;
340064
+ return entries ? { duration: Math.round(total * timescale / movieTimescale), start: Math.round(delay * timescale / movieTimescale) } : void 0;
339995
340065
  }
339996
340066
  const edits = consumeMp4SampleSteps(source, editDuration(), sampleOptions), result = await edits.next();
339997
340067
  if (!result.done) throw new Error("Unexpected MP4 edit sample");
339998
- const trackDuration = result.value ?? (duration || declaredDuration || Math.round(headerDuration / Math.max(1, movieTimescale) * timescale));
340068
+ const trackDuration = result.value?.duration ?? (duration || declaredDuration || Math.round(headerDuration / Math.max(1, movieTimescale) * timescale));
339999
340069
  maxDuration = Math.max(maxDuration, trackDuration / Math.max(1, timescale));
340000
340070
  budget.checkDuration(maxDuration);
340071
+ const startPts = result.value?.start ?? (type === "subtitle" && count && !allPacked ? 0 : firstPts);
340072
+ const probeDuration = Math.max(0, trackDuration - (result.value?.start ?? 0));
340073
+ const start = startPts / Math.max(1, timescale);
340074
+ formatStart = Math.min(formatStart, start);
340075
+ formatEnd = Math.max(formatEnd, start + probeDuration / Math.max(1, timescale));
340001
340076
  if (mode === "summary") continue;
340002
- let bytePos = 0, cursor = 0;
340003
- const stats = { totalBytes: 0, count: 0, ticks: 0, firstPts: 0, hasCts: false };
340077
+ let bytePos = 0, cursor = 0, previousPts = -Infinity;
340078
+ const stats = { totalBytes: 0, count: 0, ticks: 0, firstPts: startPts, hasReorderedSamples: false };
340004
340079
  const packet = (sample) => {
340005
340080
  if (mode === "streams") {
340006
- if (!stats.count) stats.firstPts = sample.pts;
340007
340081
  stats.count++;
340008
340082
  stats.totalBytes += sample.size;
340009
340083
  stats.ticks += sample.duration;
340010
- stats.hasCts ||= sample.pts !== sample.dts;
340084
+ stats.hasReorderedSamples ||= sample.pts < previousPts;
340085
+ previousPts = sample.pts;
340011
340086
  }
340012
340087
  const record = { sample, track, bytePos };
340013
340088
  bytePos += sample.size;
@@ -340019,27 +340094,27 @@ async function* scanProbeRecords(source, options, mode) {
340019
340094
  if (mode !== "streams") yield row2;
340020
340095
  continue;
340021
340096
  }
340022
- const start = Math.max(cursor, Math.round(sample.pts));
340023
- if (start > cursor) {
340024
- const row2 = packet({ pts: cursor, dts: cursor, duration: start - cursor, size: 2, isKeyframe: true });
340097
+ const start2 = Math.max(cursor, Math.round(sample.pts));
340098
+ if (start2 > cursor) {
340099
+ const row2 = packet({ pts: cursor, dts: cursor, duration: start2 - cursor, size: 2, isKeyframe: true });
340025
340100
  if (mode !== "streams") yield row2;
340026
- cursor = start;
340101
+ cursor = start2;
340027
340102
  }
340028
340103
  const duration2 = Math.max(1, Math.round(sample.duration));
340029
340104
  const row = packet({ pts: cursor, dts: cursor, duration: duration2, size: sample.size + 2, isKeyframe: true });
340030
340105
  if (mode !== "streams") yield row;
340031
340106
  cursor += duration2;
340032
340107
  }
340033
- if (mode === "streams") yield buildProbeStream({ ...track, duration: trackDuration }, codec, stats, index, "mp4");
340108
+ if (mode === "streams") yield buildProbeStream({ ...track, duration: probeDuration }, codec, stats, index, "mp4");
340034
340109
  }
340035
340110
  budget.checkDuration(maxDuration);
340036
- return { duration: maxDuration, tracks: trackIndex };
340111
+ return { duration: maxDuration, start: trackIndex ? formatStart : 0, presentationDuration: trackIndex ? Math.max(0, formatEnd - formatStart) : maxDuration, tracks: trackIndex };
340037
340112
  }
340038
340113
  async function probeMp4Source(source, options = {}) {
340039
340114
  const work = { ...options, budget: options.budget ?? new MediaBudgetTracker(options.limits) };
340040
340115
  const scan = scanProbeRecords(source, work, "summary"), summary = await scan.next();
340041
340116
  if (!summary.done) throw new Error("Unexpected MP4 summary row");
340042
- const { duration, tracks } = summary.value, metadata = await probeMp4MetadataSource(source, duration, work);
340117
+ const { duration, start, presentationDuration, tracks } = summary.value, metadata = await probeMp4MetadataSource(source, duration, work);
340043
340118
  async function* streams() {
340044
340119
  for await (const row of scanProbeRecords(source, work, "streams")) if (!("sample" in row)) yield row;
340045
340120
  }
@@ -340051,10 +340126,10 @@ async function probeMp4Source(source, options = {}) {
340051
340126
  nb_programs: 0,
340052
340127
  format_name: "mov,mp4,m4a,3gp,3g2,mj2",
340053
340128
  format_long_name: "QuickTime / MOV",
340054
- start_time: "0.000000",
340055
- duration: duration.toFixed(6),
340129
+ start_time: start.toFixed(6),
340130
+ duration: presentationDuration.toFixed(6),
340056
340131
  size: String(source.size),
340057
- bit_rate: duration > 0 ? String(Math.round(source.size * 8 / duration)) : "0",
340132
+ bit_rate: presentationDuration > 0 ? String(Math.round(source.size * 8 / presentationDuration)) : "0",
340058
340133
  probe_score: 100,
340059
340134
  tags: metadata.tags
340060
340135
  },
@@ -12,7 +12,7 @@ import {
12
12
  probeOggStreamMetadata,
13
13
  scanMp4Frames,
14
14
  scanMp4Packets
15
- } from "./chunk-BXOBM4HJ.js";
15
+ } from "./chunk-GQQUZGIZ.js";
16
16
 
17
17
  // packages/safe-bash-command-ffprobe/src/probe-text.ts
18
18
  import { yieldTurn } from "../../safe-bash-contracts/yield.js";
@@ -20,7 +20,7 @@ import {
20
20
  sliceMp4,
21
21
  srtAst,
22
22
  webvttAst
23
- } from "../safe-bash/command-chunks/chunk-BXOBM4HJ.js";
23
+ } from "../safe-bash/command-chunks/chunk-GQQUZGIZ.js";
24
24
  import "../safe-bash/command-chunks/chunk-65DS4IOB.js";
25
25
 
26
26
  // packages/safe-bash-command-ffmpeg/src/index.ts
@@ -786,6 +786,8 @@ async function applyVideoFilterChain(frames, filterChainStr, budget, subtitleCue
786
786
  }
787
787
  return { ...f, data: nextData };
788
788
  }, signal);
789
+ } else if (name !== "format" && name !== "null") {
790
+ throw new Error(`Unsupported video filter: ${name}`);
789
791
  }
790
792
  }
791
793
  return current;
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  createFfprobeCommand
3
- } from "../safe-bash/command-chunks/chunk-MWPL7AFP.js";
3
+ } from "../safe-bash/command-chunks/chunk-THJIIMGT.js";
4
4
  import "../safe-bash/command-chunks/chunk-MGBHGZEX.js";
5
- import "../safe-bash/command-chunks/chunk-BXOBM4HJ.js";
5
+ import "../safe-bash/command-chunks/chunk-GQQUZGIZ.js";
6
6
  import "../safe-bash/command-chunks/chunk-65DS4IOB.js";
7
7
 
8
8
  // packages/safe-bash-command-ffprobe/src/index.ts
@@ -11,9 +11,9 @@ import {
11
11
  loadManifestResources,
12
12
  resolvePath,
13
13
  rethrowRuntimeError
14
- } from "../safe-bash/command-chunks/chunk-MWPL7AFP.js";
14
+ } from "../safe-bash/command-chunks/chunk-THJIIMGT.js";
15
15
  import "../safe-bash/command-chunks/chunk-MGBHGZEX.js";
16
- import "../safe-bash/command-chunks/chunk-BXOBM4HJ.js";
16
+ import "../safe-bash/command-chunks/chunk-GQQUZGIZ.js";
17
17
  import "../safe-bash/command-chunks/chunk-65DS4IOB.js";
18
18
  export {
19
19
  createFfprobeCommand,
@@ -4321,6 +4321,8 @@ function* magickDrawSteps(img, drawCmd, state) {
4321
4321
  } else if (cmd === "path") {
4322
4322
  const d = tokens[i++] ?? "";
4323
4323
  svgElements.push(`<path d="${escapeXml(d)}" fill="${fill}" stroke="${stroke}" stroke-width="${strokeWidth}"/>`);
4324
+ } else {
4325
+ throw new Error(`Unsupported drawing primitive: ${cmd}`);
4324
4326
  }
4325
4327
  }
4326
4328
  if (svgElements.length > 0) yield { kind: "svg", svg: `<svg xmlns="http://www.w3.org/2000/svg" width="${img.width}" height="${img.height}">${svgElements.join("")}</svg>` };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poe-platform/safe-bash",
3
- "version": "0.1.830",
3
+ "version": "0.1.832",
4
4
  "description": "Extensible virtual shell with pluggable filesystems and streaming commands",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1415,7 +1415,7 @@
1415
1415
  "access": "public"
1416
1416
  },
1417
1417
  "dependencies": {
1418
- "@poe-platform/safe-fs": "0.1.830",
1418
+ "@poe-platform/safe-fs": "0.1.832",
1419
1419
  "pdf-lib": "1.17.1",
1420
1420
  "@types/node": "^25.2.2",
1421
1421
  "@formatjs/intl-numberformat": "9.4.0",