sei-nal-unit-generator.js
3.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/**
* Helper functions for creating 608/708 SEI NAL units
*/
'use strict';
var box = require('./mp4-helpers').box;
// Create SEI nal-units from Caption packets
var makeSeiFromCaptionPacket = function(caption) {
return {
pts: caption.pts,
dts: caption.dts,
nalUnitType: 'sei_rbsp',
escapedRBSP: new Uint8Array([
0x04, // payload_type === user_data_registered_itu_t_t35
0x0e, // payload_size
181, // itu_t_t35_country_code
0x00, 0x31, // itu_t_t35_provider_code
0x47, 0x41, 0x39, 0x34, // user_identifier, "GA94"
0x03, // user_data_type_code, 0x03 is cc_data
// 110 00001
0xc1, // process_cc_data, cc_count
0xff, // reserved
// 1111 1100
(0xfc | caption.type), // cc_valid, cc_type (608, field 1)
(caption.ccData & 0xff00) >> 8, // cc_data_1
caption.ccData & 0xff, // cc_data_2 without parity bit set
0xff // marker_bits
])
};
};
// Create SEI nal-units from Caption packets
var makeSeiFromMultipleCaptionPackets = function(captionHash) {
var pts = captionHash.pts,
dts = captionHash.dts,
captions = captionHash.captions;
var data = [];
captions.forEach(function(caption) {
data.push(0xfc | caption.type);
data.push((caption.ccData & 0xff00) >> 8);
data.push(caption.ccData & 0xff);
});
return {
pts: pts,
dts: dts,
nalUnitType: 'sei_rbsp',
escapedRBSP: new Uint8Array([
0x04, // payload_type === user_data_registered_itu_t_t35
(0x0b + (captions.length * 3)), // payload_size
181, // itu_t_t35_country_code
0x00, 0x31, // itu_t_t35_provider_code
0x47, 0x41, 0x39, 0x34, // user_identifier, "GA94"
0x03, // user_data_type_code, 0x03 is cc_data
// 110 00001
(0x6 << 5) | captions.length, // process_cc_data, cc_count
0xff // reserved
].concat(data).concat([0xff /* marker bits */])
)
};
};
var makeMdatFromCaptionPackets = function(packets) {
var mdat = ['mdat'];
var seis = packets.map(makeSeiFromCaptionPacket);
seis.forEach(function(sei) {
mdat.push(0x00);
mdat.push(0x00);
mdat.push(0x00);
mdat.push(sei.escapedRBSP.length + 1); // nal length
mdat.push(0x06); // declare nal type as SEI
// SEI message
for (var i = 0; i < sei.escapedRBSP.length; i++) {
var byte = sei.escapedRBSP[i];
mdat.push(byte);
}
});
return box.apply(null, mdat);
};
// Returns a ccData byte-pair for a two character string. That is,
// it converts a string like 'hi' into the two-byte number that
// would be parsed back as 'hi' when provided as ccData.
var characters = function(text) {
if (text.length !== 2) {
throw new Error('ccdata must be specified two characters at a time');
}
return (text.charCodeAt(0) << 8) | text.charCodeAt(1);
};
// Returns a ccData byte-pair including
// Header for 708 packet
// Header for the first service block
// seq should increment by 1 for each byte pair mod 3 (0,1,2,0,1,2,...)
// sizeCode is the number of byte pairs in the packet (including header)
// serviceNum is the service number of the first service block
// blockSize is the size of the first service block in bytes (no header)
// If there's only one service block, the blockSize should be (sizeCode-1)*2
var packetHeader708 = function(seq, sizeCode, serviceNum, blockSize) {
var b1 = (seq << 6) | sizeCode;
var b2 = (serviceNum << 5) | blockSize;
return (b1 << 8) | b2;
};
// Returns a ccData byte-pair to execute a 708 DSW command
// Takes an array of window indicies to display
var displayWindows708 = function(windows) {
var cmd = 0x8900;
windows.forEach(function(winIdx) {
cmd |= (0x01 << winIdx);
});
return cmd;
};
module.exports = {
makeSeiFromCaptionPacket: makeSeiFromCaptionPacket,
makeSeiFromMultipleCaptionPackets: makeSeiFromMultipleCaptionPackets,
makeMdatFromCaptionPackets: makeMdatFromCaptionPackets,
characters: characters,
packetHeader708: packetHeader708,
displayWindows708: displayWindows708
};