Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 4 additions & 3 deletions src/lines/fmtp-line.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import { NUM, REST } from '../regex-helpers';
import { Line } from './line';
import { PayloadTypeRef } from './payload-type-ref';

/**
* Parse the fmtpParams because SDP has no such util function for that.
Expand Down Expand Up @@ -60,7 +61,7 @@ export function parseFmtpParams(fmtpParams: string) {
* a=fmtp:97 apt=96
*/
export class FmtpLine extends Line {
payloadType: number;
payloadType: PayloadTypeRef;

params: Map<string, string | undefined>;

Expand All @@ -72,7 +73,7 @@ export class FmtpLine extends Line {
* @param payloadType - The payload type.
* @param params - The fmtp parameters.
*/
constructor(payloadType: number, params: Map<string, string | undefined>) {
constructor(payloadType: PayloadTypeRef, params: Map<string, string | undefined>) {
super();
this.payloadType = payloadType;
this.params = params;
Expand All @@ -89,7 +90,7 @@ export class FmtpLine extends Line {
return undefined;
}
const tokens = line.match(FmtpLine.regex) as RegExpMatchArray;
const payloadType = parseInt(tokens[1], 10);
const payloadType = new PayloadTypeRef(parseInt(tokens[1], 10));
const params = tokens[2];

return new FmtpLine(payloadType, parseFmtpParams(params));
Expand Down
1 change: 1 addition & 0 deletions src/lines/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export * from './max-message-size-line';
export * from './media-line';
export * from './mid-line';
export * from './origin-line';
export * from './payload-type-ref';
export * from './rid-line';
export * from './rtcp-mux-line';
export * from './rtcpfb-line';
Expand Down
63 changes: 63 additions & 0 deletions src/lines/payload-type-ref.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { PayloadTypeRef } from './payload-type-ref';

describe('payloadTypeRef', () => {
describe('numeric', () => {
it('should store a numeric value', () => {
expect.hasAssertions();
const pt = new PayloadTypeRef(96);
expect(pt.value).toBe(96);
});

it('should not be a wildcard', () => {
expect.hasAssertions();
const pt = new PayloadTypeRef(96);
expect(pt.isWildcard()).toBe(false);
});

it('should match the same numeric value', () => {
expect.hasAssertions();
const pt = new PayloadTypeRef(96);
expect(pt.matches(96)).toBe(true);
});

it('should not match a different numeric value', () => {
expect.hasAssertions();
const pt = new PayloadTypeRef(96);
expect(pt.matches(97)).toBe(false);
});

it('should serialize to string as the number', () => {
expect.hasAssertions();
const pt = new PayloadTypeRef(96);
expect(pt.toString()).toBe('96');
});
});

describe('wildcard', () => {
it('should store the wildcard value', () => {
expect.hasAssertions();
const pt = new PayloadTypeRef('*');
expect(pt.value).toBe('*');
});

it('should be a wildcard', () => {
expect.hasAssertions();
const pt = new PayloadTypeRef('*');
expect(pt.isWildcard()).toBe(true);
});

it('should match any numeric value', () => {
expect.hasAssertions();
const pt = new PayloadTypeRef('*');
expect(pt.matches(96)).toBe(true);
expect(pt.matches(0)).toBe(true);
expect(pt.matches(127)).toBe(true);
});

it('should serialize to string as *', () => {
expect.hasAssertions();
const pt = new PayloadTypeRef('*');
expect(pt.toString()).toBe('*');
});
});
});
45 changes: 45 additions & 0 deletions src/lines/payload-type-ref.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Represents a payload type reference as it appears in SDP attribute lines.
* Can be either a numeric payload type or a wildcard (*) meaning "all payload types".
*/
export class PayloadTypeRef {
readonly value: number | '*';

/**
* Create a PayloadTypeRef.
*
* @param value - A numeric payload type or '*' for wildcard.
*/
constructor(value: number | '*') {
this.value = value;
}

/**
* Check if this reference matches the given numeric payload type.
* A wildcard matches any payload type.
*
* @param pt - The numeric payload type to match against.
* @returns True if this reference matches the given payload type.
*/
matches(pt: number): boolean {
return this.value === '*' || this.value === pt;
}

/**
* Check if this is a wildcard reference.
*
* @returns True if this is a wildcard reference.
*/
isWildcard(): boolean {
return this.value === '*';
}

/**
* Serialize to string.
*
* @returns The string representation.
*/
toString(): string {
return `${this.value}`;
}
}
51 changes: 51 additions & 0 deletions src/lines/rtcpfb-line.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { RtcpFbLine } from './rtcpfb-line';

describe('rtcpFbLine', () => {
describe('fromSdpLine', () => {
it('should parse a numeric payload type', () => {
expect.hasAssertions();
const line = RtcpFbLine.fromSdpLine('rtcp-fb:96 goog-remb');
expect(line).toBeDefined();
expect(line?.payloadType.value).toBe(96);
expect(line?.payloadType.isWildcard()).toBe(false);
expect(line?.feedback).toBe('goog-remb');
});

it('should parse a wildcard payload type', () => {
expect.hasAssertions();
const line = RtcpFbLine.fromSdpLine('rtcp-fb:* nack');
expect(line).toBeDefined();
expect(line?.payloadType.value).toBe('*');
expect(line?.payloadType.isWildcard()).toBe(true);
expect(line?.feedback).toBe('nack');
});

it('should parse a wildcard payload type with compound feedback', () => {
expect.hasAssertions();
const line = RtcpFbLine.fromSdpLine('rtcp-fb:* nack pli');
expect(line).toBeDefined();
expect(line?.payloadType.value).toBe('*');
expect(line?.feedback).toBe('nack pli');
});

it('should return undefined for invalid line', () => {
expect.hasAssertions();
const line = RtcpFbLine.fromSdpLine('rtcp-fb:abc nack');
expect(line).toBeUndefined();
});
});

describe('toSdpLine', () => {
it('should serialize a numeric payload type', () => {
expect.hasAssertions();
const line = RtcpFbLine.fromSdpLine('rtcp-fb:96 goog-remb');
expect(line?.toSdpLine()).toBe('a=rtcp-fb:96 goog-remb');
});

it('should serialize a wildcard payload type', () => {
expect.hasAssertions();
const line = RtcpFbLine.fromSdpLine('rtcp-fb:* nack pli');
expect(line?.toSdpLine()).toBe('a=rtcp-fb:* nack pli');
});
});
});
11 changes: 7 additions & 4 deletions src/lines/rtcpfb-line.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import { NUM, REST } from '../regex-helpers';
import { Line } from './line';
import { PayloadTypeRef } from './payload-type-ref';

/**
* Implementation of an rtcp-fb attribute as defined by https://datatracker.ietf.org/doc/html/rfc4585#section-4.2.
Expand All @@ -24,19 +25,19 @@ import { Line } from './line';
* a=rtcp-fb:96 goog-remb
*/
export class RtcpFbLine extends Line {
payloadType: number;
payloadType: PayloadTypeRef;

feedback: string;

private static regex = new RegExp(`^rtcp-fb:(${NUM}) (${REST})`);
private static regex = new RegExp(`^rtcp-fb:(${NUM}|\\*) (${REST})`);

/**
* Create an RtcpFbLine from the given values.
*
* @param payloadType - The payload type.
* @param feedback - The feedback name.
*/
constructor(payloadType: number, feedback: string) {
constructor(payloadType: PayloadTypeRef, feedback: string) {
super();
this.payloadType = payloadType;
this.feedback = feedback;
Expand All @@ -53,7 +54,9 @@ export class RtcpFbLine extends Line {
return undefined;
}
const tokens = line.match(RtcpFbLine.regex) as RegExpMatchArray;
const payloadType = parseInt(tokens[1], 10);
const ptToken = tokens[1];
const payloadType =
ptToken === '*' ? new PayloadTypeRef('*') : new PayloadTypeRef(parseInt(ptToken, 10));
const feedback = tokens[2];

return new RtcpFbLine(payloadType, feedback);
Expand Down
7 changes: 4 additions & 3 deletions src/lines/rtpmap-line.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import { NUM } from '../regex-helpers';
import { Line } from './line';
import { PayloadTypeRef } from './payload-type-ref';

/**
* Definition of an rtpmap attribute line as defined in https://datatracker.ietf.org/doc/html/rfc4566#section-6.
Expand All @@ -24,7 +25,7 @@ import { Line } from './line';
* a=rtpmap:96 VP8/90000
*/
export class RtpMapLine extends Line {
payloadType: number;
payloadType: PayloadTypeRef;

encodingName: string;

Expand Down Expand Up @@ -52,7 +53,7 @@ export class RtpMapLine extends Line {
* @param encodingParams - Optional additional encoding parameters.
*/
constructor(
payloadType: number,
payloadType: PayloadTypeRef,
encodingName: string,
clockRate: number,
encodingParams?: string
Expand All @@ -75,7 +76,7 @@ export class RtpMapLine extends Line {
return undefined;
}
const tokens = line.match(RtpMapLine.regex) as RegExpMatchArray;
const payloadType = parseInt(tokens[1], 10);
const payloadType = new PayloadTypeRef(parseInt(tokens[1], 10));
const encodingName = tokens[2];
const clockRate = parseInt(tokens[3], 10);
// encodingParams, if present, will be in index 4
Expand Down
11 changes: 4 additions & 7 deletions src/model/av-media-description.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
SsrcLine,
} from '../lines';
import { CodecInfo } from './codec-info';
import { CodecStore } from './codec-store';
import { MediaDescription } from './media-description';

/**
Expand All @@ -49,7 +50,7 @@ export class AvMediaDescription extends MediaDescription {

simulcast?: SimulcastLine;

codecs: Map<number, CodecInfo> = new Map();
codecs: CodecStore = new CodecStore();

direction?: MediaDirection;

Expand Down Expand Up @@ -115,7 +116,7 @@ export class AvMediaDescription extends MediaDescription {
if (this.direction) {
lines.push(new DirectionLine(this.direction as MediaDirection));
}
this.codecs.forEach((codec) => lines.push(...codec.toLines()));
lines.push(...this.codecs.toLines());

lines.push(...this.ssrcs);
lines.push(...this.ssrcGroups);
Expand Down Expand Up @@ -162,11 +163,7 @@ export class AvMediaDescription extends MediaDescription {
}
// Lines pertaining to a specific codec
if (line instanceof RtpMapLine || line instanceof FmtpLine || line instanceof RtcpFbLine) {
const codec = this.codecs.get(line.payloadType);
if (!codec) {
throw new Error(`Error: got line for unknown codec: ${line.toSdpLine()}`);
}
codec.addLine(line);
this.codecs.addLine(line);
return true;
}
if (line instanceof SsrcLine) {
Expand Down
13 changes: 9 additions & 4 deletions src/model/codec-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import { FmtpLine, Line, RtcpFbLine, RtpMapLine } from '../lines';
import { FmtpLine, Line, PayloadTypeRef, RtcpFbLine, RtpMapLine } from '../lines';
import { SdpBlock } from './sdp-block';

/**
Expand Down Expand Up @@ -82,16 +82,21 @@ export class CodecInfo implements SdpBlock {
// First the RtpMap
if (this.name && this.clockRate) {
lines.push(
new RtpMapLine(this.pt, this.name as string, this.clockRate as number, this.encodingParams)
new RtpMapLine(
new PayloadTypeRef(this.pt),
this.name as string,
this.clockRate as number,
this.encodingParams
)
);
}
// Now all RtcpFb
this.feedback.forEach((fb) => {
lines.push(new RtcpFbLine(this.pt, fb));
lines.push(new RtcpFbLine(new PayloadTypeRef(this.pt), fb));
});
// Now all Fmtp
if (this.fmtParams.size > 0) {
lines.push(new FmtpLine(this.pt, this.fmtParams));
lines.push(new FmtpLine(new PayloadTypeRef(this.pt), this.fmtParams));
}
return lines;
}
Expand Down
Loading
Loading