Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Binary serialization for p2p messages. #312

Merged
merged 138 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from 129 commits
Commits
Show all changes
138 commits
Select commit Hold shift + click to select a range
c78c60c
Fix issue with wrong segment endTime.
i-zolotarenko Aug 21, 2023
eb21613
Fix issue with hls wrong segment local id.
i-zolotarenko Aug 21, 2023
99c234d
Add linked-map, load queue and http-loader.
i-zolotarenko Aug 23, 2023
40881d6
Add load queue class.
i-zolotarenko Aug 27, 2023
02a07fe
Add segments storage.
i-zolotarenko Aug 28, 2023
30ed915
Add segments load logic.
i-zolotarenko Aug 30, 2023
2dc9029
Add plugin requests promises map.
i-zolotarenko Aug 31, 2023
3ea3362
Rewrite load queue.
i-zolotarenko Aug 31, 2023
2ff4aa6
Move segment queue status determination to queue class.
i-zolotarenko Aug 31, 2023
77f6804
Separate main and secondary stream to different contexts.
i-zolotarenko Sep 1, 2023
d933e7a
Remove unnecessary loader layer.
i-zolotarenko Sep 1, 2023
72d33eb
Fix issue with not loading after moving playhead to random position.
i-zolotarenko Sep 4, 2023
f6713b7
Rename hybrid loader file.
i-zolotarenko Sep 4, 2023
ff79e37
Initialize playback on first segment request by plugin.
i-zolotarenko Sep 4, 2023
fe3ea0e
Make storage async. Add core destroy logic.
i-zolotarenko Sep 4, 2023
e13d358
Use StreamWithReadonlySegments type in plugins.
i-zolotarenko Sep 5, 2023
7c4197d
Add bandwidth approximator.
i-zolotarenko Sep 5, 2023
72e426a
Refactor load queue code.
i-zolotarenko Sep 5, 2023
03a388b
Load controlling: pure functions style. (#302)
i-zolotarenko Sep 7, 2023
9ac874f
Install bittorrent-tracker and ripemd160 to core.
i-zolotarenko Sep 12, 2023
81e8521
Create type declaration for bittorrent tracker.
i-zolotarenko Sep 12, 2023
5f899c7
Create P2PLoader and Peer classes.
i-zolotarenko Sep 12, 2023
9d7d6f3
Move enums to separate file.
i-zolotarenko Sep 12, 2023
08f4c23
Remove unused position core property.
i-zolotarenko Sep 13, 2023
23e9abc
Create secondary hybrid loader only if necessary.
i-zolotarenko Sep 13, 2023
fb5b0ea
Add force parameter to process queue hybrid loader method.
i-zolotarenko Sep 13, 2023
ffab215
Merge remote-tracking branch 'origin/load-controlling' into p2p-manager
i-zolotarenko Sep 13, 2023
d0da2ae
Add sendCommand method to Peer.
i-zolotarenko Sep 13, 2023
e294cad
Add request type.
i-zolotarenko Sep 13, 2023
d67e4c8
Create and apply RequestContainer class.
i-zolotarenko Sep 14, 2023
7b61d44
Rename settings time window properties.
i-zolotarenko Sep 15, 2023
90f5d1d
Add AbortError.
i-zolotarenko Sep 15, 2023
db389b0
Merge with v1
i-zolotarenko Sep 15, 2023
3e05601
Merge with v1
i-zolotarenko Sep 15, 2023
1cb7353
Add streams map to hybrid loader.
i-zolotarenko Sep 15, 2023
a18826e
Add peer self segments map generation logic.
i-zolotarenko Sep 18, 2023
70bff75
Notify peers on segment loadings update.
i-zolotarenko Sep 18, 2023
f61b852
Fix types issues.
i-zolotarenko Sep 18, 2023
3305c19
Add peer events.
i-zolotarenko Sep 18, 2023
676ddae
Merge with requests-container.
i-zolotarenko Sep 18, 2023
a2cee74
Add downloadSegment method to p2p-loader. Use p2p request type in Pee…
i-zolotarenko Sep 18, 2023
b93677a
Use request container in p2p-loader.
i-zolotarenko Sep 19, 2023
098b10a
Add receiving segment chunks logic.
i-zolotarenko Sep 19, 2023
7cf3524
Reject promise with errors in peer class.
i-zolotarenko Sep 19, 2023
bb22472
Rewrite fetch segment with async await.
i-zolotarenko Sep 19, 2023
486058f
Use object with booleans instead of set for segment queue statuses.
i-zolotarenko Sep 19, 2023
8ef7dae
Rename fetch segment function.
i-zolotarenko Sep 19, 2023
920db7d
Rename hybrid loader public method.
i-zolotarenko Sep 19, 2023
7783df1
Use engine callbacks instead of promise creation in core.
i-zolotarenko Sep 20, 2023
34e6881
Merge pull request #306 from Novage/requests-container-p2p-integration
i-zolotarenko Sep 20, 2023
4ad6e5e
Remove unnecessary getControlledPromise function from engines.
i-zolotarenko Sep 20, 2023
aa369ab
Merge branch 'requests-container-p2p-integration' into p2p-manager
i-zolotarenko Sep 20, 2023
2bb356e
Create hybrid loaders when necessary data is already set.
i-zolotarenko Sep 20, 2023
695deca
Add progress to http loading.
i-zolotarenko Sep 20, 2023
ddf2265
Add progress functionality to Peer class.
i-zolotarenko Sep 20, 2023
7bd2267
Fix type issues.
i-zolotarenko Sep 20, 2023
6fb3fbe
Use only callbacks instead of promise creation in hls engine.
i-zolotarenko Sep 21, 2023
ffc4424
Add segment storage initialization.
i-zolotarenko Sep 21, 2023
513b361
Add peer settings options.
i-zolotarenko Sep 21, 2023
6e6e108
Move storage cleanup logic into storage class.
i-zolotarenko Sep 21, 2023
541d78b
Add p2p loaders container.
i-zolotarenko Sep 22, 2023
7893230
Add p2p loaders destroy logic.
i-zolotarenko Sep 22, 2023
83d067f
Remove unnecessary toString on string variable.
i-zolotarenko Sep 22, 2023
c6419ae
Don't use response.arrayBuffer() if getting data from stream.
i-zolotarenko Sep 25, 2023
b443b03
Change peer announcement type and corresponding logic.
i-zolotarenko Sep 25, 2023
0c8af4f
Use single PeerRequestError with different types instead of lot of Er…
i-zolotarenko Sep 25, 2023
1938c55
Change peer announcement type and corresponding logic.
i-zolotarenko Sep 25, 2023
f966ca4
Remove memory request container counters.
i-zolotarenko Sep 26, 2023
a0864cf
Remove unnecessary segmentsToDelete from memory segment storage.
i-zolotarenko Sep 26, 2023
093f87b
Fix issue with typo in "chunk"
i-zolotarenko Sep 27, 2023
7363354
Add ability to subscribe to segments store update.
i-zolotarenko Sep 27, 2023
e5e24c9
Merge with origin/p2p-manager.
i-zolotarenko Sep 28, 2023
ff29655
Add stale p2p loader destroy timeout.
i-zolotarenko Sep 28, 2023
fe58ea0
Add request container http requests subscriptions.
i-zolotarenko Sep 28, 2023
978d95d
Add vite-plugin-node-polyfills to demo.
i-zolotarenko Sep 28, 2023
9671b0c
Move utils to separate folder.
i-zolotarenko Sep 28, 2023
083d593
Fix issues with abort looping, not handling requests in container.
i-zolotarenko Sep 28, 2023
420ffcd
Force queue process if playback position was significantly changed.
i-zolotarenko Sep 28, 2023
7622bdf
Add stream property to segment type.
i-zolotarenko Sep 29, 2023
44ed56b
Fix issue with fetch result data wrong promise error handling.
i-zolotarenko Oct 1, 2023
2b59ee2
Fix issue with not clearing aborted engine request.
i-zolotarenko Oct 2, 2023
15cfdda
Add random http loading.
i-zolotarenko Oct 2, 2023
6a607a3
Change JsonSegmentAnnouncement type.
i-zolotarenko Oct 2, 2023
7b6330a
Add loading through p2p to process queue.
i-zolotarenko Oct 2, 2023
7599820
Change load method parameter to queue item.
i-zolotarenko Oct 3, 2023
3cc30fe
Add loggers to hybrid loader, p2p-manager.
i-zolotarenko Oct 5, 2023
e564a76
Add load probability to http random load.
i-zolotarenko Oct 5, 2023
67886a9
Show stat in Demo. Destroy p2p manager if there is no uploadable data.
i-zolotarenko Oct 5, 2023
7f11315
Refactor code related to stat components.
i-zolotarenko Oct 6, 2023
52d88d0
Add loggers select to demo.
i-zolotarenko Oct 6, 2023
220b440
And onPeerClose event to Peer class.
i-zolotarenko Oct 8, 2023
a480959
Fix issue with wrong array buffer slicing. Modify choosing peer conne…
i-zolotarenko Oct 10, 2023
08203df
Add peer logger. Use utf-8 string for peer ids instead of hashes.
i-zolotarenko Oct 10, 2023
321cc6b
Remove ripemd160 package.
i-zolotarenko Oct 10, 2023
9a14429
Install node types. Patch bittorrent-tracker. Remove modules ignoring…
i-zolotarenko Oct 11, 2023
548320d
Sent ArrayBuffer to peer instead of Buffer. Use custom function to ch…
i-zolotarenko Oct 11, 2023
0bd2822
Add remain time predicting logic.
i-zolotarenko Oct 11, 2023
4c01697
Create universal bandwidth-approximator.
i-zolotarenko Oct 15, 2023
1315281
Add bitrate adaptation logic.
i-zolotarenko Oct 16, 2023
8bc001f
Rename p2p-loader logger.
i-zolotarenko Oct 17, 2023
11cbfa3
Use simple number variable instead of object creation.
i-zolotarenko Oct 17, 2023
62e312a
Modify uploading cancellation logic.
i-zolotarenko Oct 18, 2023
ebb843b
Broadcast announcement not more than 1 time for macrotask.
i-zolotarenko Oct 18, 2023
a033a7f
Generate user-friendly stream hash.
i-zolotarenko Oct 24, 2023
1f3b760
Configure tsconfig module, target options.
i-zolotarenko Oct 25, 2023
f3bf527
Change event handler name to onSegmentLoaded.
i-zolotarenko Oct 25, 2023
1297e87
Add core event handlers to shaka.
i-zolotarenko Oct 30, 2023
2e78864
Fix issues with shaka streams playback.
i-zolotarenko Oct 30, 2023
0932063
Revise and refactor code.
i-zolotarenko Oct 31, 2023
4162ab8
Move p2p files to separate p2p directory.
i-zolotarenko Oct 31, 2023
19ec6af
Rename request container file and p2p loader and container files.
i-zolotarenko Oct 31, 2023
bcec5be
Add request-container, storage loggers. Use queueMicrotask instead of…
i-zolotarenko Oct 31, 2023
589eda6
Fix issue with skipping first segment in queue generation function.
i-zolotarenko Oct 31, 2023
0389b57
Add binary serialization functions.
i-zolotarenko Nov 1, 2023
a2f44b8
Remove bits file.
i-zolotarenko Nov 2, 2023
8af127c
Merge branch 'v1' into p2p-manager
mrlika Nov 2, 2023
e96befe
Merge remote-tracking branch 'origin/v1' into p2p-manager
i-zolotarenko Nov 2, 2023
fb56f65
Add number and array binary serialization functions.
i-zolotarenko Nov 2, 2023
e07c1d8
Add array compression function.
i-zolotarenko Nov 16, 2023
be77b2e
Rewrite using bigint.
i-zolotarenko Nov 20, 2023
f4f725a
Fix issue with wrong bitwise operation.
i-zolotarenko Nov 22, 2023
481e6b0
Change similar int serialization algorithm.
i-zolotarenko Nov 23, 2023
bc32531
Merge with origin branch.
i-zolotarenko Nov 23, 2023
d1dec32
Complete similar int array serialization algorithm.
i-zolotarenko Nov 24, 2023
ab63b07
Refactor code and apply serialization.
i-zolotarenko Nov 24, 2023
b42d4c2
Fix errors.
i-zolotarenko Nov 24, 2023
969ed0e
Change removing/creating new p2p loader order.
i-zolotarenko Nov 24, 2023
d8a0612
Use md5 algorithm. Generate 15 bytes hash (1 byte truncate).
i-zolotarenko Dec 13, 2023
92362b2
Use one decimal in segment id. Other truncate.
i-zolotarenko Dec 13, 2023
071c8d2
Merge with v1
i-zolotarenko Dec 15, 2023
c45736b
Structure binary commands code. Fix bugs.
i-zolotarenko Dec 15, 2023
ad4a13a
Add PeerRequestSegmentCommand.
i-zolotarenko Dec 15, 2023
963c9f4
Don't add empty arrays to binary command representation.
i-zolotarenko Dec 15, 2023
b6c1632
Remove inner types file.
i-zolotarenko Dec 15, 2023
6e0c909
Split too large command buffer into chunks.
i-zolotarenko Dec 17, 2023
d1cfd74
Send split command.
i-zolotarenko Dec 18, 2023
63a22c5
Receive splited to chunks command.
i-zolotarenko Dec 18, 2023
038d7b9
Add string serialization.
i-zolotarenko Dec 18, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions packages/p2p-media-loader-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@
},
"dependencies": {
"bittorrent-tracker": "10.0.12",
"ripemd160": "^2.0.2"
"nano-md5": "^1.0.5"
},
"devDependencies": {
"@types/ripemd160": "^2.0.2"
}
}
183 changes: 183 additions & 0 deletions packages/p2p-media-loader-core/src/binary-serialization.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// restricted to max 16 item types (4 bits to type definition)
export enum SerializedItem {
Int,
SimilarIntArray,
}
export const serializedItemTypes = Object.values(SerializedItem);

function abs(num: bigint): bigint {
return num < 0 ? -num : num;
}

function getRequiredBytesForInt(num: bigint): number {
const binaryString = num.toString(2);
const necessaryBits = num < 0 ? binaryString.length : binaryString.length + 1;
return Math.ceil(necessaryBits / 8);
}

export function intToBytes(num: bigint): Uint8Array {
const isNegative = num < 0;
const bytesAmountNumber = getRequiredBytesForInt(num);
const bytes = new Uint8Array(bytesAmountNumber);
const bytesAmount = BigInt(bytesAmountNumber);

num = abs(num);
for (let i = 0; i < bytesAmountNumber; i++) {
const shift = 8n * (bytesAmount - 1n - BigInt(i));
const byte = (num >> shift) & 0xffn;
bytes[i] = Number(byte);
}

if (isNegative) bytes[0] = bytes[0] | 0b10000000;
return bytes;
}

export function bytesToInt(bytes: Uint8Array): bigint {
const byteLength = BigInt(bytes.length);
const getNumberPart = (byte: number, i: number): bigint => {
const shift = 8n * (byteLength - 1n - BigInt(i));
return BigInt(byte) << shift;
};

// ignore first bit of first byte as it is sign bit
let number = getNumberPart(bytes[0] & 0b01111111, 0);
for (let i = 1; i < byteLength; i++) {
number = getNumberPart(bytes[i], i) | number;
}
if ((bytes[0] & 0b10000000) >> 7 !== 0) number = -number;

return number;
}

export function serializeInt(num: bigint): Uint8Array {
const numBytes = intToBytes(num);
const numberMetadata = (SerializedItem.Int << 4) | numBytes.length;
return new Uint8Array([numberMetadata, ...numBytes]);
}

export function deserializeInt(bytes: Uint8Array) {
const metadata = bytes[0];
const code = metadata >> 4;
if (code !== SerializedItem.Int) {
throw new Error(
"Trying to deserialize integer with invalid serialized item code"
);
}
const numberBytesLength = metadata & 0b1111;
const start = 1;
const end = start + numberBytesLength;
return {
number: bytesToInt(bytes.slice(start, end)),
byteLength: numberBytesLength + 1,
};
}

export function serializeSimilarIntArray(numbers: bigint[]) {
const commonPartNumbersMap = new Map<bigint, ResizableUint8Array>();

for (const number of numbers) {
const common = number & ~0xffn;
const diffByte = number & 0xffn;
const bytes = commonPartNumbersMap.get(common) ?? new ResizableUint8Array();
if (!bytes.length) commonPartNumbersMap.set(common, bytes);
bytes.push(Number(diffByte));
}

const arrayMetadata = [
SerializedItem.SimilarIntArray << 4,
commonPartNumbersMap.size,
];
const result = new ResizableUint8Array();
result.unshift(arrayMetadata);

for (const [commonPart, binaryArray] of commonPartNumbersMap) {
const { length } = binaryArray.getBytesChunks();
const commonPartWithLength = commonPart | (BigInt(length) & 0xffn);
binaryArray.unshift(serializeInt(commonPartWithLength));
result.push(binaryArray.getBytes());
}

return result.getBytes();
}

export function deserializeSimilarIntArray(bytes: Uint8Array) {
const [codeByte, commonPartArraysAmount] = bytes;
const code = codeByte >> 4;
if (code !== SerializedItem.SimilarIntArray) {
throw new Error(
"Trying to deserialize similar int array with invalid serialized item code"
);
}

let offset = 2;
const originalIntArr: bigint[] = [];
for (let i = 0; i < commonPartArraysAmount; i++) {
const { number: commonPartWithLength, byteLength } = deserializeInt(
bytes.slice(offset)
);
offset += byteLength;
const arrayLength = commonPartWithLength & 0xffn;
const commonPart = commonPartWithLength & ~0xffn;

for (let j = 0; j < arrayLength; j++) {
const diffPart = BigInt(bytes[offset]);
originalIntArr.push(commonPart | diffPart);
offset++;
}
}

return { numbers: originalIntArr, byteLength: offset };
}

function joinUint8Arrays(arrays: Uint8Array[], length?: number) {
const byteLength = length ?? arrays.reduce((sum, arr) => sum + arr.length, 0);
const bytes = new Uint8Array(byteLength);
let offset = 0;
for (const array of arrays) {
bytes.set(array, offset);
offset += array.length;
}

return bytes;
}

export class ResizableUint8Array {
private bytes: Uint8Array[] = [];
private _length = 0;

push(bytes: Uint8Array | number | number[]) {
this.addBytes(bytes, "end");
}

unshift(bytes: Uint8Array | number | number[]) {
this.addBytes(bytes, "start");
}

private addBytes(
bytes: Uint8Array | number | number[],
position: "start" | "end"
) {
let bytesToAdd: Uint8Array;
if (bytes instanceof Uint8Array) {
bytesToAdd = bytes;
} else if (Array.isArray(bytes)) {
bytesToAdd = new Uint8Array(bytes);
} else {
bytesToAdd = new Uint8Array([bytes]);
}
this._length += bytesToAdd.length;
this.bytes[position === "start" ? "unshift" : "push"](bytesToAdd);
}

getBytesChunks(): ReadonlyArray<Uint8Array> {
return this.bytes;
}

getBytes(): Uint8Array {
return joinUint8Arrays(this.bytes, this._length);
}

get length() {
return this._length;
}
}
16 changes: 13 additions & 3 deletions packages/p2p-media-loader-core/src/declarations.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
declare module "bittorrent-tracker" {
export default class Client {
constructor(options: {
infoHash: string;
peerId: string;
infoHash: Uint8Array;
peerId: Uint8Array;
announce: string[];
port: number;
rtcConfig?: RTCConfiguration;
Expand Down Expand Up @@ -38,7 +38,7 @@ declare module "bittorrent-tracker" {
E extends "connect"
? () => void
: E extends "data"
? (data: ArrayBuffer) => void
? (data: Uint8Array) => void
: E extends "close"
? () => void
: E extends "error"
Expand All @@ -58,3 +58,13 @@ declare module "bittorrent-tracker" {
destroy(): void;
};
}

declare module "nano-md5" {
type BinaryStringObject = string & { toHex: () => string };
const md5: {
(utf8String: string): string; // returns hex string interpretation of binary data
fromUtf8(utf8String: string): BinaryStringObject;
};

export default md5;
}
12 changes: 0 additions & 12 deletions packages/p2p-media-loader-core/src/enums.ts

This file was deleted.

35 changes: 0 additions & 35 deletions packages/p2p-media-loader-core/src/internal-types.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Segment } from "./types";
import { PeerCommandType } from "./enums";

export type Playback = {
position: number;
Expand All @@ -24,37 +23,3 @@ export type QueueItemStatuses = {
};

export type QueueItem = { segment: Segment; statuses: QueueItemStatuses };

export type BasePeerCommand<T extends PeerCommandType = PeerCommandType> = {
c: T;
};

// {l: loadedSegmentsExternalIds; p: loadingInProcessSegmentExternalIds}
export type JsonSegmentAnnouncement = {
l: string;
p: string;
};

export type PeerSegmentCommand = BasePeerCommand<
| PeerCommandType.SegmentRequest
| PeerCommandType.SegmentAbsent
| PeerCommandType.CancelSegmentRequest
> & {
i: string;
};

export type PeerSegmentAnnouncementCommand =
BasePeerCommand<PeerCommandType.SegmentsAnnouncement> & {
a: JsonSegmentAnnouncement;
};

export type PeerSendSegmentCommand =
BasePeerCommand<PeerCommandType.SegmentData> & {
i: string;
s: number;
};

export type PeerCommand =
| PeerSegmentCommand
| PeerSegmentAnnouncementCommand
| PeerSendSegmentCommand;
Loading