From e5af28536bfb0f4c9131df56d2009ba5196f5e3a Mon Sep 17 00:00:00 2001 From: Lizzy Fleckenstein Date: Sun, 12 Apr 2026 20:57:06 +0200 Subject: init --- src/fmt/nut.c | 308 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 308 insertions(+) create mode 100644 src/fmt/nut.c (limited to 'src/fmt/nut.c') diff --git a/src/fmt/nut.c b/src/fmt/nut.c new file mode 100644 index 0000000..99f9a69 --- /dev/null +++ b/src/fmt/nut.c @@ -0,0 +1,308 @@ +#include +#include +#include +#include +#include +#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); +} + +/* +#include +#include +#include + +int main() +{ + unsigned int fps = 20; + unsigned int size[2] = { 200, 200 }; + + //FILE *f = fopen("test.nut", "wb"); + //if (!f) { + // fprintf(stderr, "failed to open output\n"); + // return EXIT_FAILURE; + //} + + struct nut_writer writer; + nut_write_start(&writer, stdout, fps, size); + + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + + size_t n_pixels = size[0] * size[1]; + size_t frame_size = n_pixels * 4; + void *frame_buf = malloc(frame_size); + + for (unsigned int stage = 0;; stage++) { + uint32_t color = htole32((0xff << 24) | (0xff << ((stage%3)*8))); + for (size_t i = 0; i < n_pixels; i++) + ((uint32_t *)frame_buf)[i] = color; + for (unsigned int frame = 0; frame < fps; frame++) { + struct timespec ts_now; + clock_gettime(CLOCK_REALTIME, &ts_now); + + double e_time = (double) (ts_now.tv_nsec - ts.tv_nsec) / 1000000000.0 + + (ts_now.tv_sec - ts.tv_sec); + double p_time = stage + (double) frame / fps; + double ahead = p_time - e_time - 5.0; + if (ahead > 0.0) + usleep(ahead * 1000000.0); + + nut_write_frame(&writer, frame_buf); + } + } + free(frame_buf); + + nut_write_end(&writer); + + fflush(stdout); + + //fclose(f); +}*/ -- cgit v1.2.3