------------------------------------------------------------------ TODO (2006-10-31) ------------------------------------------------------------------ * ! sub/rem transform code * ! option to make it auto-reset much quicker when clock skew or whatever causes lots of packet loss * ! wkr_udpin in particular is susceptible to having control msgs not make it since it uses the same socket as for recving. this is bad. the control thread will sit around waiting for a response that will never come, and the quit will not work, waiting for the control thread. at least it shows that a good chunk of the reference counting code is working. one thing to do is make msgs queue up in a list so that if packet is dropped, the msg isn't dropped, and the next successful packet recv will cause it to get all the msgs * document external interface * should make logger debug scheme consistent across wkrs * make disk sender wkrs * see about decklink directshow wkr * document internal architecture * file commands (not just command line) * pre-parsed stats for more efficient handling by wkrs * quoting in mpe responses e.g. where %s is used * responses should be mpes, not mebs (strings) * figure out how to reduce mutex lock/unlock for dg_buf gets and puts * ! dgs in dg_buf need to have refcount if going to multiple destinations * replace dg_q with linked-list style scheme. need to allocate list pointer for each dg for each possible destination. setup might be complicated, but when running, should be very simple and efficient, and should also make it possible for consumer side of dg_q to reuse list pointer to generate lists of datagrams to free to reduce dg_buf mutex locking. ------------------------------------------------------------------ EXAMPLES ------------------------------------------------------------------ Start a ctrlaccept thread to accept control connections on port 4000: build/xenarecv '(wkr_create ctrlaccept ctrl b=:4000)' "Ping" xenarecv - test that control stuff works: scripts/mpe-cmd.pl cmd localhost:4000 '(ping)' Tell xenarecv to quit: scripts/mpe-cmd.pl cmd localhost:4000 '(quit)' Receive datagrams from dlsend, but not do anything with them. Check stats every 4 seconds: build/xenarecv ' (wkr_create ctrlaccept ctrl b=:4000) (obj_create dg_buf buf) (wkr_create udpin i0 b=198.48.79.10:3000 dg_buf=buf) (wkr_create udpin i1 b=198.48.79.11:3000 dg_buf=buf) ' scripts/mpe-cmd.pl stats localhost:4000 4 i0 i1 Receive datagrams from dlsend in full image mode to xenaout. Check stats every 4 seconds. Throw some thread priority/affinity in there: build/xenarecv ' (wkr_create ctrlaccept ctrl b=:4000) (obj_create dg_buf buf) (wkr_create xenaout o affinity=1 (qr 0 0 v) (qr 2 0 a) (mix 0 2)) (wkr_create udpin i0 priority=-15 affinity=4 b=198.48.79.10:3000 dg_buf=buf out=o) (wkr_create udpin i1 priority=-15 affinity=8 b=198.48.79.11:3000 dg_buf=buf out=o) ' scripts/mpe-cmd.pl stats localhost:4000 4 i0 i1 o Receive just a subsampled stream (of a 2x2 tiling) from dlsend to xenaout. Position the stream in the middle of the screen: build/xenarecv ' (wkr_create ctrlaccept ctrl b=:4000) (obj_create dg_buf buf) (wkr_create xenaout o affinity=1 (qr 0 0 (v scale=2,2)) (qr 2 0 a) (mix (0 pos=480,270) 2)) (wkr_create udpin i0 priority=-15 affinity=4 b=198.48.79.10:3000 dg_buf=buf out=o) (wkr_create udpin i1 priority=-15 affinity=8 b=198.48.79.11:3000 dg_buf=buf out=o) ' Reset the output thread (will reset all streams waiting for them to rebuffer and so on): scripts/mpe-cmd.pl cmd localhost:4000 '(wkr o reset)' Replace "xenaout" with "dummyout" in the examples above to have it do everything but output the stuff to the xena card. All the same stats are generated. This will work on linux as well. Decklink send full video and audio to another host. Replace udpout b IP addresses with those of the local interfaces. Replace dest_* IP addresses with those of the remote (destination) machine. build/xenarecv ' (wkr_create ctrlaccept ctrl b=:4000) (wkr_create udpout o0 b=198.48.79.16) (wkr_create udpout o1 b=198.48.79.17) (wkr_create udpout o4 b=198.48.79.16) (wkr_create udpout o5 b=198.48.79.17) (wkr_create dl_in i) (wkr i cfg dest_vf0=0,198.48.79.10:3000,o0 dest_vf1=0,198.48.79.11:3000,o1 dest_a0=2,198.48.79.10:3000,o4 dest_a1=2,198.48.79.11:3000,o5) ' Receive the above stream. Replace the udpin b IP addresses with those of the local interfaces. build/xenarecv ' (wkr_create ctrlaccept ctrl b=:4000) (obj_create dg_buf buf) (wkr_create xenaout o (qr 0 0 v) (qr 2 0 a) (mix 0 2)) (wkr_create udpin i0 b=198.48.79.10:3000 dg_buf=buf out=o) (wkr_create udpin i1 b=198.48.79.11:3000 dg_buf=buf out=o) ' ------------------------------------------------------------------ CONTROL PROTOCOL (and command line arguments) ------------------------------------------------------------------ socket connection is made. then commands are issued and responded to. both commands and responses are LISP-ish expressions. A simple command is "(quit)" or "(ping)". the basic parsing rules for the expressions are: - each expression can be a string or a list of expressions. - a list has () around it. - lists can be nested. - quoting rules for strings are as follows: - outside of quotes, backslash quotes next character literally - in double quotes, backslash quotes next character literally - in single quotes, everything is literal, including backslash - double or single quoting sections can start or end at any point within the string. e.g.: ab"c d \""e\'fg'h" i'jkl gets turned into one string of the form: abc d "e'fgh" ijkl ------------------------------------------------------------------ EVERYTHING BELOW HERE: is scattered notes about the internal architecture that are probably out of date, if not flat out wrong. ------------------------------------------------------------------ ------------------------------------------------------------------ 2006-10-24 ------------------------------------------------------------------ master is not separate thread, but a set of function calls, though it has a "joiner" thread that just waits and reaps the other threads. wkr_create: called by wkr to create a wkr wkr_free: called by wkr to unlist from master's list wkr_ref_acquire: gets a ref to a wkr in master list wkr_ref_release: release an acquired ref wkr_quit: essentially calls wkr_free for all wkrs from_wkr_created: called by wkr when done preparing from_wkr_done: called by wkr when ready to quit from_wkr_join: called by wkr just before termination wkr_create: - fail if master state quitting - adds wkr to list in state preparing with refcount 1 - inc master->refcount wkr_free: - fail if not listed (ASSERT: done not called) - unlist, dec refcount - if refcount == 0 send msg_end wkr_ref_acquire: - fail if not listed - inc refcount wkr_ref_release: - ASSERT: if done called, end_msg not sent - ASSERT: if done not called, - dec refcount - if refcount == 0 send msg_end from_wkr_created: - if master state quitting, then essentially do from_wkr_done - set state to active from_wkr_done: - ASSERT: only way msg_end will not be sent is if this causes wkr to be unlisted and there are no outstanding references - if not listed, return msg_end_sent - if listed - unlist, dec refcount - if refcount nonzero, return msg_end_sent (will be sent) from_wkr_join: - dec master->refcount example cases: done free, done acquire, release, free, done acquire, free, release, done acquire, free, done, release ------------------------------------------------------------------ 2006-10-19 ------------------------------------------------------------------ allow master to create shared objects. objects die when refcount == 0. other workers can get refs to object, which incs refcount. mutex must be locked when accessing refcount. mutex can be master one for now. move dg stuff out of master into dg_buf shared object. make dg_q shared object. add type description to dg_qr that can be used to generate frame_z and be used by mix to check/get overall frame size parameters. dummyout, xenaout: replace qr_coll arg with a direct qr arg maybe change datagram header to have one 32-bit static header and one 64-bit absolute dynamic header. one static with stream_id and one dynamic that is a sequence number counter: - no wrapping, dg counter contiguous across frame boundary - useful for netspew. - when stream starts, divide/mod by dg_per_frame to align start with frame boundary. - after that no divide should be necessary. datagram either fits in dg_qr, or too_lo, or too_hi. - to prevent a mod by dg_per_frame, either make all dgs the same len, don't check dg len at all (which is ok memory access wise since the buffer containing the dg will be large enough), or wait until processing the dg to check len. - could use a wrapping 32-bit counter and keep absolute time since start in the periodic header, since it won't change often. ---------------------------------------------------------------------- 2006-9-17: rest of this file might be out of date Messages: new wkrs: master: creates wkr, sets initial args. adds to wkr list in pending state. calls prepare: starts thread. doesn't return a meaningful value. sends a msg to master (success/fail). if receive msg for wkr pending success/fail queue it up until success/fail receive success: post queued messages can post msgs until quit msg received receive fail: remove from wkr list reject queued messages deallocate wkr (might need to join thread - wkr keeps this state) wkr quit (post-success): master always sends wkr_quit to wkr wkr always sends wkr_quit to master when wkr sends wkr_quit it has no more references, can be joined, though it will wait until it gets master's wkr_quit before actually quitting when master receives wkr_quit, make sure it sends wkr_quit, removes from wkr list, joins thread. wkr - if fail received, remove wkr - thread start/stop/etc - each thread and master has refcount of things with refs to it - before thread is started, thread and master will have refs to each other - prepare will start thread. once successfully prepared, master must send message to get thread to stop. - wkr->post should fail gracefully if thread not started - thread will quit when refcount drops to 0 - same with master - for another thread to get permission to send messages, it must go through something that already does (e.g. master) - the point of all this is that thread knows when it will receive no more messages - thread will increment its refcount when it sends a message expecting a reply and dec upon receipt of message in0 prepared (thread started) in1 prepared (thread started) in0 and in1 throw away packets until start msg received in0 and in1 only ref will be from master out prepared (thread started) start msg sent to all of them in0 and in1 send request to master ---------------------------------------------------------------------- 2006-9-5: - types of streams: - full video + audio - tile only + audio - tile + remainder + audio - received streams independent from displayed streams? - this way queues can be set up beforehand to switch the type of stream. - config: - each datagram queue configured independently. can add / remove from table keyed by stream id. - when new queue goes into table (whether replacement or new), the display counters and such need to be reset - queues are also grouped - when queue list updated, groups need to be updated as well, so accept a list of operations to be applied in the same operation. ---------------------------------------------------------------------- dg_q: static: dg_z; dg_z_last; dg_n; dg_i_last; frame_n; start_len; overflow_len; dynamic: start_frame; state; pos; len; used by display thread: out_len; TODO: * put disable functionality back in * contacts server dg_q and dg_q_group allocation: dg_q for each stream. dg_q_group for each group. with remote control functionality, the number of possible streams can be exactly specified. until then, might as well allocate for all possibilities - doesn't take up that much space. dg_q for each incoming stream. sets of dg_q can be linked so that they all get start_frame updated at same time. dg_q frame_len (static) start_len (static) overflow_len (static) q_start q_len start_frame started out_len overflow_cur recv_in: if dg past overflow_cur (starts at overflow_len then drops to start_len) drop and increment overflow counter (set overflow_cur to start_len) else post if room if start_frame == -1 { if any linked have start_frame != 1, use that, else use cur } xena_out needs: single: sub linked dgs from frame rem linked dgs from frame audio linked dgs from frame other audio unlinked from frame tiled: Xx sub linked Xx audio linked each video frame: for each selected dg_q { if(! started) { if(q_len >= start_len) { out_len = frame_len out_use = 1 started = 1 } } else { if(q_len < frame_len) extend q_len = frame_len out_len = frame_len out_use = 1 } } unlock if sub rem { for each block { if(dg_q->out_use && dg_q->q[q_start + ...]) { use data from there else black } calculate } } else tiled { for each dg_q for 1 - 16 or whatever { if dg_q->out_use for each valid block copy into appropriate place or black } } audio ... { all audio = 0 for each dg_q of audio { if(dg_q->out_use) for each block add to audio samples } TODO: deal with varying number of samples for each frame. } lock for each dg_t used clear all dg_t to NULL and place on free list out_len = 0 out_use = 0 update q_start, q_len by out_len update start_frame if(q_len == 0) { started = 0 start_frame = -1 } if overflow and q_len <= start_len reset overflow