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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
|
#define _DEFAULT_SOURCE // can be removed once glibc knows about <endian.h> in POSIX 2024
#include <stdio.h>
#include <stdlib.h>
#include <stdbit.h>
#include <endian.h>
#include <string.h>
#include "util/err.h"
#include "nut.h"
#define DATA_SIZE_MUL 16383
#define STATIC_OUT(buffer) { .type = NUT_OUT_STATIC, .ptr = buffer, .cap = sizeof buffer }
static uint32_t crc32(size_t len, char bytes[len])
{
int32_t crc = 0;
for (size_t i = 0; i < len; i++) {
crc ^= bytes[i] << 24;
for (size_t j = 0; j < 8; j++)
crc = (crc << 1) ^ ((0x04C11DB7) & (crc >> 31));
}
return crc;
}
static void put_f(struct nut_output *out, size_t len, const void *data)
{
if (out->type == NUT_OUT_FILE) {
fwrite(data, 1, len, out->file);
} else {
if (out->type == NUT_OUT_HEAP)
arraybuf_grow(out, len);
else if (out->len + len > out->cap)
eprintf("NUT buffer size exceeded\n");
memcpy(out->ptr+out->len, data, len);
}
out->len += len;
}
static void put_u8(struct nut_output *out, uint8_t v)
{
put_f(out, sizeof v, &v);
}
static void put_u32(struct nut_output *out, uint32_t v)
{
v = htobe32(v);
put_f(out, sizeof v, &v);
}
static void put_u64(struct nut_output *out, uint64_t v)
{
v = htobe64(v);
put_f(out, sizeof v, &v);
}
static void put_v(struct nut_output *out, uint64_t v)
{
unsigned int bits = stdc_bit_width(v);
if (!bits) bits = 1;
for (int i = bits/7; i >= 0; i--)
put_u8(out, ((v >> (i*7)) & 0x7f) | ((!!i)*0x80));
}
static size_t put_packet_header(struct nut_output *out, size_t data_len, uint64_t startcode)
{
char buffer[BUFSIZ];
struct nut_output header = STATIC_OUT(buffer);
put_u64(&header, startcode);
size_t forward_ptr = data_len+sizeof(uint32_t);
put_v(&header, forward_ptr);
if (data_len > 4096)
put_u32(&header, crc32(header.len, header.ptr));
put_f(out, header.len, header.ptr);
return header.len;
}
static void put_packet_data(struct nut_output *out, size_t len, void *data)
{
put_f(out, len, data);
put_u32(out, crc32(len, data));
}
static void put_packet(struct nut_output *out, size_t len, void *data, uint64_t startcode)
{
put_packet_header(out, len, startcode);
put_packet_data(out, len, data);
}
static void put_syncpoint(struct nut_writer *wr)
{
char buffer[BUFSIZ];
size_t pos = wr->out.len;
size_t offset = pos - wr->last_syncpoint;
if (wr->syncp_offs.len > 0 && wr->syncp_offs.ptr[wr->syncp_offs.len-1].off == offset)
wr->syncp_offs.ptr[wr->syncp_offs.len-1].num++;
else
arraybuf_insert(&wr->syncp_offs, ((struct nut_syncp_off) { .off = offset, .num = 1 }));
struct nut_output syncp = STATIC_OUT(buffer);
put_v(&syncp, wr->n_frame++); // timestamp
put_v(&syncp, wr->last_syncpoint ? offset / 16 : 0); // back_ptr
put_packet(&wr->out, syncp.len, syncp.ptr,
0xE4ADEECA4569ULL + (((uint64_t)('N'<<8) + 'K')<<48));
wr->last_syncpoint = pos;
}
void nut_write_start(struct nut_writer *wr, FILE *f, unsigned int fps, unsigned int size[2])
{
char buffer[BUFSIZ];
wr->out.type = NUT_OUT_FILE;
wr->out.file = f;
wr->out.len = 0;
wr->frame_size = size[0] * size[1] * 4;
wr->last_syncpoint = 0; // ???
wr->n_frame = 0;
wr->syncp_offs.len = 0;
wr->syncp_offs.cap = 4;
wr->syncp_offs.ptr = nullptr;
const char id_string[] = "nut/multimedia container";
put_f(&wr->out, sizeof id_string, id_string);
// main header
struct nut_output main = STATIC_OUT(buffer);
put_v(&main, 3); // version
put_v(&main, 1); // stream count
put_v(&main, 65536); // max_distance
put_v(&main, 1); // time_base_count
put_v(&main, 1); // time_base_denom
put_v(&main, fps); // time_base_num
// frame code 0
put_v(&main, 1 << 13); // FLAG_INVALID
put_v(&main, 0); // fields
// frame code 1
put_v(&main, (1 << 0) | (1 << 6) | (1 << 5)); // FLAG_KEY | FLAG_CHECKSUM | FLAG_SIZE_MSB
put_v(&main, 6); // fields
put_v(&main, 0); // pts
put_v(&main, DATA_SIZE_MUL); // mul
put_v(&main, 0); // stream
put_v(&main, wr->frame_size % DATA_SIZE_MUL); // size
put_v(&main, 0); // res
put_v(&main, 1); // count
// frame code 2
put_v(&main, (1 << 0) | (1 << 1)); // FLAG_KEY | FLAG_EOR
put_v(&main, 2); // fields
put_v(&main, 0); // pts
put_v(&main, 1); // mul
// other frame codes
put_v(&main, 1 << 13); // FLAG_INVALID
put_v(&main, 6); // fields
for (size_t i = 0; i < 5; i++)
put_v(&main, 0);
put_v(&main, 256-4); // count
put_v(&main, 0); // header_count_minus1
put_v(&main, 0); // main_flags
put_packet(&wr->out, main.len, main.ptr,
0x7A561F5F04ADULL + (((uint64_t)('N'<<8) + 'M')<<48));
// stream header
struct nut_output stream = STATIC_OUT(buffer);
put_v(&stream, 0); // stream id
put_v(&stream, 0); // stream class: video
const char fourcc[] = "RGBA";
put_v(&stream, sizeof fourcc-1);
put_f(&stream, sizeof fourcc-1, fourcc);
put_v(&stream, 0); // time base id
put_v(&stream, 0); // msb_pts_shift
put_v(&stream, 0); // max_pts_distance
put_v(&stream, 0); // decode_delay
put_v(&stream, 1); // stream flags: FLAG_FIXED_FPS
put_v(&stream, 0); // length of codec_specific_data
// video data
put_v(&stream, size[0]); // width
put_v(&stream, size[1]); // height
put_v(&stream, 1); // sample_width
put_v(&stream, 1); // sample_height
put_v(&stream, 0); // colorspace_type: unknown (?)
put_packet(&wr->out, stream.len, stream.ptr,
0x11405BF2F9DBULL + (((uint64_t)('N'<<8) + 'S')<<48));
}
void nut_write_frame(struct nut_writer *wr, void *data)
{
char buffer[BUFSIZ];
put_syncpoint(wr);
struct nut_output frame = STATIC_OUT(buffer);
put_u8(&frame, 1); // frame code
put_v(&frame, wr->frame_size / DATA_SIZE_MUL); // size msb
put_f(&wr->out, frame.len, frame.ptr);
put_u32(&wr->out, crc32(frame.len, frame.ptr));
put_f(&wr->out, wr->frame_size, data);
}
void nut_write_end(struct nut_writer *wr)
{
put_syncpoint(wr);
put_u8(&wr->out, 2); // frame code
struct nut_output index = { .type = NUT_OUT_HEAP };
put_v(&index, wr->n_frame-1); // max_pts
put_v(&index, wr->n_frame); // syncpoints
size_t syncp_pos = 0;
for (size_t i = 0; i < wr->syncp_offs.len; i++)
for (size_t n = 0; n < wr->syncp_offs.ptr[i].num; n++) {
size_t last = syncp_pos;
syncp_pos += wr->syncp_offs.ptr[i].off;
put_v(&index, syncp_pos / 16 - last / 16);
}
free(wr->syncp_offs.ptr);
// first syncp: does not have keyframe before it: type=0 f0=0 term=1
put_v(&index, 0b100);
// other syncps: all have keyframes type=1 flag=1 n=(n_frame-1)
put_v(&index, ((wr->n_frame-1)<<2) | 0b11);
for (size_t i = 1; i < wr->n_frame; i++) {
if (i == wr->n_frame-1) {
put_v(&index, 0); // indicate eor
put_v(&index, 1); // A
put_v(&index, 1); // B
} else
put_v(&index, 1); // A
}
size_t header_len = put_packet_header(&wr->out, index.len+sizeof(uint64_t),
0xDD672F23E64EULL + (((uint64_t)('N'<<8) + 'X')<<48));
put_u64(&index, header_len+index.len+sizeof(uint64_t)+sizeof(uint32_t)); // index_ptr
put_packet_data(&wr->out, index.len, index.ptr);
free(index.ptr);
}
|