Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
fpv-wtf committed Jul 14, 2023
1 parent 266a957 commit 9352feb
Show file tree
Hide file tree
Showing 11 changed files with 83 additions and 11 deletions.
27 changes: 27 additions & 0 deletions src/osd-overlay/mp4/parsers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
UrlBox,
UrnBox,
VmhdBox,
CttsBox,
} from "./types";

export async function parseBox(stream: FileStreamReader): Promise<Box> {
Expand Down Expand Up @@ -88,6 +89,7 @@ export async function parseBox(stream: FileStreamReader): Promise<Box> {
trak: TrakBoxParser,
udta: UdtaBoxParser,
vmhd: VmhdBoxParser,
ctts: CttsBoxParser,
};

let parser: BoxParser<Box>;
Expand Down Expand Up @@ -457,6 +459,7 @@ class StblBoxParser extends SimpleBoxParser<StblBox> {
stss: childBoxes.stss![0] as StssBox,
stsz: childBoxes.stsz![0] as StszBox,
stts: childBoxes.stts![0] as SttsBox,
ctts: childBoxes.ctts ? childBoxes.ctts![0] as CttsBox : undefined
};
}
}
Expand Down Expand Up @@ -762,6 +765,30 @@ class VmhdBoxParser extends FullBoxParser<VmhdBox> {
}
}

class CttsBoxParser extends FullBoxParser<CttsBox> {
async parseBox(
header: BoxHeader,
fullBoxHeader: FullBoxHeader
): Promise<CttsBox> {
const sampleCount = await this.stream.getNextUint32();
const sampleCounts = [];
const sampleOffsets = [];

for (let i = 0; i < sampleCount; i++) {
sampleCounts.push(await this.stream.getNextUint32());
sampleOffsets.push(await this.stream.getNextUint32());
}

return {
header,
fullBoxHeader,
type: "ctts",
sampleCounts,
sampleOffsets,
};
}
}

class DrefBoxParser extends FullBoxParser<DrefBox> {
async parseBox(
header: BoxHeader,
Expand Down
8 changes: 8 additions & 0 deletions src/osd-overlay/mp4/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export type BoxType =
| "url "
| "urn "
| "vmhd"
| "ctts"
| UnknownBoxType;

export type ContainerBox =
Expand Down Expand Up @@ -68,6 +69,7 @@ export type Box =
| UrlBox
| UrnBox
| VmhdBox
| CttsBox
| UnknownBox;

export interface BoxHeader {
Expand Down Expand Up @@ -124,6 +126,7 @@ export interface StblBox extends BaseBox<"stbl"> {
stss: StssBox;
stsz: StszBox;
stts: SttsBox;
ctts?: CttsBox;
}

export interface UdtaBox extends BaseBox<"udta"> {}
Expand Down Expand Up @@ -263,6 +266,11 @@ export interface VmhdBox extends BaseFullBox<"vmhd"> {
opColor: number[];
}

export interface CttsBox extends BaseFullBox<"ctts"> {
sampleCounts: number[];
sampleOffsets: number[];
}

export interface UrlBox extends BaseFullBox<"url "> {
location: string;
}
Expand Down
43 changes: 40 additions & 3 deletions src/osd-overlay/processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ export class Processor {

this.outMp4?.setFramerate(60);

this.expectedFrames = this.inMp4!.moov!.trak[0].mdia.mdhd.duration;
this.expectedFrames = this.inMp4!.moov!.trak[0].mdia.minf.stbl.stsz.sampleCount;
this.decodedFrames = {};

this.progressInit({
Expand Down Expand Up @@ -258,16 +258,19 @@ export class Processor {
});

this.decoder!.decode(encodedChunk);
lastSampleIndex = chunk.index + 1;
this.queuedForDecode++;
}

// Wait for all samples to be decoded.
await this.decoder!.flush();

// DJI recordings straight from the goggles have all frames in sequence.
// Processed files may need reordering of the frames described in the ctts box.
const orderedFrames = this.reorderFrames(lastSampleIndex);

// Modify and enque frames for encoding.
this.encodedFrames = [];
for (const [index, entry] of Object.values(this.decodedFrames).entries()) {
for (const [index, entry] of orderedFrames.entries()) {
if (!entry.image) {
console.error(`Frame ${entry.index} was never decoded!`);
this.framesDecodedMissing++;
Expand Down Expand Up @@ -300,13 +303,47 @@ export class Processor {
for (const frame of this.encodedFrames) {
this.outMp4!.writeSample(frame.data, frame.sync);
}

lastSampleIndex += sampleChunks.length
}

await this.outMp4!.close();
this.sendProgressUpdate();
this.processResolve!();
}

private reorderFrames(lastSampleIndex: number) {
const orderedFrames = [];
const ctts = this.inMp4!.moov!.trak[0].mdia.minf.stbl.ctts;

if (!ctts) {
// No ctts box found: no reordering needed
for (let i = 0; i < Object.keys(this.decodedFrames).length; i++) {
orderedFrames.push(this.decodedFrames[lastSampleIndex + i]);
}
} else {
// Reorder frames according to ctts table
const sampleDelta = this.inMp4!.moov!.trak[0].mdia.minf.stbl.stts.entries[0].sampleDelta;
const initialOffset = ctts.sampleOffsets[0] / sampleDelta;

for (let i = 0; i < Object.keys(this.decodedFrames).length; i++) {
const frameNumber = lastSampleIndex + i;

let j = 0;
let frame = 0;
while (frameNumber >= frame) {
j++;
frame = ctts.sampleCounts.slice(0, j).reduce((acc, e) => acc + e, 0);
}

const newPosition = i + ctts.sampleOffsets[j - 1] / sampleDelta - initialOffset;
orderedFrames[newPosition] = Object.assign({}, this.decodedFrames[lastSampleIndex + i], {index: lastSampleIndex + newPosition});
}
}

return orderedFrames;
}

private async handleDecodedFrame(frame: VideoFrame) {
this.framesDecoded++;
this.decodedFrames[frame.timestamp!].image = await createImageBitmap(frame);
Expand Down
2 changes: 1 addition & 1 deletion src/translations/el/osdOverlay.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"fileDropVideo": "Video",
"noteConfigLink": "Configure this in the Package Manager.",
"noteHeader": "OSD recording is an opt-in feature on the goggle side.",
"noteWarning": "Any video files used must come directly from the goggles, with no modifications.",
"noteWarning": "The video file must either come directly from the goggles, or have only modifications that don't change the length of the video. Altering the aspect ratio, stabilization, etc are supported.",
"processing": "Processing...",
"start": "Start"
}
2 changes: 1 addition & 1 deletion src/translations/en/osdOverlay.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"fileDropVideo": "Video",
"noteConfigLink": "Configure this in the Package Manager.",
"noteHeader": "OSD recording is an opt-in feature on the goggle side.",
"noteWarning": "Any video files used must come directly from the goggles, with no modifications.",
"noteWarning": "The video file must either come directly from the goggles, or have only modifications that don't change the length of the video. Altering the aspect ratio, stabilization, etc are supported.",
"processing": "Processing...",
"start": "Start"
}
2 changes: 1 addition & 1 deletion src/translations/nl/osdOverlay.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"fileDropVideo": "Video",
"noteConfigLink": "Configure this in the Package Manager.",
"noteHeader": "OSD recording is an opt-in feature on the goggle side.",
"noteWarning": "Any video files used must come directly from the goggles, with no modifications.",
"noteWarning": "The video file must either come directly from the goggles, or have only modifications that don't change the length of the video. Altering the aspect ratio, stabilization, etc are supported.",
"processing": "Processing...",
"start": "Start"
}
2 changes: 1 addition & 1 deletion src/translations/sk/osdOverlay.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"fileDropVideo": "Video",
"noteConfigLink": "Configure this in the Package Manager.",
"noteHeader": "OSD recording is an opt-in feature on the goggle side.",
"noteWarning": "Any video files used must come directly from the goggles, with no modifications.",
"noteWarning": "The video file must either come directly from the goggles, or have only modifications that don't change the length of the video. Altering the aspect ratio, stabilization, etc are supported.",
"processing": "Processing...",
"start": "Start"
}
2 changes: 1 addition & 1 deletion src/translations/sv/osdOverlay.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"fileDropVideo": "Video",
"noteConfigLink": "Configure this in the Package Manager.",
"noteHeader": "OSD recording is an opt-in feature on the goggle side.",
"noteWarning": "Any video files used must come directly from the goggles, with no modifications.",
"noteWarning": "The video file must either come directly from the goggles, or have only modifications that don't change the length of the video. Altering the aspect ratio, stabilization, etc are supported.",
"processing": "Processing...",
"start": "Start"
}
2 changes: 1 addition & 1 deletion src/translations/uk/osdOverlay.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"fileDropVideo": "Video",
"noteConfigLink": "Configure this in the Package Manager.",
"noteHeader": "OSD recording is an opt-in feature on the goggle side.",
"noteWarning": "Any video files used must come directly from the goggles, with no modifications.",
"noteWarning": "The video file must either come directly from the goggles, or have only modifications that don't change the length of the video. Altering the aspect ratio, stabilization, etc are supported.",
"processing": "Processing...",
"start": "Start"
}
2 changes: 1 addition & 1 deletion src/translations/vi/osdOverlay.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"fileDropVideo": "Video",
"noteConfigLink": "Configure this in the Package Manager.",
"noteHeader": "OSD recording is an opt-in feature on the goggle side.",
"noteWarning": "Any video files used must come directly from the goggles, with no modifications.",
"noteWarning": "The video file must either come directly from the goggles, or have only modifications that don't change the length of the video. Altering the aspect ratio, stabilization, etc are supported.",
"processing": "Processing...",
"start": "Start"
}
2 changes: 1 addition & 1 deletion src/translations/zh-TW/osdOverlay.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"fileDropVideo": "Video",
"noteConfigLink": "Configure this in the Package Manager.",
"noteHeader": "OSD recording is an opt-in feature on the goggle side.",
"noteWarning": "Any video files used must come directly from the goggles, with no modifications.",
"noteWarning": "The video file must either come directly from the goggles, or have only modifications that don't change the length of the video. Altering the aspect ratio, stabilization, etc are supported.",
"processing": "Processing...",
"start": "Start"
}

0 comments on commit 9352feb

Please sign in to comment.