diff options
Diffstat (limited to 'vid-scratch/nutgen-elision.c')
-rw-r--r-- | vid-scratch/nutgen-elision.c | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/vid-scratch/nutgen-elision.c b/vid-scratch/nutgen-elision.c new file mode 100644 index 0000000..6dd734f --- /dev/null +++ b/vid-scratch/nutgen-elision.c @@ -0,0 +1,278 @@ +/* nut.c - A simple NUT encoder with the following properties: + * + * - Each frame is an uncompressed 8-bit RGB (specifically `(msb)2B + * 3G 3R(lsb)`, known as the 4CC "BGR\x08") framebuffer. + * + * - Each pixel in the framebuffer is non-square; it is twice as tall + * as it is wide. + * + * - The display-resolution of each frame may change between 640x480, + * 720x480, and 720x576. This is the display-resolution; because + * of non-square pixels, the framebuffer for each would be 640x240, + * 720x240, and 720x288, respectively. + * + * - VFR (Variable Frame Rate) - Each frame has its own timestamp. + */ + +#include "common.h" + +/* High-level (specific to the properties we want) ****************************/ + +enum app_res { + APP_RES_640_480 = 0, + APP_RES_720_480 = 1, + APP_RES_720_576 = 2, +}; + +struct app_state { + uint64_t pts; + uint64_t len; + uint64_t last_syncpoint; +}; + +#define APP_COMMON_FLAGS NUT_FRAMEFLAG_KEY|NUT_FRAMEFLAG_SIZE_MSB|NUT_FRAMEFLAG_CHECKSUM|NUT_FRAMEFLAG_SM_DATA + +#define APP_SIZE_MSB_MUL 0x2000 /* must be less than 0x4000 */ + +#define _APP_ELIDE_640_480 19 +#define _APP_ELIDE_720_480 19 +#define _APP_ELIDE_720_576 19 +#define _APP_W_640_480 640 +#define _APP_W_720_480 720 +#define _APP_W_720_576 720 +#define _APP_H_640_480 480 +#define _APP_H_720_480 480 +#define _APP_H_720_576 576 + +#define APP_ELIDE(res) LM_CAT2_(_APP_ELIDE_, res) +#define APP_W(res) LM_CAT2_(_APP_W_, res) +#define APP_H(res) LM_CAT2_(_APP_H_, res) +#define APP_FB(res) (APP_W(res)*APP_H(res)/2) +#define APP_SIZE(res) (APP_ELIDE(res)+APP_FB(res)) +/* HERE */ +#define APP_FRAME_MAX_OVERHEAD 31 + +bool app_write_intro(int fd, struct app_state *state, uint64_t time_ns) { + assert(state); + + struct buf out = {0}; + struct buf pkt = {0}; + struct buf hdr = {0}; + + append(&out, nut_file_id_string, sizeof(nut_file_id_string)); + + /* main_header ********************************************************/ +#define BOGUS 0 + pkt.len = 0; + + nut_append_vu(&pkt, 3); /*! version */ + nut_append_vu(&pkt, 1); /*! stream_count */ + nut_append_vu(&pkt, APP_FB(720_576) + /*! max_distance */ + APP_FRAME_MAX_OVERHEAD); + /* time bases *************************************/ + nut_append_vu(&pkt, 1); /*! time_base_count */ + + /* time_base[0] = 1ns */ + nut_append_vu(&pkt, 1); /*! numerator */ + nut_append_vu(&pkt, 1000000000ULL); /*! denominator */ + + /* frame codes ************************************/ + /* "A muxer SHOULD mark [frame codes] 0x00 and 0xFF as invalid" */ + + /* frame_code=0 (invalid) */ + nut_append_vu(&pkt, NUT_FRAMEFLAG_INVALID); /*! flags */ + nut_append_vu(&pkt, 2); /*! field_count */ + nut_append_vu(&pkt, BOGUS); /*! 0: fields.pts */ + nut_append_vu(&pkt, 1); /*! 1: fields.size_msb_nul */ + + /* frame_code=1 (640x480) */ + nut_append_vu(&pkt, APP_COMMON_FLAGS); /*! flags */ + nut_append_vu(&pkt, 8); /*! field_count */ + nut_append_vu(&pkt, BOGUS); /*! 0: fields.pts */ + nut_append_vu(&pkt, APP_SIZE_MSB_MUL); /*! 1: fields.size_msb_nul */ + nut_append_vu(&pkt, 0); /*! 2: fields.stream */ + nut_append_vu(&pkt, APP_SIZE(640_480) % /*! 3: fields.size_lsb */ + APP_SIZE_MSB_MUL); + nut_append_vu(&pkt, 0); /*! 4: fields.reserved */ + nut_append_vu(&pkt, 1); /*! 5: fields.count */ + nut_append_vs(&pkt, NUT_UNKNOWN_MATCH_TIME); /*! 6: fields.match */ + nut_append_vu(&pkt, 1); /*! 7: fields.elision_header */ + + /* frame_code=2 (720x480) */ + nut_append_vu(&pkt, APP_COMMON_FLAGS); /*! flags */ + nut_append_vu(&pkt, 8); /*! field_count */ + nut_append_vu(&pkt, BOGUS); /*! 0: fields.pts */ + nut_append_vu(&pkt, APP_SIZE_MSB_MUL); /*! 1: fields.size_msb_mul */ + nut_append_vu(&pkt, 0); /*! 2: fields.stream */ + nut_append_vu(&pkt, APP_SIZE(720_480) % /*! 3: fields.size_lsb */ + APP_SIZE_MSB_MUL); + nut_append_vu(&pkt, 0); /*! 4: fields.reserved */ + nut_append_vu(&pkt, 1); /*! 5: fields.count */ + nut_append_vs(&pkt, NUT_UNKNOWN_MATCH_TIME); /*! 6: fields.match */ + nut_append_vu(&pkt, 2); /*! 7: fields.elision_header */ + + /* frame_code=3 (720x576) */ + nut_append_vu(&pkt, APP_COMMON_FLAGS); /*! flags */ + nut_append_vu(&pkt, 8); /*! field_count */ + nut_append_vu(&pkt, BOGUS); /*! 0: fields.pts */ + nut_append_vu(&pkt, APP_SIZE_MSB_MUL); /*! 1: fields.size_msb_nul */ + nut_append_vu(&pkt, 0); /*! 2: fields.stream */ + nut_append_vu(&pkt, APP_SIZE(720_576) % /*! 3: fields.size_lsb */ + APP_SIZE_MSB_MUL); + nut_append_vu(&pkt, 0); /*! 4: fields.reserved */ + nut_append_vu(&pkt, 1); /*! 5: fields.count */ + nut_append_vs(&pkt, NUT_UNKNOWN_MATCH_TIME); /*! 6: fields.match */ + nut_append_vu(&pkt, 2); /*! 7: fields.elision_header */ + + /* frame_code=4-255 (invalid) */ + nut_append_vu(&pkt, NUT_FRAMEFLAG_INVALID); /*! flags */ + nut_append_vu(&pkt, 2); /*! field_count */ + nut_append_vu(&pkt, BOGUS); /*! 0: fields.pts */ + nut_append_vu(&pkt, 256-4-1); /*! 1: fields.size_msb_nul */ + + /* elision headers ********************************/ + nut_append_vu(&pkt, 3); /*! header_count_minus_1 */ + + /* elision_header[1] (640x480) */ + hdr.len = 0; + nut_append_vu(&hdr, 2); /*! side_data_count */ + nut_append_vb_str(&hdr, "Width"); /*! side_data[0].name */ + nut_append_vu(&hdr, APP_W(640_480)); /*! side_data[0].value */ + nut_append_vb_str(&hdr, "Height"); /*! side_data[1].name */ + nut_append_vu(&hdr, APP_H(640_480)/2); /*! side_data[1].value */ + nut_append_vu(&hdr, 0); /*! meta_data_count */ + assert(hdr.len == APP_ELIDE(640_480)); + nut_append_vb(&pkt, hdr.dat, hdr.len); + + /* elision_header[2] (720x480) */ + hdr.len = 0; + nut_append_vu(&hdr, 2); /*! side_data_count */ + nut_append_vb_str(&hdr, "Width"); /*! side_data[0].name */ + nut_append_vu(&hdr, APP_W(720_480)); /*! side_data[0].value */ + nut_append_vb_str(&hdr, "Height"); /*! side_data[1].name */ + nut_append_vu(&hdr, APP_H(720_480)/2); /*! side_data[1].value */ + nut_append_vu(&hdr, 0); /*! meta_Data_count */ + assert(hdr.len == APP_ELIDE(720_480)); + nut_append_vb(&pkt, hdr.dat, hdr.len); + + /* elision_header[3] (720x576) */ + hdr.len = 0; + nut_append_vu(&hdr, 2); /*! side_data_count */ + nut_append_vb_str(&hdr, "Width"); /*! side_data[0].name */ + nut_append_vu(&hdr, APP_H(720_576)); /*! side_data[0].value */ + nut_append_vb_str(&hdr, "Height"); /*! side_data[1].name */ + nut_append_vu(&hdr, APP_H(720_576)/2); /*! side_data[1].value */ + nut_append_vu(&hdr, 0); /*! meta_data_count */ + assert(hdr.len == APP_ELIDE(720_576)); + nut_append_vb(&pkt, hdr.dat, hdr.len); + + nut_append_packet(&out, nut_startcode_main, pkt.dat, pkt.len); +#undef BOGUS + + /* stream_header ******************************************************/ +#define BOGUS 1 + pkt.len = 0; + + nut_append_vu(&pkt, 0); /*! stream_id */ + nut_append_vu(&pkt, NUT_STREAMCLASS_VIDEO); /*! stream_class */ + nut_append_vb(&pkt, "BGR\x08", 4); /*! fourcc */ + nut_append_vu(&pkt, 0); /*! time_base_id */ + nut_append_vu(&pkt, BOGUS); /*! msb_pts_shift (only relevant if FRAMEFLAG_CODED_PTS) */ + nut_append_vu(&pkt, BOGUS); /*! max_pts_distance (all frames have a checksum) */ + nut_append_vu(&pkt, 0); /*! decode_delay */ + nut_append_vu(&pkt, 0); /*! stream_flags */ + nut_append_vb(&pkt, NULL, 0); /*! codec_specific_data */ + nut_append_vu(&pkt, BOGUS); /*! width (all frames set width) */ + nut_append_vu(&pkt, BOGUS); /*! height (all frames set height) */ + nut_append_vu(&pkt, 1); /*! sample_width */ + nut_append_vu(&pkt, 2); /*! sample_height */ + nut_append_vu(&pkt, NUT_COLORSPACE_UNKNOWN); /*! colorspace_type */ + + nut_append_packet(&out, nut_startcode_stream, pkt.dat, pkt.len); +#undef BOGUS + + /* flush **************************************************************/ + + bool ret = xwrite(fd, out.dat, out.len); + free(out.dat); + free(pkt.dat); + state->pts = time_ns; + state->len = out.len; + state->last_syncpoint = 0; + return ret; +} + +bool app_write_frame(int fd, struct app_state *state, uint64_t time_ns, enum app_res res, void *framebuffer) { + assert(state); + assert(framebuffer); + + struct buf out = {0}; + struct buf pkt = {0}; + uint64_t beg = state->len; + + size_t full_size, fb_size; + switch (res) { + case APP_RES_640_480: full_size = APP_SIZE(640_480); fb_size = APP_FB(640_480); break; + case APP_RES_720_480: full_size = APP_SIZE(720_480); fb_size = APP_FB(720_480); break; + case APP_RES_720_576: full_size = APP_SIZE(720_576); fb_size = APP_FB(720_576); break; + } + + /* packet (syncpoint) ( (8+1)+ (10+2) + 4 = 25 bytes) */ + nut_append_vu(&pkt, time_ns); /*! global_key_pts (<=10 bytes) */ + nut_append_vu(&pkt, state->last_syncpoint ? (beg - state->last_syncpoint)/16 : 0); /*! back_ptr_div16 (<=2 bytes) */ + nut_append_packet(&out, nut_startcode_syncpoint, pkt.dat, pkt.len); + + /* frame header ( 1+1+4 = 6 bytes) */ + uint64_t frame_beg = out.len; + nut_append_u8(&out, 1+res); /*! frame_code (4 byte) */ + nut_append_vu(&out, full_size/APP_SIZE_MSB_MUL); /*! data_size_msb (1 byte) */ + nut_append_u32(&out, nut_crc32(&out.dat[frame_beg], out.len - frame_beg)); /*! checksum (4 bytes) */ + /* side_data and meta_data come from elision headers */ + + /* flush */ + assert(out.len <= APP_FRAME_MAX_OVERHEAD); + bool err = xwrite(fd, out.dat, out.len); + free(out.dat); + free(pkt.dat); + if (err) + return true; + state->len += out.len; + + /* frame data */ + if (xwrite(fd, framebuffer, fb_size)) + return true; + state->len += fb_size; + + /* return */ + state->pts = time_ns; + state->last_syncpoint = beg; + return false; +} + +/* Demo application ***********************************************************/ + +int main() { + struct app_state state; + if (app_write_intro(1, &state, 0)) + return 1; + + uint8_t framebuffer[APP_FB(720_576)]; + +#define SCALE 10 + for (int i = 0; i < 10; i++) { + memset(framebuffer, 0, sizeof(framebuffer)); + for (int y = 0; y < 4*SCALE; y++) { + int width; + switch (i%3) { + case APP_RES_640_480: width = APP_W(640_480); break; + case APP_RES_720_480: width = APP_W(720_480); break; + case APP_RES_720_576: width = APP_W(720_576); break; + } + for (int x = 0; x < 8*SCALE; x++) + framebuffer[(y*width)+x] = font[i][((y/SCALE)*8)+(x/SCALE)] == ' ' ? 0b11000000 : 0b00000011; + } + if (app_write_frame(1, &state, ((uint64_t)i)*((uint64_t)i)*1000000000ULL, i%3, framebuffer)) + return 1; + } + return 0; +} |