diff options
-rw-r--r-- | libmkv/nut-generate.c | 128 |
1 files changed, 58 insertions, 70 deletions
diff --git a/libmkv/nut-generate.c b/libmkv/nut-generate.c index a1270c6..543769b 100644 --- a/libmkv/nut-generate.c +++ b/libmkv/nut-generate.c @@ -88,12 +88,14 @@ uint8_t nut_startcode_info[] = {'N', 'I', 0xAB, 0x68, 0xB5, 0x96, 0xBA, 0x7 #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) @@ -249,10 +251,6 @@ size_t app_res_h(enum app_res res) { size_t app_res_fb_size(enum app_res res) { return app_res_w(res)*app_res_h(res); } -size_t app_res_sm_size(enum app_res) { - /* See app_write_frame() */ - return 19; -} #define APP_FRAME_SIZE_MSB_MUL 0x2000 /* must be less than 0x4000 */ #define APP_FRAME_MAX_HEADER 16 @@ -261,14 +259,7 @@ struct app_state { enum app_res res; }; -#define APP_COMMON_FLAGS \ - NUT_FRAMEFLAG_KEY | /* Because they're full framebuffers, all frames are keyframes. */ \ - NUT_FRAMEFLAG_SIZE_MSB | /* 640*480/2 > 16384 (the max val of data_size_lsb). */ \ - NUT_FRAMEFLAG_CODED_PTS | /* framerate is unknown, each frame must have a timestamp. */ \ - NUT_FRAMEFLAG_CHECKSUM /* framerate is unknown, guard against exceeding max_pts_distance. */ - - -bool app_write_intro(int fd, struct app_state *state, uint64_t time_ns) { +bool app_write_intro(int fd, uint64_t time_ns) { struct buf out = {0}; struct buf pkt = {0}; unsigned int frame_code = 0; @@ -276,13 +267,6 @@ bool app_write_intro(int fd, struct app_state *state, uint64_t time_ns) { append(&out, nut_file_id_string, sizeof(nut_file_id_string)); - /* Changing resolution mid-stream requires per-fame side-data, - * which requires FLAG_SM_DATA, which requires NUT v4. - * - * Unfortunately, libnut / nututils does not support v4, so - * there goes a good debugging tool. - */ - #define BOGUS(n) n /* main_header ********************************************************/ pkt.len = 0; @@ -290,10 +274,9 @@ bool app_write_intro(int fd, struct app_state *state, uint64_t time_ns) { /* head *******************************************/ nut_append_vu(&pkt, 4); /*! version */ nut_append_vu(&pkt, 0); /*! minor_version */ - nut_append_vu(&pkt, 1); /*! stream_count */ + nut_append_vu(&pkt, APP_RES_MAX+1); /*! stream_count */ nut_append_vu(&pkt, /*! max_distance */ APP_FRAME_MAX_HEADER + - app_res_sm_size(APP_RES_MAX) + app_res_fb_size(APP_RES_MAX)); /* time bases *************************************/ @@ -314,27 +297,33 @@ bool app_write_intro(int fd, struct app_state *state, uint64_t time_ns) { INC_FRAME_CODE(); for (enum app_res res = APP_RES_MIN; res <= APP_RES_MAX; res++) { - /* frame_code=(res*2)+1 (at `res`) */ - nut_append_vu(&pkt, APP_COMMON_FLAGS); /*! flags */ + /* frame_code=(res*2)+1 (`res` keyframe) */ + nut_append_vu(&pkt, /*! flags */ + NUT_FRAMEFLAG_KEY | /* Because they're full framebuffers, all frames are keyframes. */ + NUT_FRAMEFLAG_SIZE_MSB | /* 640*480/2 > 16384 (the max val of data_size_lsb). */ + NUT_FRAMEFLAG_CODED_PTS | /* framerate is unknown, each frame must have a timestamp. */ + NUT_FRAMEFLAG_CHECKSUM ); /* framerate is unknown, guard against exceeding max_pts_distance. */ nut_append_vu(&pkt, 6); /*! field_count */ nut_append_vu(&pkt, BOGUS(0)); /*! 0: fields.pts */ nut_append_vu(&pkt, APP_FRAME_SIZE_MSB_MUL); /*! 1: fields.size_msb_nul */ - nut_append_vu(&pkt, 0); /*! 2: fields.stream */ + nut_append_vu(&pkt, res); /*! 2: fields.stream */ nut_append_vu(&pkt, /*! 3: fields.size_lsb */ app_res_fb_size(res) % APP_FRAME_SIZE_MSB_MUL); nut_append_vu(&pkt, 0); /*! 4: fields.reserved */ nut_append_vu(&pkt, 1); /*! 5: fields.count */ INC_FRAME_CODE(); - /* frame_code=(res*2)+2 (change to `res`) */ - nut_append_vu(&pkt, APP_COMMON_FLAGS| /*! flags */ - NUT_FRAMEFLAG_SM_DATA); + /* frame_code=(res*2)+2 (`res` EOR frame) */ + nut_append_vu(&pkt, /*! flags */ + NUT_FRAMEFLAG_KEY | /* Because they're full framebuffers, all frames are keyframes. */ + NUT_FRAMEFLAG_EOR | + NUT_FRAMEFLAG_CODED_PTS | /* framerate is unknown, each frame must have a timestamp. */ + NUT_FRAMEFLAG_CHECKSUM ); /* framerate is unknown, guard against exceeding max_pts_distance. */ nut_append_vu(&pkt, 6); /*! field_count */ nut_append_vu(&pkt, BOGUS(0)); /*! 0: fields.pts */ - nut_append_vu(&pkt, APP_FRAME_SIZE_MSB_MUL); /*! 1: fields.size_msb_nul */ - nut_append_vu(&pkt, 0); /*! 2: fields.stream */ - nut_append_vu(&pkt, /*! 3: fields.size_lsb */ - (app_res_sm_size(res) + app_res_fb_size(res)) % APP_FRAME_SIZE_MSB_MUL); + nut_append_vu(&pkt, BOGUS(1)); /*! 1: fields.size_msb_nul */ + nut_append_vu(&pkt, res); /*! 2: fields.stream */ + nut_append_vu(&pkt, 0); /*! 3: fields.size_lsb */ nut_append_vu(&pkt, 0); /*! 4: fields.reserved */ nut_append_vu(&pkt, 1); /*! 5: fields.count */ INC_FRAME_CODE(); @@ -354,25 +343,26 @@ bool app_write_intro(int fd, struct app_state *state, uint64_t time_ns) { nut_append_packet(&out, nut_startcode_main, pkt.dat, pkt.len); /* stream_header ******************************************************/ - 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(0)); /*! msb_pts_shift (only relevant if FRAMEFLAG_CODED_PTS) */ - nut_append_vu(&pkt, BOGUS(0)); /*! 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 */ - state->res = APP_RES_640_480; - nut_append_vu(&pkt, app_res_w(state->res)); /*! width */ - nut_append_vu(&pkt, app_res_h(state->res)); /*! 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); + for (enum app_res res = APP_RES_MIN; res <= APP_RES_MAX; res++) { + pkt.len = 0; + + nut_append_vu(&pkt, res); /*! 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(0)); /*! msb_pts_shift (only relevant if FRAMEFLAG_CODED_PTS) */ + nut_append_vu(&pkt, BOGUS(0)); /*! 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, app_res_w(res)); /*! width */ + nut_append_vu(&pkt, app_res_h(res)); /*! 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); + } /* syncpoint **********************************************************/ pkt.len = 0; @@ -397,26 +387,21 @@ bool app_write_frame(int fd, struct app_state *state, uint64_t time_ns, enum app struct buf out = {0}; struct buf pkt = {0}; + if (res != state->res) { + /* EOR frame ( 1+10+4 = 15 bytes) */ + uint64_t frame_beg = out.len; + nut_append_u8(&out, (2*(state->res))+2); /*! frame_code (1 byte) */ + nut_append_vu(&out, time_ns); /*! coded_pts (<=10 bytes) */ + nut_append_u32(&out, nut_crc32(&out.dat[frame_beg], out.len - frame_beg)); /*! checksum (4 bytes) */ + } + /* frame header ( 1+10+1+4 = 16 bytes) */ uint64_t frame_beg = out.len; - nut_append_u8(&out, 1+(2*res)+(res != state->res ? 1 : 0)); /*! frame_code (1 byte) */ + nut_append_u8(&out, (2*res)+1); /*! frame_code (1 byte) */ nut_append_vu(&out, time_ns); /*! coded_pts (<=10 bytes) */ - nut_append_vu(&out, /*! data_size_msb (1 byte) */ - (app_res_fb_size(res) + (res != state->res ? app_res_sm_size(res) : 0)) / APP_FRAME_SIZE_MSB_MUL); + nut_append_vu(&out, app_res_fb_size(res) / APP_FRAME_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) */ - assert(out.len <= APP_FRAME_MAX_HEADER); - - /* side/meta data ( 1+(6+2)+(7+2)+1 = 19 bytes) */ - if (res != state->res) { - uint64_t data_beg = out.len; - nut_append_vu(&out, 2); /*! side_data_count (1 byte) */ - nut_append_vb_str(&out, "Width"); /*! side_data[0].name (1+5 bytes) */ - nut_append_vu(&out, app_res_w(res)); /*! side_data[0].value (2 bytes) */ - nut_append_vb_str(&out, "Height"); /*! side_data[1].name (1+6 bytes) */ - nut_append_vu(&out, app_res_h(res)); /*! side_data[1].value (2 bytes) */ - nut_append_vu(&out, 0); /*! meta_data_count (1 byte) */ - assert(out.len - data_beg == app_res_sm_size(res)); - } + assert(out.len - frame_beg <= APP_FRAME_MAX_HEADER); /* flush */ bool err = xwrite(fd, out.dat, out.len); @@ -491,10 +476,13 @@ static_assert(LM_ARRAY_LEN(font) == 10); static_assert(sizeof(font[0]) == 32); int main() { - struct app_state state; - if (app_write_intro(1, &state, 0)) + if (app_write_intro(1, 0)) return 1; + struct app_state state = { + .res = 0, + }; + uint8_t framebuffer[app_res_fb_size(APP_RES_MAX)]; #define SCALE 10 @@ -502,8 +490,8 @@ int main() { memset(framebuffer, 0, sizeof(framebuffer)); for (int y = 0; y < 4*SCALE; y++) for (int x = 0; x < 8*SCALE; x++) - framebuffer[(y*app_res_w(i%3))+x] = font[i][((y/SCALE)*8)+(x/SCALE)] == ' ' ? 0b11000000 : 0b00000011; - if (app_write_frame(1, &state, ((uint64_t)i)*1000000000ULL, i%3, framebuffer)) + framebuffer[(y*app_res_w(i%(APP_RES_MAX+1)))+x] = font[i][((y/SCALE)*8)+(x/SCALE)] == ' ' ? 0b11000000 : 0b00000011; + if (app_write_frame(1, &state, ((uint64_t)i)*1000000000ULL, i%(APP_RES_MAX+1), framebuffer)) return 1; } return 0; |