diff options
-rw-r--r-- | libmkv/Makefile | 4 | ||||
-rw-r--r-- | libmkv/nut-3.c | 273 |
2 files changed, 140 insertions, 137 deletions
diff --git a/libmkv/Makefile b/libmkv/Makefile index 3c82486..37b6944 100644 --- a/libmkv/Makefile +++ b/libmkv/Makefile @@ -1,6 +1,6 @@ CFLAGS = -Wall -Werror -Wextra -all: $(patsubst %.c,%,$(filter-out mkv.c,$(wildcard *.c))) +all: $(patsubst %.c,%,$(wildcard *.c)) clean: - rm -f -- $(patsubst %.c,%,$(filter-out mkv.c,$(wildcard *.c))) + rm -f -- $(patsubst %.c,%,$(wildcard *.c)) rm -f -- *.avi *.mkv diff --git a/libmkv/nut-3.c b/libmkv/nut-3.c index 2500af5..a1270c6 100644 --- a/libmkv/nut-3.c +++ b/libmkv/nut-3.c @@ -29,6 +29,7 @@ #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; @@ -98,6 +99,9 @@ uint8_t nut_startcode_info[] = {'N', 'I', 0xAB, 0x68, 0xB5, 0x96, 0xBA, 0x7 #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 { @@ -223,37 +227,52 @@ enum app_res { APP_RES_720_480 = 1, APP_RES_720_576 = 2, }; +#define APP_RES_MIN APP_RES_640_480 +#define APP_RES_MAX APP_RES_720_576 -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 +size_t app_res_w(enum app_res res) { + switch (res) { + case APP_RES_640_480: return 640; + case APP_RES_720_480: return 720; + case APP_RES_720_576: return 720; + default: assert_notreached("invalid res"); + } +} +size_t app_res_h(enum app_res res) { + switch (res) { + case APP_RES_640_480: return 480/2; + case APP_RES_720_480: return 480/2; + case APP_RES_720_576: return 576/2; + default: assert_notreached("invalid 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 -#define APP_SM_SIZE 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 +struct app_state { + enum app_res res; +}; -#define APP_W(res) LM_CAT2_(_APP_W_, res) -#define APP_H(res) LM_CAT2_(_APP_H_, res) -#define APP_FB_SIZE(res) (APP_W(res)*APP_H(res)/2) -#define APP_FRAME_SIZE(res) (APP_SM_SIZE+APP_FB_SIZE(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. */ -#define APP_FRAME_MAX_OVERHEAD (31+APP_SM_SIZE) bool app_write_intro(int fd, struct app_state *state, uint64_t time_ns) { - assert(state); - struct buf out = {0}; struct buf pkt = {0}; + unsigned int frame_code = 0; +#define INC_FRAME_CODE() do { frame_code++; if (frame_code == 'N') frame_code++; } while(0) append(&out, nut_file_id_string, sizeof(nut_file_id_string)); @@ -269,158 +288,149 @@ bool app_write_intro(int fd, struct app_state *state, uint64_t time_ns) { pkt.len = 0; /* 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_FB_SIZE(720_576) + /*! max_distance */ - APP_FRAME_MAX_OVERHEAD); + nut_append_vu(&pkt, 4); /*! version */ + nut_append_vu(&pkt, 0); /*! minor_version */ + nut_append_vu(&pkt, 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 *************************************/ - nut_append_vu(&pkt, 1); /*! time_base_count */ + nut_append_vu(&pkt, 1); /*! time_base_count */ /* time_base[0] = 1ns */ - nut_append_vu(&pkt, 1); /*! numerator */ - nut_append_vu(&pkt, 1000000000ULL); /*! denominator */ + 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)); /*! 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, 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, APP_FRAME_SIZE(640_480) % /*! 3: fields.size_lsb */ - APP_FRAME_SIZE_MSB_MUL); - nut_append_vu(&pkt, 0); /*! 4: fields.reserved */ - nut_append_vu(&pkt, 1); /*! 5: fields.count */ - - /* frame_code=2 (720x480) */ - nut_append_vu(&pkt, APP_COMMON_FLAGS); /*! flags */ - 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_mul */ - nut_append_vu(&pkt, 0); /*! 2: fields.stream */ - nut_append_vu(&pkt, APP_FRAME_SIZE(720_480) % /*! 3: fields.size_lsb */ - APP_FRAME_SIZE_MSB_MUL); - nut_append_vu(&pkt, 0); /*! 4: fields.reserved */ - nut_append_vu(&pkt, 1); /*! 5: fields.count */ - - /* frame_code=3 (720x576) */ - nut_append_vu(&pkt, APP_COMMON_FLAGS); /*! flags */ - 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, APP_FRAME_SIZE(720_576) % /*! 3: fields.size_lsb */ - APP_FRAME_SIZE_MSB_MUL); - nut_append_vu(&pkt, 0); /*! 4: fields.reserved */ - nut_append_vu(&pkt, 1); /*! 5: fields.count */ - - /* 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)); /*! 0: fields.pts */ - nut_append_vu(&pkt, 256-4-1); /*! 1: fields.size_msb_nul */ + nut_append_vu(&pkt, NUT_FRAMEFLAG_INVALID); /*! flags */ + nut_append_vu(&pkt, 2); /*! field_count */ + nut_append_vu(&pkt, BOGUS(0)); /*! 0: fields.pts */ + nut_append_vu(&pkt, 1); /*! 1: fields.size_msb_nul */ + 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 */ + 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_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); + 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, 0); /*! 4: fields.reserved */ + nut_append_vu(&pkt, 1); /*! 5: fields.count */ + INC_FRAME_CODE(); + } + + /* frame_code=N-255 (invalid) */ + nut_append_vu(&pkt, NUT_FRAMEFLAG_INVALID); /*! flags */ + nut_append_vu(&pkt, 2); /*! field_count */ + nut_append_vu(&pkt, BOGUS(0)); /*! 0: fields.pts */ + nut_append_vu(&pkt, /*! 1: fields.size_msb_nul */ + 256-frame_code-(frame_code < 'N' ? 1 : 0)); /* tail *******************************************/ - nut_append_vu(&pkt, 0); /*! header_count_minus_1 */ - nut_append_vu(&pkt, 0); /*! main_flags */ + nut_append_vu(&pkt, 0); /*! header_count_minus_1 */ + nut_append_vu(&pkt, NUT_MAINFLAG_PIPE_MODE); /*! main_flags */ 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 */ - nut_append_vu(&pkt, BOGUS(640)); /*! width (all frames set width) */ - nut_append_vu(&pkt, BOGUS(480)); /*! 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_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); + /* syncpoint **********************************************************/ + pkt.len = 0; + + nut_append_vu(&pkt, time_ns); /*! global_key_pts */ + nut_append_vu(&pkt, 0); /*! back_ptr_div16 */ + + nut_append_packet(&out, nut_startcode_syncpoint, pkt.dat, pkt.len); + /* flush **************************************************************/ #undef BOGUS 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 frame_size, fb_size; - switch (res) { - case APP_RES_640_480: frame_size = APP_FRAME_SIZE(640_480); fb_size = APP_FB_SIZE(640_480); break; - case APP_RES_720_480: frame_size = APP_FRAME_SIZE(720_480); fb_size = APP_FB_SIZE(720_480); break; - case APP_RES_720_576: frame_size = APP_FRAME_SIZE(720_576); fb_size = APP_FB_SIZE(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) */ + /* frame header ( 1+10+1+4 = 16 bytes) */ uint64_t frame_beg = out.len; - nut_append_u8(&out, 1+res); /*! frame_code (4 bytes) */ - nut_append_vu(&out, frame_size/APP_FRAME_SIZE_MSB_MUL); /*! data_size_msb (1 byte) */ + nut_append_u8(&out, 1+(2*res)+(res != state->res ? 1 : 0)); /*! 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_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) */ - 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_W(720_480)); /*! side_data[0].value (2 bytes) */ - nut_append_vb_str(&out, "Height"); /*! side_data[1].name (1+6 bytes) */ - nut_append_vu(&out, APP_H(720_480)/2); /*! side_data[1].value (2 bytes) */ - nut_append_vu(&out, 0); /*! meta_data_count (1 byte) */ - assert(out.len - data_beg == APP_SM_SIZE); + 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)); + } /* 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)) + if (xwrite(fd, framebuffer, app_res_fb_size(res))) return true; - state->len += fb_size; /* return */ - state->pts = time_ns; - state->last_syncpoint = beg; + state->res = res; return false; } @@ -443,8 +453,8 @@ char font[10][8*4] = { " #### ", " ## " - " # " - " # " + " # " + " # " " ## ", " ## " @@ -485,22 +495,15 @@ int main() { if (app_write_intro(1, &state, 0)) return 1; - uint8_t framebuffer[APP_FB_SIZE(720_576)]; + uint8_t framebuffer[app_res_fb_size(APP_RES_MAX)]; #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 y = 0; y < 4*SCALE; y++) 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)) + 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)) return 1; } return 0; |