#include #include #include #include /* for perror() */ #include /* for memcpy() */ #include /* for realloc(), free() */ #include /* for write() */ #define LM_ARRAY_LEN(ary) (sizeof(ary)/sizeof((ary)[0])) #define LM_CEILDIV(n, d) ( ((n)+(d)-1) / (d) ) #define LM_NEXT_POWER_OF_2(x) ( (x) ? 1ULL<<((sizeof(unsigned long long)*8)-__builtin_clzll(x)) : 1) #define LM_CAT2(a, b) a ## b #define LM_CAT2_(a, b) LM_CAT2(a, b) #define assert_notreached(msg) assert(false) struct buf { uint8_t *dat; size_t len; size_t cap; }; void append(struct buf *buf, void *dat, size_t len) { assert(buf); assert(len == 0 || dat); if (buf->len + len > buf->cap) { buf->cap = LM_NEXT_POWER_OF_2(buf->len + len); buf->dat = realloc(buf->dat, buf->cap); assert(buf->dat); } memcpy(&buf->dat[buf->len], dat, len); buf->len += len; } void append_u32be(struct buf *buf, uint32_t val) { assert(buf); uint8_t dat[4] = { (val>>(8*3))&0xFF, (val>>(8*2))&0xFF, (val>>(8*1))&0xFF, (val>>(8*0))&0xFF, }; append(buf, dat, sizeof(dat)); } bool xwrite(int fd, uint8_t *dat, size_t len) { assert(len == 0 || dat); for (size_t done = 0; done < len;) { ssize_t n = write(fd, &dat[done], len-done); if (n < 0) { perror("write"); return true; } done += n; } return false; } /* NUT ************************************************************************/ /* Magic values. *****************************************/ uint8_t nut_file_id_string[] = "nut/multimedia container"; /* including NUL terminator */ uint8_t nut_startcode_main[] = {'N', 'M', 0x7A, 0x56, 0x1F, 0x5F, 0x04, 0xAD }; uint8_t nut_startcode_stream[] = {'N', 'S', 0x11, 0x40, 0x5B, 0xF2, 0xF9, 0xDB }; uint8_t nut_startcode_syncpoint[] = {'N', 'K', 0xE4, 0xAD, 0xEE, 0xCA, 0x45, 0x69 }; uint8_t nut_startcode_index[] = {'N', 'X', 0xDD, 0x67, 0x2F, 0x23, 0xE6, 0x4E }; uint8_t nut_startcode_info[] = {'N', 'I', 0xAB, 0x68, 0xB5, 0x96, 0xBA, 0x78 }; #define NUT_FRAMEFLAG_KEY (1<< 0) #define NUT_FRAMEFLAG_EOR (1<< 1) #define _NUT_FRAMEFLAG_2 (1<< 2) #define NUT_FRAMEFLAG_CODED_PTS (1<< 3) #define NUT_FRAMEFLAG_STREAM_ID (1<< 4) #define NUT_FRAMEFLAG_SIZE_MSB (1<< 5) #define NUT_FRAMEFLAG_CHECKSUM (1<< 6) #define NUT_FRAMEFLAG_RESERVED (1<< 7) #define NUT_FRAMEFLAG_SM_DATA (1<< 8) #define _NUT_FRAMEFLAG_9 (1<< 9) #define NUT_FRAMEFLAG_HEADER_IDX (1<<10) #define NUT_FRAMEFLAG_MATCH_TIME (1<<11) #define NUT_FRAMEFLAG_CODED (1<<12) #define NUT_FRAMEFLAG_INVALID (1<<13) #define NUT_MAINFLAG_BROADCAST_MODE (1<< 0) #define NUT_MAINFLAG_PIPE_MODE (1<< 1) #define NUT_UNKNOWN_MATCH_TIME (1-(UINT64_C(1)<<62)) enum nut_stream_class { NUT_STREAMCLASS_VIDEO = 0, NUT_STREAMCLASS_AUDIO = 1, NUT_STREAMCLASS_SUBTITLES = 2, NUT_STREAMCLASS_USERDATA = 3, }; #define NUT_STREAMFLAG_FIXED_FPS (1<< 1) enum nut_colorspace { NUT_COLORSPACE_UNKNOWN = 0, NUT_COLORSPACE_ITU642_TRUNC = 1, NUT_COLORSPACE_ITU709_TRUNC = 2, NUT_COLORSPACE_ITU642_FULL = 17, NUT_COLORSPACE_ITU709_FILL = 18, }; /* Basic fixed-length types. *****************************/ void nut_append_u8(struct buf *buf, uint8_t val) { assert(buf); append(buf, &val, 1); } void nut_append_u32(struct buf *buf, uint32_t val) { assert(buf); append_u32be(buf, val); } /* Basic variable-length types. **************************/ void nut_append_vu(struct buf *buf, uint64_t val) { assert(buf); uint8_t dat[10] = { 0x80|((val>>(7*9))&0x7F), 0x80|((val>>(7*8))&0x7F), 0x80|((val>>(7*7))&0x7F), 0x80|((val>>(7*6))&0x7F), 0x80|((val>>(7*5))&0x7F), 0x80|((val>>(7*4))&0x7F), 0x80|((val>>(7*3))&0x7F), 0x80|((val>>(7*2))&0x7F), 0x80|((val>>(7*1))&0x7F), 0x00|((val>>(7*0))&0x7F), }; uint8_t skip = 0; while (dat[skip] == 0x80) skip++; append(buf, &dat[skip], sizeof(dat)-skip); } void nut_append_vs(struct buf *buf, int64_t val) { assert(buf); assert((val < 0 ? -val : val) <= UINT64_MAX>>1); uint64_t temp; if (val > 0) temp = val << 1; else temp = ((-val) << 1) | 1; temp--; nut_append_vu(buf, temp); } void nut_append_vb(struct buf *buf, void *dat, size_t len) { assert(buf); assert(len == 0 || dat); nut_append_vu(buf, len); append(buf, dat, len); } #define nut_append_vb_str(buf, str) nut_append_vb(buf, str, sizeof(str)-1) /* Mid-level. ********************************************/ uint32_t nut_crc32(uint8_t *dat, int len){ assert(len == 0 || dat); /* "Generator polynomial is 0x104C11DB7." */ static const uint32_t table[16] = { 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005, 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61, 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD, }; /* "Starting value is zero." */ uint32_t crc = 0; while (len--) { crc ^= *dat++ << 24; crc = (crc<<4) ^ table[crc>>28]; crc = (crc<<4) ^ table[crc>>28]; } return crc; } void nut_append_packet(struct buf *buf, uint8_t *startcode, void *data, size_t len) { assert(buf); assert(startcode); assert(len == 0 || data); size_t packet_beg = buf->len; append(buf, startcode, 8); nut_append_vu(buf, len+4); if (len+4 > 4096) nut_append_u32(buf, nut_crc32(&buf->dat[packet_beg], buf->len - packet_beg)); size_t data_beg = buf->len; append(buf, data, len); nut_append_u32(buf, nut_crc32(&buf->dat[data_beg], buf->len - data_beg)); } [[gnu::nonstring]] char font[10][8*4] = { " ## " " # # " " # # " " ## ", " # " " ## " " # " " ### ", " ## " " # # " " # " " #### ", " ## " " # " " # " " ## ", " ## " " # # " " #### " " # ", " #### " " ### " " # " " ### ", " # " " ### " " # # " " ## ", " #### " " # " " # " " # ", " ## " " # # " " #### " " ## ", " ## " " # # " " ### " " # ", }; static_assert(LM_ARRAY_LEN(font) == 10); static_assert(sizeof(font[0]) == 32);