Skip to content

Commit

Permalink
RTP extensions
Browse files Browse the repository at this point in the history
Fixes #22

### Details

- Implement API to get/set specific RTP extensions.
- Bonus Track: Move `RtpPacket` ot `RTP` folder.
  • Loading branch information
ibc committed Aug 22, 2023
1 parent 667d324 commit eb5324b
Show file tree
Hide file tree
Showing 5 changed files with 346 additions and 26 deletions.
204 changes: 183 additions & 21 deletions src/packets/RtpPacket.ts → src/packets/RTP/RtpPacket.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { RTP_VERSION, Packet, PacketDump } from './Packet';
import { clone, padTo4Bytes } from '../utils/helpers';
import { RTP_VERSION, Packet, PacketDump } from '../Packet';
import { RtpExtensionType, RtpExtensionMapping } from './rtpExtensions';
import {
clone,
padTo4Bytes,
dataViewToString,
stringToDataView
} from '../../utils/helpers';
import {
readBitInDataView,
writeBitInDataView,
readBitsInDataView,
writeBitsInDataView
} from '../utils/bitOps';
} from '../../utils/bitOps';

const FIXED_HEADER_LENGTH = 12;

Expand All @@ -24,6 +30,9 @@ export type RtpPacketDump = PacketDump &
marker: boolean;
headerExtensionId?: number;
extensions: { id: number; length: number }[];
midExtension?: string;
ridExtension?: string;
repairedRidExtension?: string;
payloadLength: number;
};

Expand Down Expand Up @@ -85,6 +94,8 @@ export class RtpPacket extends Packet
#headerExtensionView?: DataView;
// One-Byte or Two-Bytes extensions indexed by id.
readonly #extensions: Map<number, DataView> = new Map();
// Mapping of RTP extension types and their corresponding RTP extension ids.
#extensionMapping: RtpExtensionMapping = {};
// Buffer view holding the entire RTP payload.
#payloadView: DataView;

Expand Down Expand Up @@ -329,15 +340,18 @@ export class RtpPacket extends Packet

return {
...super.dump(),
payloadType : this.getPayloadType(),
sequenceNumber : this.getSequenceNumber(),
timestamp : this.getTimestamp(),
ssrc : this.getSsrc(),
csrcs : this.getCsrcs(),
marker : this.getMarker(),
headerExtensionId : this.#headerExtensionId,
extensions : extensions,
payloadLength : this.#payloadView.byteLength
payloadType : this.getPayloadType(),
sequenceNumber : this.getSequenceNumber(),
timestamp : this.getTimestamp(),
ssrc : this.getSsrc(),
csrcs : this.getCsrcs(),
marker : this.getMarker(),
headerExtensionId : this.#headerExtensionId,
extensions : extensions,
midExtension : this.getMidExtension(),
ridExtension : this.getRidExtension(),
repairedRidExtension : this.getRepairedRidExtension(),
payloadLength : this.#payloadView.byteLength
};
}

Expand Down Expand Up @@ -672,7 +686,11 @@ export class RtpPacket extends Packet
serializationByteOffset
);

return new RtpPacket(view);
const clonedPacket = new RtpPacket(view);

clonedPacket.setExtensionMapping(this.getExtensionMapping());

return clonedPacket;
}

/**
Expand Down Expand Up @@ -839,23 +857,23 @@ export class RtpPacket extends Packet
}

/**
* Get the value of the extension with given `id` (RFC 5285).
* Get a map with all the extensions indexed by their extension id (RFC 5285).
*/
getExtension(id: number): DataView | undefined
getExtensions(): Map<number, DataView>
{
return this.#extensions.get(id);
return new Map(this.#extensions);
}

/**
* Get a map with all the extensions indexed by their extension id (RFC 5285).
* Get the value of the extension with given id (RFC 5285).
*/
getExtensions(): Map<number, DataView>
getExtension(id: number): DataView | undefined
{
return new Map(this.#extensions);
return this.#extensions.get(id);
}

/**
* Set the value of the extension with given `id` (RFC 5285).
* Set the value of the extension with given id (RFC 5285).
*
* @remarks
* - Serialization is needed after calling this method.
Expand All @@ -880,7 +898,7 @@ export class RtpPacket extends Packet
}

/**
* Delete the extension with given `id` (RFC 5285).
* Delete the extension with given id (RFC 5285).
*
* @remarks
* - Serialization maybe needed after calling this method.
Expand Down Expand Up @@ -922,6 +940,150 @@ export class RtpPacket extends Packet
this.setSerializationNeeded(true);
}

/**
* Get RTP extension mapping (association of RTP extension types and their
* numeric ids in this RTP packet).
*/
getExtensionMapping(): RtpExtensionMapping
{
return clone(this.#extensionMapping);
}

/**
* Set RTP extension mapping (association of RTP extension types and their
* numeric ids in this RTP packet).
*
* @remarks
* - Calling this method is needed before using other methods that read or
* write specific RTP extensions.
*/
setExtensionMapping(extensionMapping: RtpExtensionMapping): void
{
this.#extensionMapping = clone(extensionMapping);
}

/**
* Read the value of the {@link RtpExtensionType.MID} RTP extension.
*/
getMidExtension(): string | undefined
{
const value = this.getExtension(
this.#extensionMapping[RtpExtensionType.MID]!
);

if (!value)
{
return;
}

return dataViewToString(value);
}

/**
* Set the value of the {@link RtpExtensionType.MID} RTP extension.
*/
setMidExtension(mid: string | undefined): void
{
const extId = this.#extensionMapping[RtpExtensionType.MID];

if (!extId)
{
return;
}

if (mid)
{
this.setExtension(extId, stringToDataView(mid));
}
else
{
this.deleteExtension(extId);
}
}

/**
* Read the value of the {@link RtpExtensionType.RTP_STREAM_ID} RTP
* extension.
*/
getRidExtension(): string | undefined
{
const value = this.getExtension(
this.#extensionMapping[RtpExtensionType.RTP_STREAM_ID]!
);

if (!value)
{
return;
}

return dataViewToString(value);
}

/**
* Set the value of the {@link RtpExtensionType.RTP_STREAM_ID} RTP
* extension.
*/
setRidExtension(mid: string | undefined): void
{
const extId = this.#extensionMapping[RtpExtensionType.RTP_STREAM_ID];

if (!extId)
{
return;
}

if (mid)
{
this.setExtension(extId, stringToDataView(mid));
}
else
{
this.deleteExtension(extId);
}
}

/**
* Read the value of the {@link RtpExtensionType.RTP_REPAIRED_STREAM_ID} RTP
* extension.
*/
getRepairedRidExtension(): string | undefined
{
const value = this.getExtension(
this.#extensionMapping[RtpExtensionType.RTP_REPAIRED_STREAM_ID]!
);

if (!value)
{
return;
}

return dataViewToString(value);
}

/**
* Set the value of the {@link RtpExtensionType.RTP_REPAIRED_STREAM_ID} RTP
* extension.
*/
setRepairedRidExtension(mid: string | undefined): void
{
const extId =
this.#extensionMapping[RtpExtensionType.RTP_REPAIRED_STREAM_ID];

if (!extId)
{
return;
}

if (mid)
{
this.setExtension(extId, stringToDataView(mid));
}
else
{
this.deleteExtension(extId);
}
}

/**
* Get the packet payload.
*/
Expand Down
85 changes: 85 additions & 0 deletions src/packets/RTP/rtpExtensions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* RTP extensions.
*
* @category RTP
*/
// ESLint absurdly complains about "'RtpExtensionType' is already declared in the
// upper scope".
// eslint-disable-next-line no-shadow
export enum RtpExtensionType
{
/**
* Media identification.
*
* URI: `urn:ietf:params:rtp-hdrext:sdes:mid`
*
* @category RTP
*
* @see
* - [RFC 9143](https://datatracker.ietf.org/doc/html/rfc9143)
*/
MID,
/**
* RTP Stream Identifier.
*
* URI: `urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id`
*
* @category RTP
*
* @see
* - [RFC 8852](https://datatracker.ietf.org/doc/html/rfc8852)
*/
RTP_STREAM_ID,
/**
* RTP Repaired Stream Identifier.
*
* URI: `urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id`
*
* @category RTP
*
* @see
* - [RFC 8852](https://datatracker.ietf.org/doc/html/rfc8852)
*/
RTP_REPAIRED_STREAM_ID
}

/**
* Mapping of RTP extension types and their corresponding RTP extension ids.
*
* @category RTP
*
* @example
* ```ts
* const rtpExtensionMapping: RtpExtensionMapping =
* {
* [RtpExtensionType.MID]: 1,
* [RtpExtensionType.RTP_STREAM_ID]: 3
* };
*/
export type RtpExtensionMapping = Partial<Record<RtpExtensionType, number>>;

/**
* Get the RTP extension type associated to the given RTP extension URI.
*
* @category RTP
*/
export function rtpExtensionUriToType(uri: string): RtpExtensionType | undefined
{
switch (uri)
{
case 'urn:ietf:params:rtp-hdrext:sdes:mid':
{
return RtpExtensionType.MID;
}

case 'urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id':
{
return RtpExtensionType.RTP_STREAM_ID;
}

case 'urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id':
{
return RtpExtensionType.RTP_REPAIRED_STREAM_ID;
}
}
}
8 changes: 7 additions & 1 deletion src/packets/public.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ export {
isRtp,
RtpPacket,
RtpPacketDump
} from './RtpPacket';
} from './RTP/RtpPacket';

export {
RtpExtensionType,
RtpExtensionMapping,
rtpExtensionUriToType
} from './RTP/rtpExtensions';

export {
isRtcp,
Expand Down
Loading

0 comments on commit eb5324b

Please sign in to comment.