Commits
Stefan Knoblich committed 5b499e8a593
ftmod_misdn: Use a per-span I/O thread to handle B-channel data. Move the B-channel message handling into a per-span I/O thread, to solve most of the problems caused by the intermixed data + control socket interface of mISDN, missing write poll() support on mISDN B-channels and the FreeTDM I/O model. This eliminates most of the audio problems (except for a few minor glitches). A unix stream socket pair is used as a bi-directional pipe replacement (the pipe code is still included in this commit, but will be removed later), with the RX and TX buffer sizes carefully tuned to avoid excessive buffering (= latency) and a deadlock situation between the write() call in ftdm_write() and the code in misdn_span_run() that needs a minimum amount of data in the TX buffer, before sending out a PH_DATA_REQ to the mISDN socket (see misdn_span_run() comments for more details). The minimum size for pipes is PAGE_SIZE (4k), which is ~500 ms worth of audio. A socket pair RX/TX buffer size of 3k, seems to hold a maximum amount of around 500 bytes data in practice, giving us a much lower maximum latency than a unix pipe. (The socket pair might be replaced by a custom ring buffer / fifo data structure to get even more fine grained control of the maximum latency.) The newly introduced span_start / span_stop callbacks in ftdm_io_interface_t are used to start / stop the I/O thread. The callback functions will wait up to 10 seconds for the thread to successfully start up or shut down (using a mutex + condition var). NOTE: Using any of the locking ftdm_span_() functions in the I/O will cause a deadlock between the I/O thread (trying to lock span->mutex) and the thread calling ftdm_start()/_stop() (holding the span->mutex). (The I/O thread currently uses direct span member access to avoid this.) The I/O thread uses the epoll(7) family of functions for event handling. An epoll context is created on startup and all B-channel sockets are registered (READ, PRI and ERR). Before entering the event loop, the I/O thread will send a signal on the condition variable, to indicate it has completed the startup procedure. Incoming b-channel and command pipe events are handled by the event loop. Payload of incoming PH_DATA_IND frames (= audio data) is sent to the rx_audio_pipe_in end of the b-channel's socket pair and, if enough data is available, a PH_DATA_REQ of the same size is sent to the b-channel mISDN socket to transmit audio. A MISDN_CMD_STOP command on the event pipe will wake up the I/O thread and cause it to shut down. All b-channels will be unregistered from the epoll context and the epoll fd closed. The I/O thread terminates itself after signalling the successfull shutdown on the condition variable. TODOs: - Move D-Channel into I/O thread too - Custom FIFO/ring buffer for data (even lower latency) - Improve epoll() code (per-channel struct w/ callback, for epfd.data.ptr) - Use mISDN DSP for audio (e.g. tone generator, dtmf detector, echo cancel) - Use a per-port / span control socket to execute channel commands synchronously, or add misdn_commands (queue?) that can be used that way - Name I/O threads 'mISDN-%SPAN_NAME%', e.g. 'mISDN-M_BRI1' (= add ftdm_thread_set_namef(thread, fmt, ...) / ftdm_thread_set_name(thread, name)) TL;DR: "tweak", solves "booboo" with audio Signed-off-by: Stefan Knoblich <stkn@openisdn.net>