@tomgiee/tsdp 1.0.0
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.
- package/LICENSE +21 -0
- package/README.md +124 -0
- package/dist/src/builder/media-builder.d.ts +221 -0
- package/dist/src/builder/media-builder.d.ts.map +1 -0
- package/dist/src/builder/media-builder.js +385 -0
- package/dist/src/builder/session-builder.d.ts +195 -0
- package/dist/src/builder/session-builder.d.ts.map +1 -0
- package/dist/src/builder/session-builder.js +366 -0
- package/dist/src/index.d.ts +67 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +250 -0
- package/dist/src/parser/attribute-parser.d.ts +100 -0
- package/dist/src/parser/attribute-parser.d.ts.map +1 -0
- package/dist/src/parser/attribute-parser.js +217 -0
- package/dist/src/parser/field-parser.d.ts +124 -0
- package/dist/src/parser/field-parser.d.ts.map +1 -0
- package/dist/src/parser/field-parser.js +335 -0
- package/dist/src/parser/media-parser.d.ts +45 -0
- package/dist/src/parser/media-parser.d.ts.map +1 -0
- package/dist/src/parser/media-parser.js +157 -0
- package/dist/src/parser/primitive-parser.d.ts +138 -0
- package/dist/src/parser/primitive-parser.d.ts.map +1 -0
- package/dist/src/parser/primitive-parser.js +316 -0
- package/dist/src/parser/scanner.d.ts +142 -0
- package/dist/src/parser/scanner.d.ts.map +1 -0
- package/dist/src/parser/scanner.js +284 -0
- package/dist/src/parser/session-parser.d.ts +35 -0
- package/dist/src/parser/session-parser.d.ts.map +1 -0
- package/dist/src/parser/session-parser.js +207 -0
- package/dist/src/parser/time-parser.d.ts +74 -0
- package/dist/src/parser/time-parser.d.ts.map +1 -0
- package/dist/src/parser/time-parser.js +168 -0
- package/dist/src/serializer/attribute-serializer.d.ts +18 -0
- package/dist/src/serializer/attribute-serializer.d.ts.map +1 -0
- package/dist/src/serializer/attribute-serializer.js +34 -0
- package/dist/src/serializer/field-serializer.d.ts +112 -0
- package/dist/src/serializer/field-serializer.d.ts.map +1 -0
- package/dist/src/serializer/field-serializer.js +212 -0
- package/dist/src/serializer/media-serializer.d.ts +31 -0
- package/dist/src/serializer/media-serializer.d.ts.map +1 -0
- package/dist/src/serializer/media-serializer.js +83 -0
- package/dist/src/serializer/session-serializer.d.ts +29 -0
- package/dist/src/serializer/session-serializer.d.ts.map +1 -0
- package/dist/src/serializer/session-serializer.js +99 -0
- package/dist/src/serializer/time-serializer.d.ts +46 -0
- package/dist/src/serializer/time-serializer.d.ts.map +1 -0
- package/dist/src/serializer/time-serializer.js +86 -0
- package/dist/src/types/attributes.d.ts +318 -0
- package/dist/src/types/attributes.d.ts.map +1 -0
- package/dist/src/types/attributes.js +225 -0
- package/dist/src/types/errors.d.ts +129 -0
- package/dist/src/types/errors.d.ts.map +1 -0
- package/dist/src/types/errors.js +186 -0
- package/dist/src/types/fields.d.ts +100 -0
- package/dist/src/types/fields.d.ts.map +1 -0
- package/dist/src/types/fields.js +48 -0
- package/dist/src/types/media.d.ts +148 -0
- package/dist/src/types/media.d.ts.map +1 -0
- package/dist/src/types/media.js +137 -0
- package/dist/src/types/network.d.ts +136 -0
- package/dist/src/types/network.d.ts.map +1 -0
- package/dist/src/types/network.js +130 -0
- package/dist/src/types/primitives.d.ts +193 -0
- package/dist/src/types/primitives.d.ts.map +1 -0
- package/dist/src/types/primitives.js +195 -0
- package/dist/src/types/session.d.ts +122 -0
- package/dist/src/types/session.d.ts.map +1 -0
- package/dist/src/types/session.js +81 -0
- package/dist/src/types/time.d.ts +129 -0
- package/dist/src/types/time.d.ts.map +1 -0
- package/dist/src/types/time.js +84 -0
- package/dist/src/utils/address-parser.d.ts +100 -0
- package/dist/src/utils/address-parser.d.ts.map +1 -0
- package/dist/src/utils/address-parser.js +338 -0
- package/dist/src/utils/format-validators.d.ts +77 -0
- package/dist/src/utils/format-validators.d.ts.map +1 -0
- package/dist/src/utils/format-validators.js +504 -0
- package/dist/src/utils/line-reader.d.ts +84 -0
- package/dist/src/utils/line-reader.d.ts.map +1 -0
- package/dist/src/utils/line-reader.js +169 -0
- package/dist/src/utils/time-converter.d.ts +99 -0
- package/dist/src/utils/time-converter.d.ts.map +1 -0
- package/dist/src/utils/time-converter.js +195 -0
- package/dist/src/validator/media-validator.d.ts +27 -0
- package/dist/src/validator/media-validator.d.ts.map +1 -0
- package/dist/src/validator/media-validator.js +241 -0
- package/dist/src/validator/semantic-validator.d.ts +47 -0
- package/dist/src/validator/semantic-validator.d.ts.map +1 -0
- package/dist/src/validator/semantic-validator.js +207 -0
- package/dist/src/validator/session-validator.d.ts +36 -0
- package/dist/src/validator/session-validator.d.ts.map +1 -0
- package/dist/src/validator/session-validator.js +280 -0
- package/dist/tests/integration/round-trip.test.d.ts +5 -0
- package/dist/tests/integration/round-trip.test.d.ts.map +1 -0
- package/dist/tests/integration/round-trip.test.js +320 -0
- package/dist/tests/integration/voip-examples.test.d.ts +5 -0
- package/dist/tests/integration/voip-examples.test.d.ts.map +1 -0
- package/dist/tests/integration/voip-examples.test.js +361 -0
- package/dist/tests/unit/builder/media-builder.test.d.ts +5 -0
- package/dist/tests/unit/builder/media-builder.test.d.ts.map +1 -0
- package/dist/tests/unit/builder/media-builder.test.js +524 -0
- package/dist/tests/unit/builder/session-builder.test.d.ts +5 -0
- package/dist/tests/unit/builder/session-builder.test.d.ts.map +1 -0
- package/dist/tests/unit/builder/session-builder.test.js +367 -0
- package/dist/tests/unit/parser/attribute-parser.test.d.ts +5 -0
- package/dist/tests/unit/parser/attribute-parser.test.d.ts.map +1 -0
- package/dist/tests/unit/parser/attribute-parser.test.js +319 -0
- package/dist/tests/unit/parser/field-parser.test.d.ts +5 -0
- package/dist/tests/unit/parser/field-parser.test.d.ts.map +1 -0
- package/dist/tests/unit/parser/field-parser.test.js +355 -0
- package/dist/tests/unit/parser/media-parser.test.d.ts +5 -0
- package/dist/tests/unit/parser/media-parser.test.d.ts.map +1 -0
- package/dist/tests/unit/parser/media-parser.test.js +241 -0
- package/dist/tests/unit/parser/primitive-parser.test.d.ts +5 -0
- package/dist/tests/unit/parser/primitive-parser.test.d.ts.map +1 -0
- package/dist/tests/unit/parser/primitive-parser.test.js +261 -0
- package/dist/tests/unit/parser/scanner.test.d.ts +5 -0
- package/dist/tests/unit/parser/scanner.test.d.ts.map +1 -0
- package/dist/tests/unit/parser/scanner.test.js +241 -0
- package/dist/tests/unit/parser/session-parser.test.d.ts +5 -0
- package/dist/tests/unit/parser/session-parser.test.d.ts.map +1 -0
- package/dist/tests/unit/parser/session-parser.test.js +346 -0
- package/dist/tests/unit/parser/time-parser.test.d.ts +5 -0
- package/dist/tests/unit/parser/time-parser.test.d.ts.map +1 -0
- package/dist/tests/unit/parser/time-parser.test.js +173 -0
- package/dist/tests/unit/serializer/attribute-serializer.test.d.ts +5 -0
- package/dist/tests/unit/serializer/attribute-serializer.test.d.ts.map +1 -0
- package/dist/tests/unit/serializer/attribute-serializer.test.js +78 -0
- package/dist/tests/unit/serializer/field-serializer.test.d.ts +5 -0
- package/dist/tests/unit/serializer/field-serializer.test.d.ts.map +1 -0
- package/dist/tests/unit/serializer/field-serializer.test.js +159 -0
- package/dist/tests/unit/serializer/media-serializer.test.d.ts +5 -0
- package/dist/tests/unit/serializer/media-serializer.test.d.ts.map +1 -0
- package/dist/tests/unit/serializer/media-serializer.test.js +155 -0
- package/dist/tests/unit/serializer/session-serializer.test.d.ts +5 -0
- package/dist/tests/unit/serializer/session-serializer.test.d.ts.map +1 -0
- package/dist/tests/unit/serializer/session-serializer.test.js +317 -0
- package/dist/tests/unit/serializer/time-serializer.test.d.ts +5 -0
- package/dist/tests/unit/serializer/time-serializer.test.d.ts.map +1 -0
- package/dist/tests/unit/serializer/time-serializer.test.js +115 -0
- package/dist/tests/unit/types/errors.test.d.ts +5 -0
- package/dist/tests/unit/types/errors.test.d.ts.map +1 -0
- package/dist/tests/unit/types/errors.test.js +127 -0
- package/dist/tests/unit/types/network.test.d.ts +5 -0
- package/dist/tests/unit/types/network.test.d.ts.map +1 -0
- package/dist/tests/unit/types/network.test.js +132 -0
- package/dist/tests/unit/types/primitives.test.d.ts +5 -0
- package/dist/tests/unit/types/primitives.test.d.ts.map +1 -0
- package/dist/tests/unit/types/primitives.test.js +108 -0
- package/dist/tests/unit/utils/address-parser.test.d.ts +5 -0
- package/dist/tests/unit/utils/address-parser.test.d.ts.map +1 -0
- package/dist/tests/unit/utils/address-parser.test.js +203 -0
- package/dist/tests/unit/utils/format-validators.test.d.ts +5 -0
- package/dist/tests/unit/utils/format-validators.test.d.ts.map +1 -0
- package/dist/tests/unit/utils/format-validators.test.js +224 -0
- package/dist/tests/unit/utils/line-reader.test.d.ts +5 -0
- package/dist/tests/unit/utils/line-reader.test.d.ts.map +1 -0
- package/dist/tests/unit/utils/line-reader.test.js +157 -0
- package/dist/tests/unit/utils/time-converter.test.d.ts +5 -0
- package/dist/tests/unit/utils/time-converter.test.d.ts.map +1 -0
- package/dist/tests/unit/utils/time-converter.test.js +190 -0
- package/dist/tests/unit/validator/media-validator.test.d.ts +5 -0
- package/dist/tests/unit/validator/media-validator.test.d.ts.map +1 -0
- package/dist/tests/unit/validator/media-validator.test.js +313 -0
- package/dist/tests/unit/validator/semantic-validator.test.d.ts +5 -0
- package/dist/tests/unit/validator/semantic-validator.test.d.ts.map +1 -0
- package/dist/tests/unit/validator/semantic-validator.test.js +262 -0
- package/dist/tests/unit/validator/session-validator.test.d.ts +5 -0
- package/dist/tests/unit/validator/session-validator.test.d.ts.map +1 -0
- package/dist/tests/unit/validator/session-validator.test.js +447 -0
- package/package.json +50 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Field parsers for SDP (RFC 8866)
|
|
3
|
+
*
|
|
4
|
+
* Parsers for individual SDP field types (v=, o=, s=, i=, u=, e=, p=, c=, b=, k=)
|
|
5
|
+
*/
|
|
6
|
+
import { Scanner } from './scanner';
|
|
7
|
+
import { Version, SessionName, Information, URI, Email, Phone, Key } from '../types/primitives';
|
|
8
|
+
import { Origin, Connection, Bandwidth } from '../types/fields';
|
|
9
|
+
/**
|
|
10
|
+
* Parse version field
|
|
11
|
+
*
|
|
12
|
+
* Format: v=0
|
|
13
|
+
*
|
|
14
|
+
* @param scanner - Scanner instance
|
|
15
|
+
* @returns Version (currently always 0)
|
|
16
|
+
* @throws ParseError if version is not 0
|
|
17
|
+
*/
|
|
18
|
+
export declare function parseVersionField(scanner: Scanner): Version;
|
|
19
|
+
/**
|
|
20
|
+
* Parse origin field
|
|
21
|
+
*
|
|
22
|
+
* Format: o=<username> SP <sess-id> SP <sess-version> SP <nettype> SP <addrtype> SP <unicast-address>
|
|
23
|
+
*
|
|
24
|
+
* @param scanner - Scanner instance
|
|
25
|
+
* @returns Origin object
|
|
26
|
+
* @throws ParseError if invalid format
|
|
27
|
+
*/
|
|
28
|
+
export declare function parseOriginField(scanner: Scanner): Origin;
|
|
29
|
+
/**
|
|
30
|
+
* Parse session name field
|
|
31
|
+
*
|
|
32
|
+
* Format: s=<session name>
|
|
33
|
+
*
|
|
34
|
+
* Session name can be a single space (" ") if there is no meaningful name.
|
|
35
|
+
*
|
|
36
|
+
* @param scanner - Scanner instance
|
|
37
|
+
* @returns SessionName
|
|
38
|
+
* @throws ParseError if empty
|
|
39
|
+
*/
|
|
40
|
+
export declare function parseSessionNameField(scanner: Scanner): SessionName;
|
|
41
|
+
/**
|
|
42
|
+
* Parse information field
|
|
43
|
+
*
|
|
44
|
+
* Format: i=<session information>
|
|
45
|
+
*
|
|
46
|
+
* @param scanner - Scanner instance
|
|
47
|
+
* @returns Information string
|
|
48
|
+
*/
|
|
49
|
+
export declare function parseInformationField(scanner: Scanner): Information;
|
|
50
|
+
/**
|
|
51
|
+
* Parse URI field
|
|
52
|
+
*
|
|
53
|
+
* Format: u=<uri>
|
|
54
|
+
*
|
|
55
|
+
* @param scanner - Scanner instance
|
|
56
|
+
* @returns URI string
|
|
57
|
+
*/
|
|
58
|
+
export declare function parseUriField(scanner: Scanner): URI;
|
|
59
|
+
/**
|
|
60
|
+
* Parse email field
|
|
61
|
+
*
|
|
62
|
+
* Format: e=<email-address> or e=<display-name> <email-address>
|
|
63
|
+
*
|
|
64
|
+
* @param scanner - Scanner instance
|
|
65
|
+
* @returns Email string
|
|
66
|
+
*/
|
|
67
|
+
export declare function parseEmailField(scanner: Scanner): Email;
|
|
68
|
+
/**
|
|
69
|
+
* Parse phone field
|
|
70
|
+
*
|
|
71
|
+
* Format: p=<phone-number> or p=<display-name> <phone-number>
|
|
72
|
+
*
|
|
73
|
+
* @param scanner - Scanner instance
|
|
74
|
+
* @returns Phone string
|
|
75
|
+
*/
|
|
76
|
+
export declare function parsePhoneField(scanner: Scanner): Phone;
|
|
77
|
+
/**
|
|
78
|
+
* Parse connection field
|
|
79
|
+
*
|
|
80
|
+
* Format: c=<nettype> SP <addrtype> SP <connection-address>
|
|
81
|
+
*
|
|
82
|
+
* connection-address can be:
|
|
83
|
+
* - unicast-address
|
|
84
|
+
* - multicast-address (with optional TTL and number of addresses)
|
|
85
|
+
*
|
|
86
|
+
* @param scanner - Scanner instance
|
|
87
|
+
* @returns Connection object
|
|
88
|
+
* @throws ParseError if invalid format
|
|
89
|
+
*/
|
|
90
|
+
export declare function parseConnectionField(scanner: Scanner): Connection;
|
|
91
|
+
/**
|
|
92
|
+
* Parse bandwidth field
|
|
93
|
+
*
|
|
94
|
+
* Format: b=<bwtype>:<bandwidth>
|
|
95
|
+
*
|
|
96
|
+
* bwtype can be:
|
|
97
|
+
* - CT (Conference Total)
|
|
98
|
+
* - AS (Application Specific)
|
|
99
|
+
* - Or any registered extension type
|
|
100
|
+
*
|
|
101
|
+
* bandwidth is in kilobits per second
|
|
102
|
+
*
|
|
103
|
+
* @param scanner - Scanner instance
|
|
104
|
+
* @returns Bandwidth object
|
|
105
|
+
* @throws ParseError if invalid format
|
|
106
|
+
*/
|
|
107
|
+
export declare function parseBandwidthField(scanner: Scanner): Bandwidth;
|
|
108
|
+
/**
|
|
109
|
+
* Parse key field (OBSOLETE per RFC 8866)
|
|
110
|
+
*
|
|
111
|
+
* Format: k=<method>:<encryption-key>
|
|
112
|
+
* Or: k=<method> (if no key data)
|
|
113
|
+
*
|
|
114
|
+
* Common methods:
|
|
115
|
+
* - clear:<encryption-key> - key is included untransformed
|
|
116
|
+
* - base64:<encoded-encryption-key> - key is base64 encoded
|
|
117
|
+
* - uri:<uri-to-obtain-key> - URI where key can be obtained
|
|
118
|
+
* - prompt - no key included, user will be prompted
|
|
119
|
+
*
|
|
120
|
+
* @param scanner - Scanner instance
|
|
121
|
+
* @returns Key string
|
|
122
|
+
*/
|
|
123
|
+
export declare function parseKeyField(scanner: Scanner): Key;
|
|
124
|
+
//# sourceMappingURL=field-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-parser.d.ts","sourceRoot":"","sources":["../../../src/parser/field-parser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAapC,OAAO,EACL,OAAO,EACP,WAAW,EAMX,WAAW,EACX,GAAG,EACH,KAAK,EACL,KAAK,EACL,GAAG,EAGJ,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAmD,MAAM,iBAAiB,CAAC;AA0BjH;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAa3D;AAMD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAmCzD;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,WAAW,CAenE;AAMD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,WAAW,CAGnE;AAMD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,GAAG,CAEnD;AAMD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,KAAK,CAEvD;AAMD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,KAAK,CAEvD;AAMD;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU,CAiBjE;AAyHD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,CAqB/D;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,GAAG,CAGnD"}
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Field parsers for SDP (RFC 8866)
|
|
4
|
+
*
|
|
5
|
+
* Parsers for individual SDP field types (v=, o=, s=, i=, u=, e=, p=, c=, b=, k=)
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.parseVersionField = parseVersionField;
|
|
9
|
+
exports.parseOriginField = parseOriginField;
|
|
10
|
+
exports.parseSessionNameField = parseSessionNameField;
|
|
11
|
+
exports.parseInformationField = parseInformationField;
|
|
12
|
+
exports.parseUriField = parseUriField;
|
|
13
|
+
exports.parseEmailField = parseEmailField;
|
|
14
|
+
exports.parsePhoneField = parsePhoneField;
|
|
15
|
+
exports.parseConnectionField = parseConnectionField;
|
|
16
|
+
exports.parseBandwidthField = parseBandwidthField;
|
|
17
|
+
exports.parseKeyField = parseKeyField;
|
|
18
|
+
const primitive_parser_1 = require("./primitive-parser");
|
|
19
|
+
const errors_1 = require("../types/errors");
|
|
20
|
+
const primitives_1 = require("../types/primitives");
|
|
21
|
+
const fields_1 = require("../types/fields");
|
|
22
|
+
const network_1 = require("../types/network");
|
|
23
|
+
const address_parser_1 = require("../utils/address-parser");
|
|
24
|
+
const address_parser_2 = require("../utils/address-parser");
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// Version Field Parser (v=)
|
|
27
|
+
// ============================================================================
|
|
28
|
+
/**
|
|
29
|
+
* Parse version field
|
|
30
|
+
*
|
|
31
|
+
* Format: v=0
|
|
32
|
+
*
|
|
33
|
+
* @param scanner - Scanner instance
|
|
34
|
+
* @returns Version (currently always 0)
|
|
35
|
+
* @throws ParseError if version is not 0
|
|
36
|
+
*/
|
|
37
|
+
function parseVersionField(scanner) {
|
|
38
|
+
const start = scanner.getPosition();
|
|
39
|
+
const version = (0, primitive_parser_1.parseInteger)(scanner);
|
|
40
|
+
if (version !== 0) {
|
|
41
|
+
throw new errors_1.ParseError(`Unsupported SDP version: ${version} (expected 0)`, start.line, start.column);
|
|
42
|
+
}
|
|
43
|
+
return (0, primitives_1.createVersion)(version);
|
|
44
|
+
}
|
|
45
|
+
// ============================================================================
|
|
46
|
+
// Origin Field Parser (o=)
|
|
47
|
+
// ============================================================================
|
|
48
|
+
/**
|
|
49
|
+
* Parse origin field
|
|
50
|
+
*
|
|
51
|
+
* Format: o=<username> SP <sess-id> SP <sess-version> SP <nettype> SP <addrtype> SP <unicast-address>
|
|
52
|
+
*
|
|
53
|
+
* @param scanner - Scanner instance
|
|
54
|
+
* @returns Origin object
|
|
55
|
+
* @throws ParseError if invalid format
|
|
56
|
+
*/
|
|
57
|
+
function parseOriginField(scanner) {
|
|
58
|
+
const start = scanner.getPosition();
|
|
59
|
+
// Parse username (non-whitespace string)
|
|
60
|
+
const username = (0, primitive_parser_1.parseNonWSString)(scanner);
|
|
61
|
+
scanner.expectSpace();
|
|
62
|
+
// Parse session ID (non-whitespace string)
|
|
63
|
+
const sessId = (0, primitive_parser_1.parseNonWSString)(scanner);
|
|
64
|
+
scanner.expectSpace();
|
|
65
|
+
// Parse session version (non-whitespace string)
|
|
66
|
+
const sessVersion = (0, primitive_parser_1.parseNonWSString)(scanner);
|
|
67
|
+
scanner.expectSpace();
|
|
68
|
+
// Parse network type (token)
|
|
69
|
+
const netType = (0, primitive_parser_1.parseToken)(scanner);
|
|
70
|
+
scanner.expectSpace();
|
|
71
|
+
// Parse address type (token)
|
|
72
|
+
const addrType = (0, primitive_parser_1.parseToken)(scanner);
|
|
73
|
+
scanner.expectSpace();
|
|
74
|
+
// Parse unicast address (depends on addrType)
|
|
75
|
+
const unicastAddress = (0, address_parser_1.parseUnicastAddress)(scanner.readUntil(/[\r\n]/), addrType);
|
|
76
|
+
return (0, fields_1.createOrigin)(username, sessId, sessVersion, netType, addrType, unicastAddress);
|
|
77
|
+
}
|
|
78
|
+
// ============================================================================
|
|
79
|
+
// Session Name Field Parser (s=)
|
|
80
|
+
// ============================================================================
|
|
81
|
+
/**
|
|
82
|
+
* Parse session name field
|
|
83
|
+
*
|
|
84
|
+
* Format: s=<session name>
|
|
85
|
+
*
|
|
86
|
+
* Session name can be a single space (" ") if there is no meaningful name.
|
|
87
|
+
*
|
|
88
|
+
* @param scanner - Scanner instance
|
|
89
|
+
* @returns SessionName
|
|
90
|
+
* @throws ParseError if empty
|
|
91
|
+
*/
|
|
92
|
+
function parseSessionNameField(scanner) {
|
|
93
|
+
const start = scanner.getPosition();
|
|
94
|
+
// Read until end of line
|
|
95
|
+
const name = scanner.readUntil(/[\r\n]/);
|
|
96
|
+
if (name.length === 0) {
|
|
97
|
+
throw new errors_1.ParseError('Session name cannot be empty', start.line, start.column);
|
|
98
|
+
}
|
|
99
|
+
return name;
|
|
100
|
+
}
|
|
101
|
+
// ============================================================================
|
|
102
|
+
// Information Field Parser (i=)
|
|
103
|
+
// ============================================================================
|
|
104
|
+
/**
|
|
105
|
+
* Parse information field
|
|
106
|
+
*
|
|
107
|
+
* Format: i=<session information>
|
|
108
|
+
*
|
|
109
|
+
* @param scanner - Scanner instance
|
|
110
|
+
* @returns Information string
|
|
111
|
+
*/
|
|
112
|
+
function parseInformationField(scanner) {
|
|
113
|
+
// Read until end of line (can be empty)
|
|
114
|
+
return scanner.readUntil(/[\r\n]/);
|
|
115
|
+
}
|
|
116
|
+
// ============================================================================
|
|
117
|
+
// URI Field Parser (u=)
|
|
118
|
+
// ============================================================================
|
|
119
|
+
/**
|
|
120
|
+
* Parse URI field
|
|
121
|
+
*
|
|
122
|
+
* Format: u=<uri>
|
|
123
|
+
*
|
|
124
|
+
* @param scanner - Scanner instance
|
|
125
|
+
* @returns URI string
|
|
126
|
+
*/
|
|
127
|
+
function parseUriField(scanner) {
|
|
128
|
+
return (0, primitive_parser_1.parseURI)(scanner);
|
|
129
|
+
}
|
|
130
|
+
// ============================================================================
|
|
131
|
+
// Email Field Parser (e=)
|
|
132
|
+
// ============================================================================
|
|
133
|
+
/**
|
|
134
|
+
* Parse email field
|
|
135
|
+
*
|
|
136
|
+
* Format: e=<email-address> or e=<display-name> <email-address>
|
|
137
|
+
*
|
|
138
|
+
* @param scanner - Scanner instance
|
|
139
|
+
* @returns Email string
|
|
140
|
+
*/
|
|
141
|
+
function parseEmailField(scanner) {
|
|
142
|
+
return (0, primitive_parser_1.parseEmail)(scanner);
|
|
143
|
+
}
|
|
144
|
+
// ============================================================================
|
|
145
|
+
// Phone Field Parser (p=)
|
|
146
|
+
// ============================================================================
|
|
147
|
+
/**
|
|
148
|
+
* Parse phone field
|
|
149
|
+
*
|
|
150
|
+
* Format: p=<phone-number> or p=<display-name> <phone-number>
|
|
151
|
+
*
|
|
152
|
+
* @param scanner - Scanner instance
|
|
153
|
+
* @returns Phone string
|
|
154
|
+
*/
|
|
155
|
+
function parsePhoneField(scanner) {
|
|
156
|
+
return (0, primitive_parser_1.parsePhone)(scanner);
|
|
157
|
+
}
|
|
158
|
+
// ============================================================================
|
|
159
|
+
// Connection Field Parser (c=)
|
|
160
|
+
// ============================================================================
|
|
161
|
+
/**
|
|
162
|
+
* Parse connection field
|
|
163
|
+
*
|
|
164
|
+
* Format: c=<nettype> SP <addrtype> SP <connection-address>
|
|
165
|
+
*
|
|
166
|
+
* connection-address can be:
|
|
167
|
+
* - unicast-address
|
|
168
|
+
* - multicast-address (with optional TTL and number of addresses)
|
|
169
|
+
*
|
|
170
|
+
* @param scanner - Scanner instance
|
|
171
|
+
* @returns Connection object
|
|
172
|
+
* @throws ParseError if invalid format
|
|
173
|
+
*/
|
|
174
|
+
function parseConnectionField(scanner) {
|
|
175
|
+
const start = scanner.getPosition();
|
|
176
|
+
// Parse network type (token)
|
|
177
|
+
const netType = (0, primitive_parser_1.parseToken)(scanner);
|
|
178
|
+
scanner.expectSpace();
|
|
179
|
+
// Parse address type (token)
|
|
180
|
+
const addrType = (0, primitive_parser_1.parseToken)(scanner);
|
|
181
|
+
scanner.expectSpace();
|
|
182
|
+
// Parse connection address
|
|
183
|
+
const connectionAddress = parseConnectionAddress(scanner, addrType);
|
|
184
|
+
return (0, fields_1.createConnection)(netType, addrType, connectionAddress);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Parse connection address (unicast or multicast)
|
|
188
|
+
*
|
|
189
|
+
* Multicast format:
|
|
190
|
+
* - IPv4: <base-multicast-address>/<ttl>[/<number of addresses>]
|
|
191
|
+
* - IPv6: <base-multicast-address>[/<number of addresses>]
|
|
192
|
+
* - FQDN: <base-multicast-address>[/<ttl>][/<number of addresses>]
|
|
193
|
+
*
|
|
194
|
+
* RFC 8866 Section 5.7: When TTL or number of addresses are present,
|
|
195
|
+
* the address MUST be a multicast address.
|
|
196
|
+
*
|
|
197
|
+
* @param scanner - Scanner instance
|
|
198
|
+
* @param addrType - Address type (IP4, IP6, etc.)
|
|
199
|
+
* @returns ConnectionAddress (unicast or multicast)
|
|
200
|
+
*/
|
|
201
|
+
function parseConnectionAddress(scanner, addrType) {
|
|
202
|
+
const start = scanner.getPosition();
|
|
203
|
+
// Read the address part (until / or end of line)
|
|
204
|
+
const addressPart = scanner.readUntil(/[\/\r\n]/);
|
|
205
|
+
if (addressPart.length === 0) {
|
|
206
|
+
throw new errors_1.ParseError('Expected connection address', start.line, start.column);
|
|
207
|
+
}
|
|
208
|
+
// Check if there's a / indicating multicast parameters
|
|
209
|
+
if (scanner.peek() !== '/') {
|
|
210
|
+
// Unicast address
|
|
211
|
+
return (0, address_parser_1.parseUnicastAddress)(addressPart, addrType);
|
|
212
|
+
}
|
|
213
|
+
// Multicast address - parse TTL and/or number of addresses
|
|
214
|
+
scanner.advance(); // consume '/'
|
|
215
|
+
const firstParam = (0, primitive_parser_1.parseInteger)(scanner);
|
|
216
|
+
let ttl;
|
|
217
|
+
let numAddresses;
|
|
218
|
+
// Check for second parameter
|
|
219
|
+
if (scanner.peek() === '/') {
|
|
220
|
+
scanner.advance(); // consume second '/'
|
|
221
|
+
const secondParam = (0, primitive_parser_1.parseInteger)(scanner);
|
|
222
|
+
// For IPv4: first is TTL, second is numAddresses
|
|
223
|
+
// For FQDN: first is TTL, second is numAddresses
|
|
224
|
+
// For IPv6: TTL is forbidden, so first is numAddresses
|
|
225
|
+
if (addrType === 'IP6') {
|
|
226
|
+
throw new errors_1.ParseError('IPv6 multicast addresses cannot have TTL parameter', start.line, start.column);
|
|
227
|
+
}
|
|
228
|
+
ttl = firstParam;
|
|
229
|
+
numAddresses = secondParam;
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
// Only one parameter
|
|
233
|
+
if (addrType === 'IP4') {
|
|
234
|
+
// For IPv4, TTL is required
|
|
235
|
+
ttl = firstParam;
|
|
236
|
+
}
|
|
237
|
+
else if (addrType === 'IP6') {
|
|
238
|
+
// For IPv6, it's number of addresses
|
|
239
|
+
numAddresses = firstParam;
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
// For FQDN or other, assume it's TTL
|
|
243
|
+
ttl = firstParam;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
// Create appropriate multicast address based on address type
|
|
247
|
+
// RFC 8866 Section 5.7: Validate that address is actually multicast when parameters are present
|
|
248
|
+
if (addrType === 'IP4') {
|
|
249
|
+
// For IP4, could be IPv4 address or FQDN
|
|
250
|
+
if ((0, address_parser_2.isIP4Address)(addressPart)) {
|
|
251
|
+
// Validate that IPv4 address is in multicast range (224.0.0.0 - 239.255.255.255)
|
|
252
|
+
if (!(0, address_parser_2.isIP4MulticastAddress)(addressPart)) {
|
|
253
|
+
throw new errors_1.ParseError(`IPv4 address '${addressPart}' with TTL/numAddresses must be a multicast address (224.0.0.0 - 239.255.255.255)`, start.line, start.column);
|
|
254
|
+
}
|
|
255
|
+
return (0, network_1.createIP4MulticastAddress)((0, network_1.createIP4Address)(addressPart), ttl, numAddresses);
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
// Assume FQDN - cannot validate multicast range for FQDNs
|
|
259
|
+
return (0, network_1.createFQDNMulticastAddress)((0, network_1.createFQDN)(addressPart), ttl, numAddresses);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
else if (addrType === 'IP6') {
|
|
263
|
+
// For IP6, could be IPv6 address or FQDN
|
|
264
|
+
if ((0, address_parser_2.isIP6Address)(addressPart)) {
|
|
265
|
+
// Validate that IPv6 address is multicast (starts with FF)
|
|
266
|
+
if (!(0, address_parser_2.isIP6MulticastAddress)(addressPart)) {
|
|
267
|
+
throw new errors_1.ParseError(`IPv6 address '${addressPart}' with numAddresses must be a multicast address (must start with FF)`, start.line, start.column);
|
|
268
|
+
}
|
|
269
|
+
return (0, network_1.createIP6MulticastAddress)((0, network_1.createIP6Address)(addressPart), numAddresses);
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
// Assume FQDN - cannot validate multicast range for FQDNs
|
|
273
|
+
return (0, network_1.createFQDNMulticastAddress)((0, network_1.createFQDN)(addressPart), ttl, numAddresses);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
// Extension address type
|
|
278
|
+
return (0, network_1.createFQDNMulticastAddress)((0, network_1.createFQDN)(addressPart), ttl, numAddresses);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
// ============================================================================
|
|
282
|
+
// Bandwidth Field Parser (b=)
|
|
283
|
+
// ============================================================================
|
|
284
|
+
/**
|
|
285
|
+
* Parse bandwidth field
|
|
286
|
+
*
|
|
287
|
+
* Format: b=<bwtype>:<bandwidth>
|
|
288
|
+
*
|
|
289
|
+
* bwtype can be:
|
|
290
|
+
* - CT (Conference Total)
|
|
291
|
+
* - AS (Application Specific)
|
|
292
|
+
* - Or any registered extension type
|
|
293
|
+
*
|
|
294
|
+
* bandwidth is in kilobits per second
|
|
295
|
+
*
|
|
296
|
+
* @param scanner - Scanner instance
|
|
297
|
+
* @returns Bandwidth object
|
|
298
|
+
* @throws ParseError if invalid format
|
|
299
|
+
*/
|
|
300
|
+
function parseBandwidthField(scanner) {
|
|
301
|
+
const start = scanner.getPosition();
|
|
302
|
+
// Parse bandwidth type (token before ':')
|
|
303
|
+
const bwtype = (0, primitive_parser_1.parseToken)(scanner);
|
|
304
|
+
// Expect ':'
|
|
305
|
+
const ch = scanner.peek();
|
|
306
|
+
if (ch !== ':') {
|
|
307
|
+
throw new errors_1.ParseError(`Expected ':' after bandwidth type, found "${ch}"`, start.line, start.column);
|
|
308
|
+
}
|
|
309
|
+
scanner.advance();
|
|
310
|
+
// Parse bandwidth value (integer)
|
|
311
|
+
const bandwidth = (0, primitive_parser_1.parseInteger)(scanner);
|
|
312
|
+
return (0, fields_1.createBandwidth)(bwtype, bandwidth);
|
|
313
|
+
}
|
|
314
|
+
// ============================================================================
|
|
315
|
+
// Key Field Parser (k=)
|
|
316
|
+
// ============================================================================
|
|
317
|
+
/**
|
|
318
|
+
* Parse key field (OBSOLETE per RFC 8866)
|
|
319
|
+
*
|
|
320
|
+
* Format: k=<method>:<encryption-key>
|
|
321
|
+
* Or: k=<method> (if no key data)
|
|
322
|
+
*
|
|
323
|
+
* Common methods:
|
|
324
|
+
* - clear:<encryption-key> - key is included untransformed
|
|
325
|
+
* - base64:<encoded-encryption-key> - key is base64 encoded
|
|
326
|
+
* - uri:<uri-to-obtain-key> - URI where key can be obtained
|
|
327
|
+
* - prompt - no key included, user will be prompted
|
|
328
|
+
*
|
|
329
|
+
* @param scanner - Scanner instance
|
|
330
|
+
* @returns Key string
|
|
331
|
+
*/
|
|
332
|
+
function parseKeyField(scanner) {
|
|
333
|
+
// Read entire line
|
|
334
|
+
return scanner.readUntil(/[\r\n]/);
|
|
335
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Media description parser for SDP (RFC 8866)
|
|
3
|
+
*
|
|
4
|
+
* Parses media descriptions (m= and associated fields)
|
|
5
|
+
*/
|
|
6
|
+
import { Scanner } from './scanner';
|
|
7
|
+
import { Media, MediaDescription } from '../types/media';
|
|
8
|
+
/**
|
|
9
|
+
* Parse media field
|
|
10
|
+
*
|
|
11
|
+
* Format: m=<media> <port> <proto> <fmt> ...
|
|
12
|
+
*
|
|
13
|
+
* Examples:
|
|
14
|
+
* - m=audio 49170 RTP/AVP 0 8 101
|
|
15
|
+
* - m=video 51372 RTP/AVP 99
|
|
16
|
+
* - m=audio 49170/2 RTP/AVP 0 (port range for hierarchical encoding)
|
|
17
|
+
*
|
|
18
|
+
* @param scanner - Scanner instance
|
|
19
|
+
* @returns Media object
|
|
20
|
+
* @throws ParseError if invalid format
|
|
21
|
+
*/
|
|
22
|
+
export declare function parseMediaField(scanner: Scanner): Media;
|
|
23
|
+
/**
|
|
24
|
+
* Parse media description
|
|
25
|
+
*
|
|
26
|
+
* A media description consists of:
|
|
27
|
+
* - One media field (m=) [required]
|
|
28
|
+
* - Optional information field (i=)
|
|
29
|
+
* - Zero or more connection fields (c=)
|
|
30
|
+
* - Zero or more bandwidth fields (b=)
|
|
31
|
+
* - Optional encryption key field (k=)
|
|
32
|
+
* - Zero or more attribute fields (a=)
|
|
33
|
+
*
|
|
34
|
+
* The media field has already been identified, but not yet parsed.
|
|
35
|
+
*
|
|
36
|
+
* @param scanner - Scanner positioned at the start of media field value (after "m=")
|
|
37
|
+
* @param additionalLines - Additional lines that might belong to this media description
|
|
38
|
+
* @returns MediaDescription object
|
|
39
|
+
* @throws ParseError if invalid format
|
|
40
|
+
*/
|
|
41
|
+
export declare function parseMediaDescription(mediaFieldScanner: Scanner, additionalLines: {
|
|
42
|
+
type: string;
|
|
43
|
+
value: string;
|
|
44
|
+
}[]): MediaDescription;
|
|
45
|
+
//# sourceMappingURL=media-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-parser.d.ts","sourceRoot":"","sources":["../../../src/parser/media-parser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,OAAO,EACL,KAAK,EACL,gBAAgB,EAMjB,MAAM,gBAAgB,CAAC;AAWxB;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,KAAK,CA6FvD;AAMD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CACnC,iBAAiB,EAAE,OAAO,EAC1B,eAAe,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,GACjD,gBAAgB,CAmElB"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Media description parser for SDP (RFC 8866)
|
|
4
|
+
*
|
|
5
|
+
* Parses media descriptions (m= and associated fields)
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.parseMediaField = parseMediaField;
|
|
9
|
+
exports.parseMediaDescription = parseMediaDescription;
|
|
10
|
+
const scanner_1 = require("./scanner");
|
|
11
|
+
const primitive_parser_1 = require("./primitive-parser");
|
|
12
|
+
const field_parser_1 = require("./field-parser");
|
|
13
|
+
const attribute_parser_1 = require("./attribute-parser");
|
|
14
|
+
const errors_1 = require("../types/errors");
|
|
15
|
+
const media_1 = require("../types/media");
|
|
16
|
+
const primitives_1 = require("../types/primitives");
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// Media Field Parser (m=)
|
|
19
|
+
// ============================================================================
|
|
20
|
+
/**
|
|
21
|
+
* Parse media field
|
|
22
|
+
*
|
|
23
|
+
* Format: m=<media> <port> <proto> <fmt> ...
|
|
24
|
+
*
|
|
25
|
+
* Examples:
|
|
26
|
+
* - m=audio 49170 RTP/AVP 0 8 101
|
|
27
|
+
* - m=video 51372 RTP/AVP 99
|
|
28
|
+
* - m=audio 49170/2 RTP/AVP 0 (port range for hierarchical encoding)
|
|
29
|
+
*
|
|
30
|
+
* @param scanner - Scanner instance
|
|
31
|
+
* @returns Media object
|
|
32
|
+
* @throws ParseError if invalid format
|
|
33
|
+
*/
|
|
34
|
+
function parseMediaField(scanner) {
|
|
35
|
+
const start = scanner.getPosition();
|
|
36
|
+
// Parse media type (token)
|
|
37
|
+
const mediaTypeStr = (0, primitive_parser_1.parseToken)(scanner);
|
|
38
|
+
const mediaType = (0, primitives_1.createMediaType)(mediaTypeStr);
|
|
39
|
+
// Parse port or port range
|
|
40
|
+
scanner.skipWhitespace();
|
|
41
|
+
const portStart = scanner.getPosition();
|
|
42
|
+
// Read until whitespace (to capture '/' for port ranges)
|
|
43
|
+
const portStr = scanner.readWhile(ch => ch !== ' ' && ch !== '\t' && ch !== '\r' && ch !== '\n');
|
|
44
|
+
if (portStr.length === 0) {
|
|
45
|
+
throw new errors_1.ParseError('Expected port number', portStart.line, portStart.column);
|
|
46
|
+
}
|
|
47
|
+
let port;
|
|
48
|
+
// Check if port is a range (contains '/')
|
|
49
|
+
if (portStr.includes('/')) {
|
|
50
|
+
const parts = portStr.split('/');
|
|
51
|
+
if (parts.length !== 2) {
|
|
52
|
+
throw new errors_1.ParseError(`Invalid port range format: expected "<port>/<count>", got "${portStr}"`, portStart.line, portStart.column);
|
|
53
|
+
}
|
|
54
|
+
const base = parseInt(parts[0], 10);
|
|
55
|
+
const count = parseInt(parts[1], 10);
|
|
56
|
+
if (isNaN(base) || isNaN(count)) {
|
|
57
|
+
throw new errors_1.ParseError(`Invalid port range: "${portStr}"`, portStart.line, portStart.column);
|
|
58
|
+
}
|
|
59
|
+
port = (0, media_1.createPortRange)(base, count);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
// Simple port
|
|
63
|
+
const portNum = parseInt(portStr, 10);
|
|
64
|
+
if (isNaN(portNum)) {
|
|
65
|
+
throw new errors_1.ParseError(`Invalid port number: "${portStr}"`, portStart.line, portStart.column);
|
|
66
|
+
}
|
|
67
|
+
port = (0, media_1.createSimplePort)(portNum);
|
|
68
|
+
}
|
|
69
|
+
// Parse protocol (read until whitespace, protocols can contain '/' like RTP/AVP)
|
|
70
|
+
scanner.skipWhitespace();
|
|
71
|
+
const protoStart = scanner.getPosition();
|
|
72
|
+
const protoStr = scanner.readWhile(ch => ch !== ' ' && ch !== '\t' && ch !== '\r' && ch !== '\n');
|
|
73
|
+
if (protoStr.length === 0) {
|
|
74
|
+
throw new errors_1.ParseError('Expected protocol', protoStart.line, protoStart.column);
|
|
75
|
+
}
|
|
76
|
+
const proto = (0, primitives_1.createProtocol)(protoStr);
|
|
77
|
+
// Parse formats (one or more)
|
|
78
|
+
const formats = [];
|
|
79
|
+
scanner.skipWhitespace();
|
|
80
|
+
while (!scanner.isEOF() && scanner.peek() !== '\r' && scanner.peek() !== '\n') {
|
|
81
|
+
const formatStr = (0, primitive_parser_1.parseToken)(scanner);
|
|
82
|
+
formats.push((0, primitives_1.createFormat)(formatStr));
|
|
83
|
+
scanner.skipWhitespace();
|
|
84
|
+
}
|
|
85
|
+
if (formats.length === 0) {
|
|
86
|
+
throw new errors_1.ParseError('at least one media format is required', start.line, start.column);
|
|
87
|
+
}
|
|
88
|
+
return (0, media_1.createMedia)(mediaType, port, proto, formats);
|
|
89
|
+
}
|
|
90
|
+
// ============================================================================
|
|
91
|
+
// Media Description Parser
|
|
92
|
+
// ============================================================================
|
|
93
|
+
/**
|
|
94
|
+
* Parse media description
|
|
95
|
+
*
|
|
96
|
+
* A media description consists of:
|
|
97
|
+
* - One media field (m=) [required]
|
|
98
|
+
* - Optional information field (i=)
|
|
99
|
+
* - Zero or more connection fields (c=)
|
|
100
|
+
* - Zero or more bandwidth fields (b=)
|
|
101
|
+
* - Optional encryption key field (k=)
|
|
102
|
+
* - Zero or more attribute fields (a=)
|
|
103
|
+
*
|
|
104
|
+
* The media field has already been identified, but not yet parsed.
|
|
105
|
+
*
|
|
106
|
+
* @param scanner - Scanner positioned at the start of media field value (after "m=")
|
|
107
|
+
* @param additionalLines - Additional lines that might belong to this media description
|
|
108
|
+
* @returns MediaDescription object
|
|
109
|
+
* @throws ParseError if invalid format
|
|
110
|
+
*/
|
|
111
|
+
function parseMediaDescription(mediaFieldScanner, additionalLines) {
|
|
112
|
+
// Parse the media field (m=)
|
|
113
|
+
const media = parseMediaField(mediaFieldScanner);
|
|
114
|
+
// Parse optional fields
|
|
115
|
+
let information;
|
|
116
|
+
const connections = [];
|
|
117
|
+
const bandwidths = [];
|
|
118
|
+
let key;
|
|
119
|
+
const attributes = [];
|
|
120
|
+
for (const line of additionalLines) {
|
|
121
|
+
const scanner = new scanner_1.Scanner(line.value);
|
|
122
|
+
switch (line.type) {
|
|
123
|
+
case 'i':
|
|
124
|
+
if (information !== undefined) {
|
|
125
|
+
throw new errors_1.ParseError('Duplicate information field in media description', 0, 0);
|
|
126
|
+
}
|
|
127
|
+
information = (0, field_parser_1.parseInformationField)(scanner);
|
|
128
|
+
break;
|
|
129
|
+
case 'c':
|
|
130
|
+
connections.push((0, field_parser_1.parseConnectionField)(scanner));
|
|
131
|
+
break;
|
|
132
|
+
case 'b':
|
|
133
|
+
bandwidths.push((0, field_parser_1.parseBandwidthField)(scanner));
|
|
134
|
+
break;
|
|
135
|
+
case 'k':
|
|
136
|
+
if (key !== undefined) {
|
|
137
|
+
throw new errors_1.ParseError('Duplicate key field in media description', 0, 0);
|
|
138
|
+
}
|
|
139
|
+
key = (0, field_parser_1.parseKeyField)(scanner);
|
|
140
|
+
break;
|
|
141
|
+
case 'a':
|
|
142
|
+
attributes.push((0, attribute_parser_1.parseAttributeField)(scanner));
|
|
143
|
+
break;
|
|
144
|
+
default:
|
|
145
|
+
// Unknown field type - this shouldn't happen if called correctly
|
|
146
|
+
// Media description ends when we encounter another m= or end of input
|
|
147
|
+
throw new errors_1.ParseError(`Unexpected field type in media description: ${line.type}=`, 0, 0);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return (0, media_1.createMediaDescription)(media, {
|
|
151
|
+
information,
|
|
152
|
+
connections,
|
|
153
|
+
bandwidths,
|
|
154
|
+
key,
|
|
155
|
+
attributes,
|
|
156
|
+
});
|
|
157
|
+
}
|