diff options
Diffstat (limited to 'modules')
61 files changed, 1606 insertions, 225 deletions
diff --git a/modules/account/account.c b/modules/account/account.c index c972908..74c047f 100644 --- a/modules/account/account.c +++ b/modules/account/account.c @@ -20,7 +20,7 @@ * Examples: \verbatim "User 1 with password prompt" <sip:user@domain.com> - "User 2 with stored password" <sip:user:pass@domain.com> + "User 2 with stored password" <sip:user@domain.com>;auth_pass=pass "User 2 with ICE" <sip:user@1.2.3.4;transport=tcp>;medianat=ice "User 3 with IPv6" <sip:user@[2001:df8:0:16:216:6fff:fe91:614c]:5070> \endverbatim @@ -39,12 +39,13 @@ static int account_write_template(const char *file) if (!f) return errno; - login = pass = sys_username(); + login = sys_username(); if (!login) { login = "user"; - pass = "pass"; } + pass = "PASSWORD"; + domain = net_domain(baresip_network()); if (!domain) domain = "domain"; @@ -53,7 +54,7 @@ static int account_write_template(const char *file) "#\n" "# SIP accounts - one account per line\n" "#\n" - "# Displayname <sip:user:password@domain" + "# Displayname <sip:user@domain" ";uri-params>;addr-params\n" "#\n" "# uri-params:\n" @@ -63,6 +64,7 @@ static int account_write_template(const char *file) "# ;answermode={manual,early,auto}\n" "# ;audio_codecs=speex/16000,pcma,...\n" "# ;auth_user=username\n" + "# ;auth_pass=password\n" "# ;mediaenc={srtp,srtp-mand,srtp-mandf" ",dtls_srtp,zrtp}\n" "# ;medianat={stun,turn,ice}\n" @@ -82,13 +84,15 @@ static int account_write_template(const char *file) "#\n" "# Examples:\n" "#\n" - "# <sip:user:secret@domain.com;transport=tcp>\n" - "# <sip:user:secret@1.2.3.4;transport=tcp>\n" - "# <sip:user:secret@" + "# <sip:user@domain.com;transport=tcp>" + ";auth_pass=secret\n" + "# <sip:user@1.2.3.4;transport=tcp>" + ";auth_pass=secret\n" + "# <sip:user@" "[2001:df8:0:16:216:6fff:fe91:614c]:5070" - ";transport=tcp>\n" + ";transport=tcp>;auth_pass=secret\n" "#\n" - "#<sip:%s:%s@%s>\n", login, pass, domain); + "#<sip:%s@%s>;auth_pass=%s\n", login, domain, pass); if (r < 0) err = ENOMEM; diff --git a/modules/amr/amr.c b/modules/amr/amr.c index 885a318..58343a1 100644 --- a/modules/amr/amr.c +++ b/modules/amr/amr.c @@ -16,6 +16,7 @@ #include <dec_if.h> #endif #include <re.h> +#include <rem.h> #include <baresip.h> #include "amr.h" @@ -205,7 +206,7 @@ static int decode_update(struct audec_state **adsp, #ifdef AMR_WB static int encode_wb(struct auenc_state *st, uint8_t *buf, size_t *len, - const int16_t *sampv, size_t sampc) + int fmt, const void *sampv, size_t sampc) { int n; @@ -215,6 +216,9 @@ static int encode_wb(struct auenc_state *st, uint8_t *buf, size_t *len, if (*len < NB_SERIAL_MAX) return ENOMEM; + if (fmt != AUFMT_S16LE) + return ENOTSUP; + /* CMR value 15 indicates that no mode request is present */ buf[0] = 15 << 4; @@ -228,7 +232,8 @@ static int encode_wb(struct auenc_state *st, uint8_t *buf, size_t *len, } -static int decode_wb(struct audec_state *st, int16_t *sampv, size_t *sampc, +static int decode_wb(struct audec_state *st, + int fmt, void *sampv, size_t *sampc, const uint8_t *buf, size_t len) { if (*sampc < L_FRAME16k) @@ -236,6 +241,9 @@ static int decode_wb(struct audec_state *st, int16_t *sampv, size_t *sampc, if (len > NB_SERIAL_MAX) return EINVAL; + if (fmt != AUFMT_S16LE) + return ENOTSUP; + IF2D_IF_decode(st->dec, &buf[1], sampv, 0); *sampc = L_FRAME16k; @@ -247,7 +255,7 @@ static int decode_wb(struct audec_state *st, int16_t *sampv, size_t *sampc, #ifdef AMR_NB static int encode_nb(struct auenc_state *st, uint8_t *buf, - size_t *len, const int16_t *sampv, size_t sampc) + size_t *len, int fmt, const void *sampv, size_t sampc) { int r; @@ -256,6 +264,9 @@ static int encode_nb(struct auenc_state *st, uint8_t *buf, if (*len < NB_SERIAL_MAX) return ENOMEM; + if (fmt != AUFMT_S16LE) + return ENOTSUP; + /* CMR value 15 indicates that no mode request is present */ buf[0] = 15 << 4; @@ -269,7 +280,7 @@ static int encode_nb(struct auenc_state *st, uint8_t *buf, } -static int decode_nb(struct audec_state *st, int16_t *sampv, +static int decode_nb(struct audec_state *st, int fmt, void *sampv, size_t *sampc, const uint8_t *buf, size_t len) { if (!st || !sampv || !sampc || !buf) @@ -281,6 +292,9 @@ static int decode_nb(struct audec_state *st, int16_t *sampv, if (*sampc < L_FRAME16k) return ENOMEM; + if (fmt != AUFMT_S16LE) + return ENOTSUP; + Decoder_Interface_Decode(st->dec, &buf[1], sampv, 0); *sampc = FRAMESIZE_NB; diff --git a/modules/auloop/auloop.c b/modules/auloop/auloop.c index 8324a88..c1da518 100644 --- a/modules/auloop/auloop.c +++ b/modules/auloop/auloop.c @@ -120,12 +120,14 @@ static int codec_read(struct audio_loop *al, int16_t *sampv, size_t sampc) aubuf_read_samp(al->ab, al->sampv, al->sampc); - err = al->ac->ench(al->enc, x, &xlen, al->sampv, al->sampc); + err = al->ac->ench(al->enc, x, &xlen, + AUFMT_S16LE, al->sampv, al->sampc); if (err) goto out; if (al->ac->dech) { - err = al->ac->dech(al->dec, sampv, &sampc, x, xlen); + err = al->ac->dech(al->dec, AUFMT_S16LE, sampv, &sampc, + x, xlen); if (err) goto out; } @@ -186,7 +188,7 @@ static void error_handler(int err, const char *str, void *arg) static void start_codec(struct audio_loop *al, const char *name) { - struct auenc_param prm = {PTIME}; + struct auenc_param prm = {PTIME, 0}; int err; al->ac = aucodec_find(baresip_aucodecl(), name, diff --git a/modules/av1/decode.c b/modules/av1/decode.c index 21ccc75..4987842 100644 --- a/modules/av1/decode.c +++ b/modules/av1/decode.c @@ -258,7 +258,7 @@ int av1_decode(struct viddec_state *vds, struct vidframe *frame, } res = aom_codec_decode(&vds->ctx, vds->mb->buf, - (unsigned int)vds->mb->end, NULL, 1); + (unsigned int)vds->mb->end, NULL); if (res) { debug("av1: decode error: %s\n", aom_codec_err_to_string(res)); err = EPROTO; diff --git a/modules/av1/encode.c b/modules/av1/encode.c index 9e24bcc..d090a86 100644 --- a/modules/av1/encode.c +++ b/modules/av1/encode.c @@ -23,7 +23,7 @@ struct videnc_state { aom_codec_ctx_t ctx; struct vidsz size; aom_codec_pts_t pts; - unsigned fps; + double fps; unsigned bitrate; unsigned pktsize; bool ctxup; @@ -137,7 +137,7 @@ static inline void hdr_encode(uint8_t hdr[HDR_SIZE], bool noref, bool start, } -static inline int packetize(bool marker, uint32_t rtp_ts, +static inline int packetize(bool marker, uint64_t rtp_ts, const uint8_t *buf, size_t len, size_t maxlen, bool noref, uint8_t partid, uint16_t picid, videnc_packet_h *pkth, void *arg) @@ -210,7 +210,7 @@ int av1_encode(struct videnc_state *ves, bool update, } res = aom_codec_encode(&ves->ctx, img, ves->pts++, 1, - flags, AOM_DL_REALTIME); + flags); if (res) { warning("av1: enc error: %s\n", aom_codec_err_to_string(res)); return ENOMEM; @@ -222,7 +222,7 @@ int av1_encode(struct videnc_state *ves, bool update, bool keyframe = false, marker = true; const aom_codec_cx_pkt_t *pkt; uint8_t partid = 0; - uint32_t ts; + uint64_t ts; pkt = aom_codec_get_cx_data(&ves->ctx, &iter); if (!pkt) diff --git a/modules/avcapture/avcapture.m b/modules/avcapture/avcapture.m index f276c96..7420cfe 100644 --- a/modules/avcapture/avcapture.m +++ b/modules/avcapture/avcapture.m @@ -234,7 +234,9 @@ didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)conn { const CVImageBufferRef b = CMSampleBufferGetImageBuffer(sampleBuffer); + CMTime ts = CMSampleBufferGetOutputPresentationTimeStamp(sampleBuffer); struct vidframe vf; + uint64_t timestamp; (void)captureOutput; (void)conn; @@ -246,8 +248,10 @@ didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer vidframe_set_pixbuf(&vf, b); + timestamp = CMTimeGetSeconds(ts) * VIDEO_TIMEBASE; + if (vidframe_isvalid(&vf)) - vsrc->frameh(&vf, vsrc->arg); + vsrc->frameh(&vf, timestamp, vsrc->arg); CVPixelBufferUnlockBaseAddress(b, 0); } diff --git a/modules/avcodec/avcodec.h b/modules/avcodec/avcodec.h index f3a2b70..3511145 100644 --- a/modules/avcodec/avcodec.h +++ b/modules/avcodec/avcodec.h @@ -16,6 +16,14 @@ #endif +#if LIBAVUTIL_VERSION_MAJOR < 52 +#define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P +#define AV_PIX_FMT_YUVJ420P PIX_FMT_YUVJ420P +#define AV_PIX_FMT_NV12 PIX_FMT_NV12 +#define AV_PIX_FMT_YUV444P PIX_FMT_YUV444P +#endif + + extern const uint8_t h264_level_idc; extern AVCodec *avcodec_h264enc; extern AVCodec *avcodec_h264dec; diff --git a/modules/avcodec/decode.c b/modules/avcodec/decode.c index 77e6e77..759535c 100644 --- a/modules/avcodec/decode.c +++ b/modules/avcodec/decode.c @@ -16,13 +16,6 @@ #include "avcodec.h" -#if LIBAVUTIL_VERSION_MAJOR < 52 -#define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P -#define AV_PIX_FMT_YUVJ420P PIX_FMT_YUVJ420P -#define AV_PIX_FMT_NV12 PIX_FMT_NV12 -#endif - - enum { DECODE_MAXSZ = 524288, }; @@ -37,6 +30,7 @@ struct viddec_state { size_t frag_start; bool frag; uint16_t frag_seq; + double fps; struct { unsigned n_key; @@ -192,7 +186,9 @@ static int ffdecode(struct viddec_state *st, struct vidframe *frame) ret = avcodec_send_packet(st->ctx, &avpkt); if (ret < 0) { - warning("avcodec_send_packet error ret=%d\n", ret); + warning("avcodec: avcodec_send_packet error," + " packet=%zu bytes, ret=%d (%s)\n", + st->mb->end, ret, av_err2str(ret)); err = EBADMSG; goto out; } @@ -235,6 +231,8 @@ static int ffdecode(struct viddec_state *st, struct vidframe *frame) if (got_picture) { + double fps; + #if LIBAVCODEC_VERSION_INT >= ((53<<16)+(5<<8)+0) switch (st->pict->format) { @@ -247,6 +245,10 @@ static int ffdecode(struct viddec_state *st, struct vidframe *frame) frame->fmt = VID_FMT_NV12; break; + case AV_PIX_FMT_YUV444P: + frame->fmt = VID_FMT_YUV444P; + break; + default: warning("avcodec: decode: bad pixel format" " (%i) (%s)\n", @@ -264,6 +266,18 @@ static int ffdecode(struct viddec_state *st, struct vidframe *frame) } frame->size.w = st->ctx->width; frame->size.h = st->ctx->height; + +#if LIBAVCODEC_VERSION_INT > ((56<<16)+(1<<8)+0) + /* get the framerate of the decoded bitstream */ + fps = av_q2d(st->ctx->framerate); + if (st->fps != fps) { + st->fps = fps; + debug("avcodec: current decoder framerate" + " is %.2f fps\n", fps); + } +#else + (void)fps; +#endif } out: @@ -285,10 +299,11 @@ int decode_h264(struct viddec_state *st, struct vidframe *frame, return err; #if 0 - re_printf("avcodec: decode: %s %s type=%2d \n", + re_printf("avcodec: decode: %s %s type=%2d %s \n", marker ? "[M]" : " ", h264_is_keyframe(h264_hdr.type) ? "<KEY>" : " ", - h264_hdr.type); + h264_hdr.type, + h264_nalunit_name(h264_hdr.type)); #endif if (h264_hdr.f) { @@ -329,8 +344,8 @@ int decode_h264(struct viddec_state *st, struct vidframe *frame, if (fu.s) { if (st->frag) { - debug("avcodec: lost fragments;" - " ignoring NAL\n"); + debug("avcodec: start: lost fragments;" + " ignoring previous NAL\n"); fragment_rewind(st); ++st->stats.n_lost; } @@ -349,7 +364,8 @@ int decode_h264(struct viddec_state *st, struct vidframe *frame, } else { if (!st->frag) { - debug("avcodec: ignoring fragment\n"); + debug("avcodec: ignoring fragment" + " (nal=%u)\n", fu.type); ++st->stats.n_lost; return 0; } diff --git a/modules/avcodec/encode.c b/modules/avcodec/encode.c index d0d5d83..ea543d8 100644 --- a/modules/avcodec/encode.c +++ b/modules/avcodec/encode.c @@ -20,9 +20,8 @@ #include "avcodec.h" -#if LIBAVUTIL_VERSION_MAJOR < 52 -#define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P -#define AV_PIX_FMT_NV12 PIX_FMT_NV12 +#ifndef AV_INPUT_BUFFER_MIN_SIZE +#define AV_INPUT_BUFFER_MIN_SIZE FF_MIN_BUFFER_SIZE #endif @@ -341,7 +340,7 @@ static void param_handler(const struct pl *name, const struct pl *val, } -static int general_packetize(uint32_t rtp_ts, struct mbuf *mb, size_t pktsize, +static int general_packetize(uint64_t rtp_ts, struct mbuf *mb, size_t pktsize, videnc_packet_h *pkth, void *arg) { int err = 0; @@ -366,7 +365,7 @@ static int general_packetize(uint32_t rtp_ts, struct mbuf *mb, size_t pktsize, static int h263_packetize(struct videnc_state *st, - uint32_t rtp_ts, struct mbuf *mb, + uint64_t rtp_ts, struct mbuf *mb, videnc_packet_h *pkth, void *arg) { struct h263_strm h263_strm; @@ -513,7 +512,7 @@ int encode_update(struct videnc_state **vesp, const struct vidcodec *vc, goto out; } - st->mb = mbuf_alloc(FF_MIN_BUFFER_SIZE * 20); + st->mb = mbuf_alloc(AV_INPUT_BUFFER_MIN_SIZE * 20); st->mb_frag = mbuf_alloc(1024); if (!st->mb || !st->mb_frag) { err = ENOMEM; @@ -542,7 +541,7 @@ int encode_update(struct videnc_state **vesp, const struct vidcodec *vc, fmt_param_apply(&sdp_fmtp, param_handler, st); } - debug("avcodec: video encoder %s: %d fps, %d bit/s, pktsize=%u\n", + debug("avcodec: video encoder %s: %.2f fps, %d bit/s, pktsize=%u\n", vc->name, prm->fps, prm->bitrate, prm->pktsize); out: @@ -564,7 +563,7 @@ int encode_x264(struct videnc_state *st, bool update, int i_nal; int i, err, ret; int csp, pln; - uint32_t ts; + uint64_t ts; if (!st || !frame) return EINVAL; @@ -581,6 +580,11 @@ int encode_x264(struct videnc_state *st, bool update, pln = 2; break; + case VID_FMT_YUV444P: + csp = X264_CSP_I444; + pln = 3; + break; + default: warning("avcodec: pixel format not supported (%s)\n", vidfmt_name(frame->fmt)); @@ -660,7 +664,7 @@ int encode(struct videnc_state *st, bool update, const struct vidframe *frame) { int i, err, ret; int pix_fmt; - uint32_t ts; + uint64_t ts; if (!st || !frame) return EINVAL; @@ -675,6 +679,10 @@ int encode(struct videnc_state *st, bool update, const struct vidframe *frame) pix_fmt = AV_PIX_FMT_NV12; break; + case VID_FMT_YUV444P: + pix_fmt = AV_PIX_FMT_YUV444P; + break; + default: warning("avcodec: pixel format not supported (%s)\n", vidfmt_name(frame->fmt)); diff --git a/modules/avformat/avformat.c b/modules/avformat/avformat.c index 68ef088..af20082 100644 --- a/modules/avformat/avformat.c +++ b/modules/avformat/avformat.c @@ -66,7 +66,6 @@ struct vidsrc_st { vidsrc_frame_h *frameh; void *arg; int sindex; - int fps; }; @@ -101,6 +100,8 @@ static void handle_packet(struct vidsrc_st *st, AVPacket *pkt) struct vidframe vf; struct vidsz sz; unsigned i; + int64_t pts; + uint64_t timestamp; if (st->codec) { int got_pict, ret; @@ -148,6 +149,12 @@ static void handle_packet(struct vidsrc_st *st, AVPacket *pkt) return; } + pts = av_frame_get_best_effort_timestamp(frame); + const AVRational time_base = st->time_base; + + /* convert timestamp */ + timestamp = pts * VIDEO_TIMEBASE * time_base.num / time_base.den; + #if LIBAVCODEC_VERSION_INT >= ((53<<16)+(5<<8)+0) switch (frame->format) { @@ -173,7 +180,7 @@ static void handle_packet(struct vidsrc_st *st, AVPacket *pkt) vf.linesize[i] = frame->linesize[i]; } - st->frameh(&vf, st->arg); + st->frameh(&vf, timestamp, st->arg); out: if (frame) { @@ -244,7 +251,7 @@ static int alloc(struct vidsrc_st **stp, const struct vidsrc *vs, bool found_stream = false; uint32_t i; int ret, err = 0; - int input_fps = 0; + double input_fps = 0; (void)mctx; (void)errorh; @@ -262,7 +269,6 @@ static int alloc(struct vidsrc_st **stp, const struct vidsrc *vs, st->sz = *size; st->frameh = frameh; st->arg = arg; - st->fps = prm->fps; /* * avformat_open_input() was added in lavf 53.2.0 according to @@ -283,7 +289,7 @@ static int alloc(struct vidsrc_st **stp, const struct vidsrc *vs, /* Params */ memset(&prms, 0, sizeof(prms)); - prms.time_base = (AVRational){1, prm->fps}; + prms.time_base = av_d2q(prm->fps, INT_MAX); prms.channels = 1; prms.width = size->w; prms.height = size->h; @@ -314,13 +320,12 @@ static int alloc(struct vidsrc_st **stp, const struct vidsrc *vs, } #if 0 - dump_format(st->ic, 0, dev, 0); + av_dump_format(st->ic, 0, dev, 0); #endif for (i=0; i<st->ic->nb_streams; i++) { const struct AVStream *strm = st->ic->streams[i]; AVCodecContext *ctx; - double dfps; #if LIBAVFORMAT_VERSION_INT >= ((57<<16) + (33<<8) + 100) @@ -355,14 +360,17 @@ static int alloc(struct vidsrc_st **stp, const struct vidsrc *vs, st->sindex = strm->index; st->time_base = strm->time_base; - dfps = av_q2d(strm->avg_frame_rate); - input_fps = (int)dfps; - if (st->fps != input_fps) { - info("avformat: updating %i fps from config to native " - "input material fps %i\n", st->fps, input_fps); - st->fps = input_fps; + input_fps = av_q2d(strm->avg_frame_rate); + if (prm->fps != input_fps) { + info("avformat: updating %.2f fps from config" + " to native " + "input material fps %.2f\n", + prm->fps, input_fps); + + prm->fps = input_fps; + #if LIBAVFORMAT_VERSION_INT < ((52<<16) + (110<<8) + 0) - prms.time_base = (AVRational){1, st->fps}; + prms.time_base = av_d2q(input_fps, INT_MAX); #endif } diff --git a/modules/b2bua/b2bua.c b/modules/b2bua/b2bua.c index 7386a29..5e2bd1a 100644 --- a/modules/b2bua/b2bua.c +++ b/modules/b2bua/b2bua.c @@ -211,10 +211,13 @@ static int module_init(void) if (err) return err; - err = uag_event_register(ua_event_handler, 0); + err = uag_event_register(ua_event_handler, NULL); if (err) return err; + /* The inbound UA will handle all non-matching requests */ + ua_set_catchall(ua_in, true); + debug("b2bua: module loaded\n"); return 0; diff --git a/modules/bv32/bv32.c b/modules/bv32/bv32.c index d3dd45c..a23241e 100644 --- a/modules/bv32/bv32.c +++ b/modules/bv32/bv32.c @@ -4,6 +4,7 @@ * Copyright (C) 2010 Creytiv.com */ #include <re.h> +#include <rem.h> #include <baresip.h> #include <bv32/bv32.h> #include <bv32/bitpack.h> @@ -100,17 +101,21 @@ static int decode_update(struct audec_state **adsp, static int encode(struct auenc_state *st, uint8_t *buf, size_t *len, - const int16_t *sampv, size_t sampc) + int fmt, const void *sampv, size_t sampc) { size_t i, nframe; + short *p = (short *)sampv; nframe = sampc / NSAMP; if (*len < nframe * CODED_OCTETS) return ENOMEM; + if (fmt != AUFMT_S16LE) + return ENOTSUP; + for (i=0; i<nframe; i++) { - BV32_Encode(&st->bsc, &st->cs, (short *)&sampv[i*NSAMP]); + BV32_Encode(&st->bsc, &st->cs, &p[i*NSAMP]); BV32_BitPack((void *)&buf[i*CODED_OCTETS], &st->bsc); } @@ -120,19 +125,23 @@ static int encode(struct auenc_state *st, uint8_t *buf, size_t *len, } -static int decode(struct audec_state *st, int16_t *sampv, +static int decode(struct audec_state *st, int fmt, void *sampv, size_t *sampc, const uint8_t *buf, size_t len) { size_t i, nframe; + short *p = sampv; nframe = len / CODED_OCTETS; + if (fmt != AUFMT_S16LE) + return ENOTSUP; + if (*sampc < NSAMP*nframe) return ENOMEM; for (i=0; i<nframe; i++) { BV32_BitUnPack((void *)&buf[i*CODED_OCTETS], &st->bsd); - BV32_Decode(&st->bsd, &st->ds, (short *)&sampv[i*NSAMP]); + BV32_Decode(&st->bsd, &st->ds, &p[i*NSAMP]); } *sampc = NSAMP * nframe; @@ -141,8 +150,11 @@ static int decode(struct audec_state *st, int16_t *sampv, } -static int plc(struct audec_state *st, int16_t *sampv, size_t *sampc) +static int plc(struct audec_state *st, int fmt, void *sampv, size_t *sampc) { + if (fmt != AUFMT_S16LE) + return ENOTSUP; + BV32_PLC(&st->ds, sampv); *sampc = NSAMP; diff --git a/modules/cairo/cairo.c b/modules/cairo/cairo.c index 2af2a12..2422d6b 100644 --- a/modules/cairo/cairo.c +++ b/modules/cairo/cairo.c @@ -129,7 +129,7 @@ static void draw_logo(struct vidsrc_st *st) } -static void process(struct vidsrc_st *st) +static void process(struct vidsrc_st *st, uint64_t timestamp) { struct vidframe f; unsigned xoffs = 2, yoffs = 24; @@ -138,9 +138,12 @@ static void process(struct vidsrc_st *st) draw_text(st, xoffs, yoffs + FONT_SIZE, "%H", fmt_gmtime, NULL); - draw_text(st, xoffs, yoffs + FONT_SIZE*2, "%u x %u @ %d fps", + draw_text(st, xoffs, yoffs + FONT_SIZE*2, "%u x %u @ %.2f fps", st->size.w, st->size.h, st->prm.fps); + draw_text(st, xoffs, yoffs + FONT_SIZE*3, "Time: %.3f sec", + timestamp / (double)VIDEO_TIMEBASE); + draw_logo(st); st->step += 0.02 / st->prm.fps; @@ -148,29 +151,33 @@ static void process(struct vidsrc_st *st) vidframe_init_buf(&f, VID_FMT_RGB32, &st->size, cairo_image_surface_get_data(st->surface)); - st->frameh(&f, st->arg); + st->frameh(&f, timestamp, st->arg); } static void *read_thread(void *arg) { struct vidsrc_st *st = arg; - uint64_t ts = 0; + uint64_t ts = 0, ts_start; while (st->run) { uint64_t now; + uint64_t timestamp; sys_msleep(2); now = tmr_jiffies(); - if (!ts) - ts = now; + if (!ts) { + ts = ts_start = now; + } if (ts > now) continue; - process(st); + timestamp = (ts - ts_start) * VIDEO_TIMEBASE / 1000; + + process(st, timestamp); ts += 1000/st->prm.fps; } @@ -227,7 +234,7 @@ static int load_logo(struct vidsrc_st *st, const char *filename) cairo_set_source_surface(st->cr_logo, logo, 0, 0); cairo_paint(st->cr_logo); - info("cairo: scaling logo '%s' from %d x %d to %f x %f\n", + info("cairo: scaling logo '%s' from %d x %d to %.1f x %.1f\n", filename, cairo_image_surface_get_width(logo), cairo_image_surface_get_height(logo), diff --git a/modules/codec2/codec2.c b/modules/codec2/codec2.c index b6911dc..44e8e43 100644 --- a/modules/codec2/codec2.c +++ b/modules/codec2/codec2.c @@ -123,7 +123,7 @@ static int decode_update(struct audec_state **adsp, static int encode(struct auenc_state *aes, uint8_t *buf, - size_t *len, const int16_t *sampv, size_t sampc) + size_t *len, int fmt, const void *sampv, size_t sampc) { if (!buf || !len || !sampv) return EINVAL; @@ -133,6 +133,9 @@ static int encode(struct auenc_state *aes, uint8_t *buf, if (sampc != (size_t)codec2_samples_per_frame(aes->c2)) return EPROTO; + if (fmt != AUFMT_S16LE) + return ENOTSUP; + codec2_encode(aes->c2, buf, (short *)sampv); *len = codec2_bits_per_frame(aes->c2)/8; @@ -141,7 +144,7 @@ static int encode(struct auenc_state *aes, uint8_t *buf, } -static int decode(struct audec_state *ads, int16_t *sampv, +static int decode(struct audec_state *ads, int fmt, void *sampv, size_t *sampc, const uint8_t *buf, size_t len) { if (!sampv || !sampc || !buf) @@ -152,6 +155,9 @@ static int decode(struct audec_state *ads, int16_t *sampv, if (len < (size_t)codec2_bits_per_frame(ads->c2)/8) return EPROTO; + if (fmt != AUFMT_S16LE) + return ENOTSUP; + codec2_decode(ads->c2, sampv, buf); *sampc = codec2_samples_per_frame(ads->c2); diff --git a/modules/ctrl_tcp/ctrl_tcp.c b/modules/ctrl_tcp/ctrl_tcp.c new file mode 100644 index 0000000..713f9f4 --- /dev/null +++ b/modules/ctrl_tcp/ctrl_tcp.c @@ -0,0 +1,377 @@ +/** + * @file ctrl_tcp.c TCP control interface using JSON payload + * + * Copyright (C) 2018 46 Labs LLC + */ + +#include <re.h> +#include <baresip.h> + +#include "tcp_netstring.h" + + +/** + * @defgroup ctrl_tcp ctrl_tcp + * + * Communication channel to control and monitor Baresip via JSON messages. + * + * It receives commands to be executed, sends back command responses and + * notifies about events. + * + * Command message parameters: + * + * - command : Command to be executed. + * - params : Command parameters. + * - token : Optional. Included in the response if present. + * + * Command message example: + * + \verbatim + { + "command" : "dial", + "params" : "sip:alice@atlanta.com", + "token" : "qwerasdf" + } + \endverbatim + * + * + * Response message parameters: + * + * - response : true. Identifies the message type. + * - ok: : true/false. Indicates whether the command execution succeeded. + * - data : Baresip response to the related command execution. + * - token : Present if it was included in the related command request. + * + * Response message example: + * + \verbatim + { + "response" : true, + "ok" : true, + "data" : "", + "token" : "qwerasdf" + } + \endverbatim + * + * + * Event message parameters: + * + * - event : true. Identifies the message type. + * - class : Event class. + * - type : Event ID. + * - param : Specific event information. + * + * Apart from the above, events may contain aditional parameters. + * + * Event message example: + * + \verbatim + { + "event" : "true", + "class" : "call", + "type" : "CALL_CLOSED", + "param" : "Connection reset by peer", + "accountaor" : "sip:alice@atlanta.com", + "direction" : "incoming", + "peeruri" : "sip:bob@biloxy.com", + "id" : "73a12546589651f8" + } + \endverbatim + * + * + * Sample config: + * + \verbatim + ctrl_tcp_listen 0.0.0.0:4444 # IP-address and port to listen on + \endverbatim + */ + + +enum {CTRL_PORT = 4444}; + +struct ctrl_st { + struct tcp_sock *ts; + struct tcp_conn *tc; + struct netstring *ns; +}; + +static struct ctrl_st *ctrl = NULL; /* allow only one instance */ + +static int print_handler(const char *p, size_t size, void *arg) +{ + struct mbuf *mb = arg; + + return mbuf_write_mem(mb, (uint8_t *)p, size); +} + + +static int encode_response(int cmd_error, struct mbuf *resp, const char *token) +{ + struct re_printf pf = {print_handler, resp}; + struct odict *od = NULL; + char *buf = NULL; + char m[256]; + int err; + + /* Empty response. */ + if (resp->pos == NETSTRING_HEADER_SIZE) + { + buf = mem_alloc(1, NULL); + buf[0] = '\0'; + } + else + { + resp->pos = NETSTRING_HEADER_SIZE; + err = mbuf_strdup(resp, &buf, + resp->end - NETSTRING_HEADER_SIZE); + if (err) + return err; + } + + err = odict_alloc(&od, 8); + if (err) + return err; + + err |= odict_entry_add(od, "response", ODICT_BOOL, true); + err |= odict_entry_add(od, "ok", ODICT_BOOL, (bool)!cmd_error); + + if (cmd_error && str_len(buf) == 0) + err |= odict_entry_add(od, "data", ODICT_STRING, + str_error(cmd_error, m, sizeof(m))); + else + err |= odict_entry_add(od, "data", ODICT_STRING, buf); + + if (token) + err |= odict_entry_add(od, "token", ODICT_STRING, token); + + if (err) + goto out; + + mbuf_reset(resp); + mbuf_init(resp); + resp->pos = NETSTRING_HEADER_SIZE; + + err = json_encode_odict(&pf, od); + if (err) + warning("ctrl_tcp: failed to encode response JSON (%m)\n", + err); + + out: + mem_deref(buf); + mem_deref(od); + + return err; +} + + +static bool command_handler(struct mbuf *mb, void *arg) +{ + struct ctrl_st *st = arg; + struct mbuf *resp = mbuf_alloc(2048); + struct re_printf pf = {print_handler, resp}; + struct odict *od = NULL; + const struct odict_entry *oe_cmd, *oe_prm, *oe_tok; + char buf[256]; + int err; + + err = json_decode_odict(&od, 32, (const char*)mb->buf, mb->end, 16); + if (err) { + warning("ctrl_tcp: failed to decode JSON (%m)\n", err); + goto out; + } + + oe_cmd = odict_lookup(od, "command"); + oe_prm = odict_lookup(od, "params"); + oe_tok = odict_lookup(od, "token"); + if (!oe_cmd) { + warning("ctrl_tcp: missing json entries\n"); + goto out; + } + + debug("ctrl_tcp: handle_command: cmd='%s', params:'%s', token='%s'\n", + oe_cmd ? oe_cmd->u.str : "", + oe_prm ? oe_prm->u.str : "", + oe_tok ? oe_tok->u.str : ""); + + re_snprintf(buf, sizeof(buf), "%s%s%s", + oe_cmd->u.str, + oe_prm ? " " : "", + oe_prm ? oe_prm->u.str : ""); + + resp->pos = NETSTRING_HEADER_SIZE; + + /* Relay message to long commands */ + err = cmd_process_long(baresip_commands(), + buf, + str_len(buf), + &pf, NULL); + if (err) { + warning("ctrl_tcp: error processing command (%m)\n", err); + } + + err = encode_response(err, resp, oe_tok ? oe_tok->u.str : NULL); + if (err) { + warning("ctrl_tcp: failed to encode response (%m)\n", err); + goto out; + } + + resp->pos = NETSTRING_HEADER_SIZE; + err = tcp_send(st->tc, resp); + if (err) { + warning("ctrl_tcp: failed to send the message (%m)\n", err); + } + + out: + mem_deref(resp); + mem_deref(od); + + return true; /* always handled */ +} + + +static void tcp_close_handler(int err, void *arg) +{ + struct ctrl_st *st = arg; + + (void)err; + + st->tc = mem_deref(st->tc); +} + + +static void tcp_conn_handler(const struct sa *peer, void *arg) +{ + struct ctrl_st *st = arg; + + (void)peer; + + /* only one connection allowed */ + st->tc = mem_deref(st->tc); + st->ns = mem_deref(st->ns); + + (void)tcp_accept(&st->tc, st->ts, NULL, NULL, tcp_close_handler, st); + (void)netstring_insert(&st->ns, st->tc, 0, command_handler, st); +} + + +/* + * Relay UA events + */ +static void ua_event_handler(struct ua *ua, enum ua_event ev, + struct call *call, const char *prm, void *arg) +{ + struct ctrl_st *st = arg; + struct mbuf *buf = mbuf_alloc(1024); + struct re_printf pf = {print_handler, buf}; + struct odict *od = NULL; + int err; + + buf->pos = NETSTRING_HEADER_SIZE; + + err = odict_alloc(&od, 8); + if (err) + return; + + err = odict_entry_add(od, "event", ODICT_BOOL, true); + err |= event_encode_dict(od, ua, ev, call, prm); + if (err) + goto out; + + err = json_encode_odict(&pf, od); + if (err) { + warning("ctrl_tcp: failed to encode json (%m)\n", err); + goto out; + } + + if (st->tc) { + buf->pos = NETSTRING_HEADER_SIZE; + err = tcp_send(st->tc, buf); + if (err) { + warning("ctrl_tcp: failed to send the message (%m)\n", + err); + } + } + + out: + mem_deref(buf); + mem_deref(od); +} + + +static void ctrl_destructor(void *arg) +{ + struct ctrl_st *st = arg; + + mem_deref(st->tc); + mem_deref(st->ts); + mem_deref(st->ns); +} + + +static int ctrl_alloc(struct ctrl_st **stp, const struct sa *laddr) +{ + struct ctrl_st *st; + int err; + + if (!stp) + return EINVAL; + + st = mem_zalloc(sizeof(*st), ctrl_destructor); + if (!st) + return ENOMEM; + + err = tcp_listen(&st->ts, laddr, tcp_conn_handler, st); + if (err) { + warning("ctrl_tcp: failed to listen on TCP %J (%m)\n", + laddr, err); + goto out; + } + + debug("ctrl_tcp: TCP socket listening on %J\n", laddr); + + out: + if (err) + mem_deref(st); + else + *stp = st; + + return err; +} + + +static int ctrl_init(void) +{ + struct sa laddr; + int err; + + if (conf_get_sa(conf_cur(), "ctrl_tcp_listen", &laddr)) { + sa_set_str(&laddr, "0.0.0.0", CTRL_PORT); + } + + err = ctrl_alloc(&ctrl, &laddr); + if (err) + return err; + + err = uag_event_register(ua_event_handler, ctrl); + if (err) + return err; + + return 0; +} + + +static int ctrl_close(void) +{ + uag_event_unregister(ua_event_handler); + ctrl = mem_deref(ctrl); + + return 0; +} + + +const struct mod_export DECL_EXPORTS(ctrl_tcp) = { + "ctrl_tcp", + "application", + ctrl_init, + ctrl_close +}; diff --git a/modules/ctrl_tcp/module.mk b/modules/ctrl_tcp/module.mk new file mode 100644 index 0000000..cf6f0f3 --- /dev/null +++ b/modules/ctrl_tcp/module.mk @@ -0,0 +1,10 @@ +# +# module.mk +# +# Copyright (C) 2018 46 Labs LLC +# + +MOD := ctrl_tcp +$(MOD)_SRCS += ctrl_tcp.c tcp_netstring.c ./netstring/netstring.c + +include mk/mod.mk diff --git a/modules/ctrl_tcp/netstring/netstring.c b/modules/ctrl_tcp/netstring/netstring.c new file mode 100644 index 0000000..0309872 --- /dev/null +++ b/modules/ctrl_tcp/netstring/netstring.c @@ -0,0 +1,164 @@ +/* Streaming API for netstrings. */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <math.h> +#include <re.h> +#include "netstring.h" + + +const char* netstring_error_str(netstring_error err) +{ + switch (err) { + case NETSTRING_ERROR_TOO_LONG: + return "NETSTRING_ERROR_TOO_LONG"; + case NETSTRING_ERROR_NO_COLON: + return "NETSTRING_ERROR_NO_COLON"; + case NETSTRING_ERROR_TOO_SHORT: + return "NETSTRING_ERROR_TOO_SHORT"; + case NETSTRING_ERROR_NO_COMMA: + return "NETSTRING_ERROR_NO_COMMA"; + case NETSTRING_ERROR_LEADING_ZERO: + return "NETSTRING_ERROR_LEADING_ZERO"; + case NETSTRING_ERROR_NO_LENGTH: + return "NETSTRING_ERROR_NO_LENGTH"; + default: + return "NETSTRING_ERROR_UNKNOWN"; + } +} + + +/** + * Reads a netstring from a `buffer` of length `buffer_length`. Writes + * to `netstring_start` a pointer to the beginning of the string in + * the buffer, and to `netstring_length` the length of the + * string. Does not allocate any memory. If it reads successfully, + * then it returns 0. If there is an error, then the return value will + * be negative. The error values are: + + * NETSTRING_ERROR_TOO_LONG More than 999999999 bytes in a field + * NETSTRING_ERROR_NO_COLON No colon was found after the number + * NETSTRING_ERROR_TOO_SHORT Number of bytes greater than buffer length + * NETSTRING_ERROR_NO_COMMA No comma was found at the end + * NETSTRING_ERROR_LEADING_ZERO Leading zeros are not allowed + * NETSTRING_ERROR_NO_LENGTH Length not given at start of netstring + + * If you're sending messages with more than 999999999 bytes -- about + * 2 GB -- then you probably should not be doing so in the form of a + * single netstring. This restriction is in place partially to protect + * from malicious or erroneous input, and partly to be compatible with + * D. J. Bernstein's reference implementation. + + * Example: + * if (netstring_read("3:foo,", 6, &str, &len) < 0) explode_and_die(); + */ +int netstring_read(char *buffer, size_t buffer_length, + char **netstring_start, size_t *netstring_length) +{ + size_t i; + size_t len = 0; + + /* Write default values for outputs */ + *netstring_start = NULL; *netstring_length = 0; + + /* Make sure buffer is big enough. Minimum size is 3. */ + if (buffer_length < 3) + return NETSTRING_ERROR_TOO_SHORT; + + /* No leading zeros allowed! */ + if (buffer[0] == '0' && isdigit(buffer[1])) + return NETSTRING_ERROR_LEADING_ZERO; + + /* The netstring must start with a number */ + if (!isdigit(buffer[0])) + return NETSTRING_ERROR_NO_LENGTH; + + /* Read the number of bytes */ + for (i = 0; i < buffer_length && isdigit(buffer[i]); i++) { + + /* Error if more than 9 digits */ + if (i >= 9) + return NETSTRING_ERROR_TOO_LONG; + + /* Accumulate each digit, assuming ASCII. */ + len = len*10 + (buffer[i] - '0'); + } + + /** + * Check buffer length. The buffer must be longer than the sum of: + * - the number we've read. + * - the length of the string itself. + * - the colon. + * - the comma. + */ + if (i + len + 1 >= buffer_length) + return NETSTRING_ERROR_TOO_SHORT; + + /* Read the colon */ + if (buffer[i++] != ':') + return NETSTRING_ERROR_NO_COLON; + + /* Test for the trailing comma, and set the return values */ + if (buffer[i + len] != ',') + return NETSTRING_ERROR_NO_COMMA; + + *netstring_start = &buffer[i]; *netstring_length = len; + + return 0; +} + +/** + * Return the number of digits represented in the given number. + * We are assuming that the input is not bigger than NETSTRING_MAX_SIZE. + */ +size_t netstring_num_len(size_t num) +{ + char num_str[32]; + + re_snprintf(num_str, sizeof(num_str), "%zu", num); + + return strlen(num_str); +} + +/** + * Return the length, in ASCII characters, of a netstring containing + * `data_length` bytes. + */ +size_t netstring_buffer_size(size_t data_length) +{ + if (data_length == 0) + return 3; + + return netstring_num_len(data_length) + data_length + 2; +} + +/* + * Allocate and create a netstring containing the first `len` bytes of `data`. + * This must be manually freed by the client. + * If `len` is 0 then no data will be read from `data`, and it may be NULL. + */ +size_t netstring_encode_new(char **netstring, char *data, size_t len) +{ + char *ns; + size_t num_len = 1; + + if (len == 0) { + ns = malloc(3); + ns[0] = '0'; + ns[1] = ':'; + ns[2] = ','; + } + else { + num_len = netstring_num_len(len); + ns = malloc(num_len + len + 2); + sprintf(ns, "%lu:", (unsigned long)len); + memcpy(ns + num_len + 1, data, len); + ns[num_len + len + 1] = ','; + } + + *netstring = ns; + + return num_len + len + 2; +} diff --git a/modules/ctrl_tcp/netstring/netstring.h b/modules/ctrl_tcp/netstring/netstring.h new file mode 100644 index 0000000..a084428 --- /dev/null +++ b/modules/ctrl_tcp/netstring/netstring.h @@ -0,0 +1,28 @@ +#ifndef __NETSTRING_STREAM_H +#define __NETSTRING_STREAM_H + +#include <string.h> + +const char* netstring_error_str(int err); + +int netstring_read(char *buffer, size_t buffer_length, + char **netstring_start, size_t *netstring_length); + +size_t netstring_num_len(size_t num); +size_t netstring_buffer_size(size_t data_length); + +size_t netstring_encode_new(char **netstring, char *data, size_t len); + +#define NETSTRING_MAX_SIZE 999999999 + +/* Errors that can occur during netstring parsing */ +typedef enum { + NETSTRING_ERROR_TOO_LONG = -100, + NETSTRING_ERROR_NO_COLON, + NETSTRING_ERROR_TOO_SHORT, + NETSTRING_ERROR_NO_COMMA, + NETSTRING_ERROR_LEADING_ZERO, + NETSTRING_ERROR_NO_LENGTH +} netstring_error; + +#endif diff --git a/modules/ctrl_tcp/tcp_netstring.c b/modules/ctrl_tcp/tcp_netstring.c new file mode 100644 index 0000000..2ad4742 --- /dev/null +++ b/modules/ctrl_tcp/tcp_netstring.c @@ -0,0 +1,221 @@ +/** + * @file tcp_netstring.c TCP netstring framing + * + * Copyright (C) 2018 46 Labs LLC + */ + +#include <math.h> +#include <string.h> + +#include <re_types.h> +#include <re_fmt.h> +#include <re_mem.h> +#include <re_mbuf.h> +#include <re_tcp.h> +#include <re_net.h> + +#include "tcp_netstring.h" +#include "netstring/netstring.h" + + +#define DEBUG_MODULE "tcp_netstring" +#define DEBUG_LEVEL 5 +#include <re_dbg.h> + + +struct netstring { + struct tcp_conn *tc; + struct tcp_helper *th; + struct mbuf *mb; + netstring_frame_h *frameh; + void *arg; + + uint64_t n_tx; + uint64_t n_rx; +}; + + +/* responsible for adding the netstring header + - assumes that the sent MBUF contains a complete packet + */ +static bool netstring_send_handler(int *err, struct mbuf *mb, void *arg) +{ + struct netstring *netstring = arg; + size_t num_len; + char num_str[32]; + + if (mb->pos < NETSTRING_HEADER_SIZE) { + DEBUG_WARNING("send: not enough space for netstring header\n"); + *err = ENOMEM; + return true; + } + + if (mbuf_get_left(mb) > NETSTRING_MAX_SIZE) { + DEBUG_WARNING("send: buffer exceeds max size\n"); + *err = EMSGSIZE; + return true; + } + + /* Build the netstring. */ + if (mbuf_get_left(mb) == 0) { + mb->buf[0] = '0'; + mb->buf[1] = ':'; + mb->buf[2] = ','; + + mb->end += 3; + + return false; + } + + re_snprintf(num_str, sizeof(num_str), "%zu", mbuf_get_left(mb)); + num_len = strlen(num_str); + + mb->pos = NETSTRING_HEADER_SIZE - (num_len + 1); + mbuf_write_mem(mb, (uint8_t*) num_str, num_len); + mb->pos = NETSTRING_HEADER_SIZE - (num_len + 1); + mb->buf[mb->pos + num_len] = ':'; + mb->buf[mb->end] = ','; + + mb->end += 1; + + ++netstring->n_tx; + + return false; +} + + +static bool netstring_recv_handler(int *errp, struct mbuf *mbx, bool *estab, + void *arg) +{ + struct netstring *netstring = arg; + int err = 0; + size_t pos = 0; + (void)estab; + + /* handle re-assembly */ + if (!netstring->mb) { + netstring->mb = mbuf_alloc(1024); + if (!netstring->mb) { + *errp = ENOMEM; + return true; + } + } + + pos = netstring->mb->pos; + + netstring->mb->pos = netstring->mb->end; + + err = mbuf_write_mem(netstring->mb, mbuf_buf(mbx), + mbuf_get_left(mbx)); + if (err) + goto out; + + netstring->mb->pos = pos; + + /* extract all NETSTRING-frames in the TCP-stream */ + for (;;) { + + size_t len, end; + struct mbuf mb; + + if (mbuf_get_left(netstring->mb) < (3)) + break; + + err = netstring_read((char*)netstring->mb->buf, + netstring->mb->end, + (char**)&mb.buf, &len); + if (err) { + + if (err == NETSTRING_ERROR_TOO_SHORT) { + DEBUG_INFO("receive: %s\n", + netstring_error_str(err)); + } + + else { + DEBUG_WARNING("receive: %s\n", + netstring_error_str(err)); + netstring->mb = mem_deref(netstring->mb); + } + + return false; + } + + pos = netstring->mb->pos; + end = netstring->mb->end; + + netstring->mb->end = pos + len; + + ++netstring->n_rx; + + netstring->frameh(&mb, netstring->arg); + + netstring->mb->pos = pos + netstring_buffer_size(len); + netstring->mb->end = end; + + if (netstring->mb->pos >= netstring->mb->end) { + netstring->mb = mem_deref(netstring->mb); + break; + } + + continue; + } + + out: + if (err) + *errp = err; + + return true; /* always handled */ +} + + +static void destructor(void *arg) +{ + struct netstring *netstring = arg; + + mem_deref(netstring->th); + mem_deref(netstring->tc); + mem_deref(netstring->mb); +} + + +int netstring_insert(struct netstring **netstringp, struct tcp_conn *tc, + int layer, netstring_frame_h *frameh, void *arg) +{ + struct netstring *netstring; + int err; + + if (!netstringp || !tc || !frameh) + return EINVAL; + + netstring = mem_zalloc(sizeof(*netstring), destructor); + if (!netstring) + return ENOMEM; + + netstring->tc = mem_ref(tc); + err = tcp_register_helper(&netstring->th, tc, layer, NULL, + netstring_send_handler, + netstring_recv_handler, netstring); + if (err) + goto out; + + netstring->frameh = frameh; + netstring->arg = arg; + + out: + if (err) + mem_deref(netstring); + else + *netstringp = netstring; + + return err; +} + + +int netstring_debug(struct re_printf *pf, const struct netstring *netstring) +{ + if (!netstring) + return 0; + + return re_hprintf(pf, "tx=%llu, rx=%llu", + netstring->n_tx, netstring->n_rx); +} diff --git a/modules/ctrl_tcp/tcp_netstring.h b/modules/ctrl_tcp/tcp_netstring.h new file mode 100644 index 0000000..8195217 --- /dev/null +++ b/modules/ctrl_tcp/tcp_netstring.h @@ -0,0 +1,16 @@ +/** + * @file tcp_netstring.h TCP netstring framing + * + * Copyright (C) 2018 46 Labs LLC + */ + +enum {NETSTRING_HEADER_SIZE = 10}; + +struct netstring; + +typedef bool (netstring_frame_h)(struct mbuf *mb, void *arg); + + +int netstring_insert(struct netstring **netstringp, struct tcp_conn *tc, + int layer, netstring_frame_h *frameh, void *arg); +int netstring_debug(struct re_printf *pf, const struct netstring *netstring); diff --git a/modules/dshow/dshow.cpp b/modules/dshow/dshow.cpp index d081f69..46aef3e 100644 --- a/modules/dshow/dshow.cpp +++ b/modules/dshow/dshow.cpp @@ -99,12 +99,13 @@ public: STDMETHOD(BufferCB) (double sample_time, BYTE *buf, long buf_len) { struct vidframe vidframe; + uint64_t timestamp = sample_time * VIDEO_TIMEBASE; /* XXX: should be VID_FMT_BGR24 */ vidframe_init_buf(&vidframe, VID_FMT_RGB32, &src->size, buf); if (src->frameh) - src->frameh(&vidframe, src->arg); + src->frameh(&vidframe, timestamp, src->arg); return S_OK; } diff --git a/modules/fakevideo/fakevideo.c b/modules/fakevideo/fakevideo.c index e0552d6..dcbd0e5 100644 --- a/modules/fakevideo/fakevideo.c +++ b/modules/fakevideo/fakevideo.c @@ -31,9 +31,14 @@ struct vidsrc_st { const struct vidsrc *vs; /* inheritance */ struct vidframe *frame; +#ifdef HAVE_PTHREAD pthread_t thread; bool run; - int fps; +#else + struct tmr tmr; +#endif + uint64_t ts; + double fps; vidsrc_frame_h *frameh; void *arg; }; @@ -47,35 +52,63 @@ static struct vidsrc *vidsrc; static struct vidisp *vidisp; +static void process_frame(struct vidsrc_st *st) +{ + st->ts += (VIDEO_TIMEBASE / st->fps); + + st->frameh(st->frame, st->ts, st->arg); +} + + +#ifdef HAVE_PTHREAD static void *read_thread(void *arg) { struct vidsrc_st *st = arg; - uint64_t ts = tmr_jiffies(); + + st->ts = tmr_jiffies_usec(); while (st->run) { - if (tmr_jiffies() < ts) { + if (tmr_jiffies_usec() < st->ts) { sys_msleep(4); continue; } - st->frameh(st->frame, st->arg); - - ts += (1000/st->fps); + process_frame(st); } return NULL; } +#else +static void tmr_handler(void *arg) +{ + struct vidsrc_st *st = arg; + const uint64_t now = tmr_jiffies_usec(); + + tmr_start(&st->tmr, 4, tmr_handler, st); + + if (!st->ts) + st->ts = now; + + if (now >= st->ts) { + process_frame(st); + } +} +#endif static void src_destructor(void *arg) { struct vidsrc_st *st = arg; +#ifdef HAVE_PTHREAD if (st->run) { st->run = false; pthread_join(st->thread, NULL); } +#else + tmr_cancel(&st->tmr); +#endif mem_deref(st->frame); } @@ -95,6 +128,7 @@ static int src_alloc(struct vidsrc_st **stp, const struct vidsrc *vs, vidsrc_error_h *errorh, void *arg) { struct vidsrc_st *st; + unsigned x; int err; (void)ctx; @@ -118,12 +152,31 @@ static int src_alloc(struct vidsrc_st **stp, const struct vidsrc *vs, if (err) goto out; + /* Pattern of three vertical bars in RGB */ + for (x=0; x<size->w; x++) { + + uint8_t r=0, g=0, b=0; + + if (x < size->w/3) + r = 255; + else if (x < size->w*2/3) + g = 255; + else + b = 255; + + vidframe_draw_vline(st->frame, x, 0, size->h, r, g, b); + } + +#ifdef HAVE_PTHREAD st->run = true; err = pthread_create(&st->thread, NULL, read_thread, st); if (err) { st->run = false; goto out; } +#else + tmr_start(&st->tmr, 1, tmr_handler, st); +#endif out: if (err) diff --git a/modules/g711/g711.c b/modules/g711/g711.c index 7c01b61..e72f117 100644 --- a/modules/g711/g711.c +++ b/modules/g711/g711.c @@ -17,8 +17,10 @@ static int pcmu_encode(struct auenc_state *aes, uint8_t *buf, - size_t *len, const int16_t *sampv, size_t sampc) + size_t *len, int fmt, const void *sampv, size_t sampc) { + const int16_t *p = sampv; + (void)aes; if (!buf || !len || !sampv) @@ -27,18 +29,23 @@ static int pcmu_encode(struct auenc_state *aes, uint8_t *buf, if (*len < sampc) return ENOMEM; + if (fmt != AUFMT_S16LE) + return ENOTSUP; + *len = sampc; while (sampc--) - *buf++ = g711_pcm2ulaw(*sampv++); + *buf++ = g711_pcm2ulaw(*p++); return 0; } -static int pcmu_decode(struct audec_state *ads, int16_t *sampv, +static int pcmu_decode(struct audec_state *ads, int fmt, void *sampv, size_t *sampc, const uint8_t *buf, size_t len) { + int16_t *p = sampv; + (void)ads; if (!sampv || !sampc || !buf) @@ -47,18 +54,23 @@ static int pcmu_decode(struct audec_state *ads, int16_t *sampv, if (*sampc < len) return ENOMEM; + if (fmt != AUFMT_S16LE) + return ENOTSUP; + *sampc = len; while (len--) - *sampv++ = g711_ulaw2pcm(*buf++); + *p++ = g711_ulaw2pcm(*buf++); return 0; } static int pcma_encode(struct auenc_state *aes, uint8_t *buf, - size_t *len, const int16_t *sampv, size_t sampc) + size_t *len, int fmt, const void *sampv, size_t sampc) { + const int16_t *p = sampv; + (void)aes; if (!buf || !len || !sampv) @@ -67,18 +79,23 @@ static int pcma_encode(struct auenc_state *aes, uint8_t *buf, if (*len < sampc) return ENOMEM; + if (fmt != AUFMT_S16LE) + return ENOTSUP; + *len = sampc; while (sampc--) - *buf++ = g711_pcm2alaw(*sampv++); + *buf++ = g711_pcm2alaw(*p++); return 0; } -static int pcma_decode(struct audec_state *ads, int16_t *sampv, +static int pcma_decode(struct audec_state *ads, int fmt, void *sampv, size_t *sampc, const uint8_t *buf, size_t len) { + int16_t *p = sampv; + (void)ads; if (!sampv || !sampc || !buf) @@ -87,10 +104,13 @@ static int pcma_decode(struct audec_state *ads, int16_t *sampv, if (*sampc < len) return ENOMEM; + if (fmt != AUFMT_S16LE) + return ENOTSUP; + *sampc = len; while (len--) - *sampv++ = g711_alaw2pcm(*buf++); + *p++ = g711_alaw2pcm(*buf++); return 0; } diff --git a/modules/g722/g722.c b/modules/g722/g722.c index 16d1f98..dd2a943 100644 --- a/modules/g722/g722.c +++ b/modules/g722/g722.c @@ -7,6 +7,7 @@ #include <stdlib.h> #include <string.h> #include <re.h> +#include <rem_au.h> #include <baresip.h> #define SPANDSP_EXPOSE_INTERNAL_STRUCTURES 1 #include <spandsp.h> @@ -125,10 +126,13 @@ static int decode_update(struct audec_state **adsp, static int encode(struct auenc_state *st, uint8_t *buf, size_t *len, - const int16_t *sampv, size_t sampc) + int fmt, const void *sampv, size_t sampc) { int n; + if (fmt != AUFMT_S16LE) + return ENOTSUP; + n = g722_encode(&st->enc, buf, sampv, (int)sampc); if (n <= 0) { return EPROTO; @@ -143,7 +147,7 @@ static int encode(struct auenc_state *st, uint8_t *buf, size_t *len, } -static int decode(struct audec_state *st, int16_t *sampv, size_t *sampc, +static int decode(struct audec_state *st, int fmt, void *sampv, size_t *sampc, const uint8_t *buf, size_t len) { int n; @@ -151,6 +155,9 @@ static int decode(struct audec_state *st, int16_t *sampv, size_t *sampc, if (!st || !sampv || !buf) return EINVAL; + if (fmt != AUFMT_S16LE) + return ENOTSUP; + n = g722_decode(&st->dec, sampv, buf, (int)len); if (n < 0) return EPROTO; diff --git a/modules/g7221/decode.c b/modules/g7221/decode.c index 0f7155f..298a8ae 100644 --- a/modules/g7221/decode.c +++ b/modules/g7221/decode.c @@ -48,7 +48,8 @@ int g7221_decode_update(struct audec_state **adsp, const struct aucodec *ac, } -int g7221_decode(struct audec_state *ads, int16_t *sampv, size_t *sampc, +int g7221_decode(struct audec_state *ads, + int fmt, void *sampv, size_t *sampc, const uint8_t *buf, size_t len) { size_t framec; diff --git a/modules/g7221/encode.c b/modules/g7221/encode.c index 8345f5f..8dec82f 100644 --- a/modules/g7221/encode.c +++ b/modules/g7221/encode.c @@ -50,7 +50,7 @@ int g7221_encode_update(struct auenc_state **aesp, const struct aucodec *ac, int g7221_encode(struct auenc_state *aes, uint8_t *buf, size_t *len, - const int16_t *sampv, size_t sampc) + int fmt, const void *sampv, size_t sampc) { size_t framec; diff --git a/modules/g7221/g7221.h b/modules/g7221/g7221.h index 635fc01..2c16d4c 100644 --- a/modules/g7221/g7221.h +++ b/modules/g7221/g7221.h @@ -13,13 +13,14 @@ struct g7221_aucodec { int g7221_encode_update(struct auenc_state **aesp, const struct aucodec *ac, struct auenc_param *prm, const char *fmtp); int g7221_encode(struct auenc_state *aes, uint8_t *buf, size_t *len, - const int16_t *sampv, size_t sampc); + int fmt, const void *sampv, size_t sampc); /* Decode */ int g7221_decode_update(struct audec_state **adsp, const struct aucodec *ac, const char *fmtp); -int g7221_decode(struct audec_state *ads, int16_t *sampv, size_t *sampc, +int g7221_decode(struct audec_state *ads, + int fmt, void *sampv, size_t *sampc, const uint8_t *buf, size_t len); diff --git a/modules/g726/g726.c b/modules/g726/g726.c index fd4e462..8334b84 100644 --- a/modules/g726/g726.c +++ b/modules/g726/g726.c @@ -5,6 +5,7 @@ */ #include <re.h> +#include <rem_au.h> #include <baresip.h> #define SPANDSP_EXPOSE_INTERNAL_STRUCTURES 1 #include <spandsp.h> @@ -119,11 +120,14 @@ static int decode_update(struct audec_state **adsp, static int encode(struct auenc_state *st, uint8_t *buf, - size_t *len, const int16_t *sampv, size_t sampc) + size_t *len, int fmt, const void *sampv, size_t sampc) { if (!buf || !len || !sampv) return EINVAL; + if (fmt != AUFMT_S16LE) + return ENOTSUP; + if (*len < MAX_PACKET) return ENOMEM; @@ -133,12 +137,15 @@ static int encode(struct auenc_state *st, uint8_t *buf, } -static int decode(struct audec_state *st, int16_t *sampv, +static int decode(struct audec_state *st, int fmt, void *sampv, size_t *sampc, const uint8_t *buf, size_t len) { if (!sampv || !sampc || !buf) return EINVAL; + if (fmt != AUFMT_S16LE) + return ENOTSUP; + *sampc = g726_decode(&st->st, sampv, buf, (int)len); return 0; diff --git a/modules/gsm/gsm.c b/modules/gsm/gsm.c index be225d8..da7fba7 100644 --- a/modules/gsm/gsm.c +++ b/modules/gsm/gsm.c @@ -5,6 +5,7 @@ */ #include <gsm.h> /* please report if you have problems finding this file */ #include <re.h> +#include <rem_au.h> #include <baresip.h> @@ -113,13 +114,16 @@ static int decode_update(struct audec_state **adsp, static int encode(struct auenc_state *st, uint8_t *buf, size_t *len, - const int16_t *sampv, size_t sampc) + int fmt, const void *sampv, size_t sampc) { if (sampc != FRAME_SIZE) return EPROTO; if (*len < sizeof(gsm_frame)) return ENOMEM; + if (fmt != AUFMT_S16LE) + return ENOTSUP; + gsm_encode(st->enc, (gsm_signal *)sampv, buf); *len = sizeof(gsm_frame); @@ -128,7 +132,7 @@ static int encode(struct auenc_state *st, uint8_t *buf, size_t *len, } -static int decode(struct audec_state *st, int16_t *sampv, size_t *sampc, +static int decode(struct audec_state *st, int fmt, void *sampv, size_t *sampc, const uint8_t *buf, size_t len) { int ret; @@ -138,6 +142,9 @@ static int decode(struct audec_state *st, int16_t *sampv, size_t *sampc, if (len < sizeof(gsm_frame)) return EBADMSG; + if (fmt != AUFMT_S16LE) + return ENOTSUP; + ret = gsm_decode(st->dec, (gsm_byte *)buf, (gsm_signal *)sampv); if (ret) return EPROTO; diff --git a/modules/gst1/gst.c b/modules/gst1/gst.c index 380334a..a500f4c 100644 --- a/modules/gst1/gst.c +++ b/modules/gst1/gst.c @@ -4,6 +4,7 @@ * Copyright (C) 2010 - 2015 Creytiv.com */ #define _DEFAULT_SOURCE 1 +#define _POSIX_C_SOURCE 199309L #include <stdlib.h> #include <string.h> #include <time.h> diff --git a/modules/gst_video/encode.c b/modules/gst_video/encode.c index b6dfe8a..6377785 100644 --- a/modules/gst_video/encode.c +++ b/modules/gst_video/encode.c @@ -169,14 +169,14 @@ static void internal_appsink_new_buffer(GstElement *sink, if (buffer) { GstClockTime ts; - uint32_t rtp_ts; + uint64_t rtp_ts; guint8 *data = GST_BUFFER_DATA(buffer); guint size = GST_BUFFER_SIZE(buffer); ts = GST_BUFFER_TIMESTAMP(buffer); - rtp_ts = (uint32_t)((90000ULL*ts) / 1000000000UL ); + rtp_ts = (uint64_t)((90000ULL*ts) / 1000000000UL ); h264_packetize(rtp_ts, data, size, st->pktsize, st->pkth, st->pkth_arg); diff --git a/modules/gst_video1/encode.c b/modules/gst_video1/encode.c index d0a3d44..0c3a088 100644 --- a/modules/gst_video1/encode.c +++ b/modules/gst_video1/encode.c @@ -123,7 +123,7 @@ static GstFlowReturn appsink_new_sample_cb(GstAppSink *sink, if (sample) { GstClockTime ts; - uint32_t rtp_ts; + uint64_t rtp_ts; buffer = gst_sample_get_buffer(sample); gst_buffer_map( buffer, &info, (GstMapFlags)(GST_MAP_READ) ); @@ -139,7 +139,7 @@ static GstFlowReturn appsink_new_sample_cb(GstAppSink *sink, } else { /* convert from nanoseconds to RTP clock */ - rtp_ts = (uint32_t)((90000ULL * ts) / 1000000000UL); + rtp_ts = (uint64_t)((90000ULL * ts) / 1000000000UL); } h264_packetize(rtp_ts, data, size, st->encoder.pktsize, diff --git a/modules/h265/encode.c b/modules/h265/encode.c index f4835a3..62b16e0 100644 --- a/modules/h265/encode.c +++ b/modules/h265/encode.c @@ -17,7 +17,7 @@ struct videnc_state { x265_param *param; x265_encoder *x265; int64_t pts; - unsigned fps; + double fps; unsigned bitrate; unsigned pktsize; videnc_packet_h *pkth; @@ -36,7 +36,7 @@ static void destructor(void *arg) } -static int set_params(struct videnc_state *st, unsigned fps, unsigned bitrate) +static int set_params(struct videnc_state *st, double fps, unsigned bitrate) { st->param = x265_param_alloc(); if (!st->param) { @@ -140,7 +140,7 @@ static int open_encoder(struct videnc_state *st, const struct vidsz *size) static inline int packetize(bool marker, const uint8_t *buf, size_t len, - size_t maxlen, uint32_t rtp_ts, + size_t maxlen, uint64_t rtp_ts, videnc_packet_h *pkth, void *arg) { int err = 0; @@ -195,7 +195,7 @@ int h265_encode(struct videnc_state *st, bool update, uint32_t i, nalc = 0; int colorspace; int n, err = 0; - uint32_t ts; + uint64_t ts; if (!st || !frame) return EINVAL; diff --git a/modules/l16/l16.c b/modules/l16/l16.c index 204a8f5..3f324c9 100644 --- a/modules/l16/l16.c +++ b/modules/l16/l16.c @@ -4,6 +4,7 @@ * Copyright (C) 2010 - 2015 Creytiv.com */ #include <re.h> +#include <rem.h> #include <baresip.h> @@ -18,9 +19,10 @@ enum {NR_CODECS = 8}; static int encode(struct auenc_state *st, uint8_t *buf, size_t *len, - const int16_t *sampv, size_t sampc) + int fmt, const void *sampv, size_t sampc) { int16_t *p = (void *)buf; + const int16_t *sampv16 = sampv; (void)st; if (!buf || !len || !sampv) @@ -29,19 +31,23 @@ static int encode(struct auenc_state *st, uint8_t *buf, size_t *len, if (*len < sampc*2) return ENOMEM; + if (fmt != AUFMT_S16LE) + return ENOTSUP; + *len = sampc*2; while (sampc--) - *p++ = htons(*sampv++); + *p++ = htons(*sampv16++); return 0; } -static int decode(struct audec_state *st, int16_t *sampv, size_t *sampc, +static int decode(struct audec_state *st, int fmt, void *sampv, size_t *sampc, const uint8_t *buf, size_t len) { int16_t *p = (void *)buf; + int16_t *sampv16 = sampv; (void)st; if (!buf || !len || !sampv) @@ -50,11 +56,14 @@ static int decode(struct audec_state *st, int16_t *sampv, size_t *sampc, if (*sampc < len/2) return ENOMEM; + if (fmt != AUFMT_S16LE) + return ENOTSUP; + *sampc = len/2; len /= 2; while (len--) - *sampv++ = ntohs(*p++); + *sampv16++ = ntohs(*p++); return 0; } diff --git a/modules/menu/menu.c b/modules/menu/menu.c index c7f1646..aab9661 100644 --- a/modules/menu/menu.c +++ b/modules/menu/menu.c @@ -774,6 +774,27 @@ static int set_current_call(struct re_printf *pf, void *arg) } +static int set_audio_bitrate(struct re_printf *pf, void *arg) +{ + struct cmd_arg *carg = arg; + struct call *call; + uint32_t bitrate = atoi(carg->prm); + int err; + + call = ua_call(uag_cur()); + if (call) { + err = re_hprintf(pf, "setting audio bitrate: %u bps\n", + bitrate); + audio_set_bitrate(call_audio(call), bitrate); + } + else { + err = re_hprintf(pf, "call not found\n"); + } + + return err; +} + + static const struct cmd callcmdv[] = { {"reinvite", 'I', 0, "Send re-INVITE", call_reinvite }, {"resume", 'X', 0, "Call resume", cmd_call_resume }, @@ -784,6 +805,7 @@ static const struct cmd callcmdv[] = { {"hold", 'x', 0, "Call hold", cmd_call_hold }, {"", 'H', 0, "Hold previous call", hold_prev_call }, {"", 'L', 0, "Resume previous call",hold_prev_call }, +{"aubitrate", 0, CMD_PRM, "Set audio bitrate", set_audio_bitrate }, #ifdef USE_VIDEO {"video_cycle", 'E', 0, "Cycle video encoder", call_videoenc_cycle }, @@ -1108,7 +1130,13 @@ static int module_init(void) start_ticks = tmr_jiffies(); tmr_init(&tmr_alert); - statmode = STATMODE_CALL; + if (0 == conf_get(conf_cur(), "statmode_default", &val) && + 0 == pl_strcasecmp(&val, "off")) { + statmode = STATMODE_OFF; + } + else { + statmode = STATMODE_CALL; + } err = cmd_register(baresip_commands(), cmdv, ARRAY_SIZE(cmdv)); err |= cmd_register(baresip_commands(), dialcmdv, diff --git a/modules/mpa/decode.c b/modules/mpa/decode.c index 4e2a720..b7aaf41 100644 --- a/modules/mpa/decode.c +++ b/modules/mpa/decode.c @@ -5,6 +5,7 @@ */ #include <re.h> +#include <rem.h> #include <baresip.h> #include <mpg123.h> #include <speex/speex_resampler.h> @@ -110,7 +111,8 @@ int mpa_decode_update(struct audec_state **adsp, const struct aucodec *ac, } -int mpa_decode_frm(struct audec_state *ads, int16_t *sampv, size_t *sampc, +int mpa_decode_frm(struct audec_state *ads, + int fmt, void *sampv_void, size_t *sampc, const uint8_t *buf, size_t len) { int result, channels, encoding, i; @@ -118,6 +120,7 @@ int mpa_decode_frm(struct audec_state *ads, int16_t *sampv, size_t *sampc, size_t n; spx_uint32_t intermediate_len; spx_uint32_t out_len; + int16_t *sampv = sampv_void; #ifdef DEBUG debug("MPA dec start %d %ld\n",len, *sampc); @@ -132,6 +135,9 @@ int mpa_decode_frm(struct audec_state *ads, int16_t *sampv, size_t *sampc, return EPROTO; } + if (fmt != AUFMT_S16LE) + return ENOTSUP; + n = 0; result = mpg123_decode(ads->dec, buf+4, len-4, (unsigned char*)ads->intermediate_buffer, diff --git a/modules/mpa/encode.c b/modules/mpa/encode.c index d13bc0a..8c8968a 100644 --- a/modules/mpa/encode.c +++ b/modules/mpa/encode.c @@ -5,6 +5,7 @@ */ #include <re.h> +#include <rem.h> #include <baresip.h> #include <twolame.h> #include <string.h> @@ -136,7 +137,7 @@ out: int mpa_encode_frm(struct auenc_state *aes, uint8_t *buf, size_t *len, - const int16_t *sampv, size_t sampc) + int fmt, const void *sampv, size_t sampc) { int n; spx_uint32_t intermediate_len,in_len; @@ -144,6 +145,9 @@ int mpa_encode_frm(struct auenc_state *aes, uint8_t *buf, size_t *len, if (!aes || !buf || !len || !sampv) return EINVAL; + if (fmt != AUFMT_S16LE) + return ENOTSUP; + if (aes->resampler) { in_len = (uint32_t)sampc/2; intermediate_len = sizeof(aes->intermediate_buffer) diff --git a/modules/mpa/mpa.h b/modules/mpa/mpa.h index 0db2528..170cc3c 100644 --- a/modules/mpa/mpa.h +++ b/modules/mpa/mpa.h @@ -24,14 +24,15 @@ struct mpa_param { int mpa_encode_update(struct auenc_state **aesp, const struct aucodec *ac, struct auenc_param *prm, const char *fmtp); int mpa_encode_frm(struct auenc_state *aes, uint8_t *buf, size_t *len, - const int16_t *sampv, size_t sampc); + int fmt, const void *sampv, size_t sampc); /* Decode */ int mpa_decode_update(struct audec_state **adsp, const struct aucodec *ac, const char *fmtp); -int mpa_decode_frm(struct audec_state *ads, int16_t *sampv, size_t *sampc, - const uint8_t *buf, size_t len); +int mpa_decode_frm(struct audec_state *ads, + int fmt, void *sampv, size_t *sampc, + const uint8_t *buf, size_t len); /* SDP */ void mpa_decode_fmtp(struct mpa_param *prm, const char *fmtp); diff --git a/modules/mqtt/README.md b/modules/mqtt/README.md index 6a86b84..ace3c36 100644 --- a/modules/mqtt/README.md +++ b/modules/mqtt/README.md @@ -6,7 +6,7 @@ This module implements an MQTT (Message Queue Telemetry Transport) client for publishing and subscribing to topics. -The module is using libmosquitto +The module is using libmosquitto. All messages are encoded in JSON format. Starting the MQTT broker: @@ -19,7 +19,7 @@ $ /usr/local/sbin/mosquitto -v Subscribing to all topics: ``` -$ mosquitto_sub -t /baresip/+ +$ mosquitto_sub -v -t /baresip/# ``` @@ -43,17 +43,16 @@ $ mosquitto_pub -t /baresip/xxx -m foo=42 ## Examples ``` -/baresip/event sip:aeh@iptel.org,REGISTERING -/baresip/event sip:aeh@iptel.org,REGISTER_OK -/baresip/event sip:aeh@iptel.org,SHUTDOWN +/baresip/event {"type":"REGISTERING","class":"register","accountaor":"sip:aeh@iptel.org"} +/baresip/event {"type":"REGISTER_OK","class":"register","accountaor":"sip:aeh@iptel.org","param":"200 OK"} +/baresip/event {"type":"SHUTDOWN","class":"application","accountaor":"sip:aeh@iptel.org"} ``` ``` mosquitto_pub -t /baresip/command -m "/dial music" -/baresip/command /dial music -/baresip/command_resp (null) -/baresip/event sip:aeh@iptel.org,CALL_ESTABLISHED -/baresip/event sip:aeh@iptel.org,CALL_CLOSED +/baresip/command {"command":"dial","params":"music","token":"123"} +/baresip/command_resp/123 (null) +/baresip/event {"type":"CALL_ESTABLISHED","class":"call","accountaor":"sip:aeh@iptel.org","direction":"outgoing","peeruri":"sip:music@iptel.org","id":"4d758140c42c5d55","param":"sip:music@iptel.org"} +/baresip/event {"type":"CALL_CLOSED","class":"call","accountaor":"sip:aeh@iptel.org","direction":"outgoing","peeruri":"sip:music@iptel.org","id":"4d758140c42c5d55","param":"Connection reset by user"} ``` - diff --git a/modules/omx/module.c b/modules/omx/module.c index a5b6fa8..1580f75 100644 --- a/modules/omx/module.c +++ b/modules/omx/module.c @@ -38,7 +38,7 @@ static void destructor(void *arg) } -int omx_vidisp_alloc(struct vidisp_st **vp, const struct vidisp* vd, +int omx_vidisp_alloc(struct vidisp_st **vp, const struct vidisp *vd, struct vidisp_prm *prm, const char *dev, vidisp_resize_h *resizeh, void *arg) { diff --git a/modules/omx/module.mk b/modules/omx/module.mk index 958a1a5..c7a796d 100644 --- a/modules/omx/module.mk +++ b/modules/omx/module.mk @@ -9,10 +9,11 @@ $(MOD)_SRCS += omx.c module.c ifneq ($(USE_OMX_RPI),) $(MOD)_CFLAGS := -DRASPBERRY_PI -DOMX_SKIP64BIT \ - -I/usr/local/include/interface/vmcs_host/linux/ \ - -I /usr/local/include/interface/vcos/pthreads/ \ - -I /opt/vc/include -I /opt/vc/include/interface/vmcs_host/linux \ - -I /opt/vc/include/interface/vcos/pthreads + -isystem /usr/local/include/interface/vmcs_host/linux/ \ + -isystem /usr/local/include/interface/vcos/pthreads/ \ + -isystem /opt/vc/include \ + -isystem /opt/vc/include/interface/vmcs_host/linux \ + -isystem /opt/vc/include/interface/vcos/pthreads $(MOD)_LFLAGS += -lvcos -lbcm_host -lopenmaxil -L /opt/vc/lib endif diff --git a/modules/omx/omx.c b/modules/omx/omx.c index 6b08d19..8a03822 100644 --- a/modules/omx/omx.c +++ b/modules/omx/omx.c @@ -5,6 +5,8 @@ * Copyright (C) 2016 - 2017 Jonathan Sieber */ +#define _POSIX_C_SOURCE 199309L + #include "omx.h" #include <re.h> @@ -19,6 +21,11 @@ #include <time.h> #include <sys/time.h> +#ifdef RASPBERRY_PI +#include <bcm_host.h> +#endif + + /** * @defgroup omx omx * @@ -109,9 +116,10 @@ static struct OMX_CALLBACKTYPE callbacks = { }; -int omx_init(struct omx_state* st) +int omx_init(struct omx_state *st) { OMX_ERRORTYPE err; + #ifdef RASPBERRY_PI bcm_host_init(); #endif @@ -128,11 +136,11 @@ int omx_init(struct omx_state* st) #endif if (!st->video_render || err != 0) { - warning("Failed to create OMX video_render component\n"); + warning("omx: Failed to create OMX video_render component\n"); return ENOENT; } else { - info("created video_render component\n"); + info("omx: created video_render component\n"); return 0; } } @@ -153,8 +161,11 @@ static void block_until_state_changed(OMX_HANDLETYPE hComponent, } -void omx_deinit(struct omx_state* st) +void omx_deinit(struct omx_state *st) { + if (!st) + return; + info("omx_deinit"); OMX_SendCommand(st->video_render, OMX_CommandStateSet, OMX_StateIdle, NULL); @@ -167,13 +178,15 @@ void omx_deinit(struct omx_state* st) } -void omx_display_disable(struct omx_state* st) +void omx_display_disable(struct omx_state *st) { - (void)st; - - #ifdef RASPBERRY_PI +#ifdef RASPBERRY_PI OMX_ERRORTYPE err; OMX_CONFIG_DISPLAYREGIONTYPE config; + + if (!st) + return; + memset(&config, 0, sizeof(OMX_CONFIG_DISPLAYREGIONTYPE)); config.nSize = sizeof(OMX_CONFIG_DISPLAYREGIONTYPE); config.nVersion.nVersion = OMX_VERSION; @@ -187,8 +200,9 @@ void omx_display_disable(struct omx_state* st) if (err != 0) { warning("omx_display_disable command failed"); } - - #endif +#else + (void)st; +#endif } diff --git a/modules/omx/omx.h b/modules/omx/omx.h index 13d8928..6691d95 100644 --- a/modules/omx/omx.h +++ b/modules/omx/omx.h @@ -41,6 +41,6 @@ int omx_display_input_buffer(struct omx_state* st, void** pbuf, uint32_t* plen); int omx_display_flush_buffer(struct omx_state* st); -int omx_display_enable(struct omx_state* st, +int omx_display_enable(struct omx_state *st, int width, int height, int stride); -void omx_display_disable(struct omx_state* st); +void omx_display_disable(struct omx_state *st); diff --git a/modules/opus/decode.c b/modules/opus/decode.c index f2d67b1..a4b8721 100644 --- a/modules/opus/decode.c +++ b/modules/opus/decode.c @@ -5,6 +5,7 @@ */ #include <re.h> +#include <rem.h> #include <baresip.h> #include <opus/opus.h> #include "opus.h" @@ -63,7 +64,8 @@ int opus_decode_update(struct audec_state **adsp, const struct aucodec *ac, } -int opus_decode_frm(struct audec_state *ads, int16_t *sampv, size_t *sampc, +int opus_decode_frm(struct audec_state *ads, + int fmt, void *sampv, size_t *sampc, const uint8_t *buf, size_t len) { int n; @@ -71,11 +73,29 @@ int opus_decode_frm(struct audec_state *ads, int16_t *sampv, size_t *sampc, if (!ads || !sampv || !sampc || !buf) return EINVAL; - n = opus_decode(ads->dec, buf, (opus_int32)len, - sampv, (int)(*sampc/ads->ch), 0); - if (n < 0) { - warning("opus: decode error: %s\n", opus_strerror(n)); - return EPROTO; + switch (fmt) { + + case AUFMT_S16LE: + n = opus_decode(ads->dec, buf, (opus_int32)len, + sampv, (int)(*sampc/ads->ch), 0); + if (n < 0) { + warning("opus: decode error: %s\n", opus_strerror(n)); + return EPROTO; + } + break; + + case AUFMT_FLOAT: + n = opus_decode_float(ads->dec, buf, (opus_int32)len, + sampv, (int)(*sampc/ads->ch), 0); + if (n < 0) { + warning("opus: float decode error: %s\n", + opus_strerror(n)); + return EPROTO; + } + break; + + default: + return ENOTSUP; } *sampc = n * ads->ch; @@ -84,16 +104,33 @@ int opus_decode_frm(struct audec_state *ads, int16_t *sampv, size_t *sampc, } -int opus_decode_pkloss(struct audec_state *ads, int16_t *sampv, size_t *sampc) +int opus_decode_pkloss(struct audec_state *ads, + int fmt, void *sampv, size_t *sampc) { int n; if (!ads || !sampv || !sampc) return EINVAL; - n = opus_decode(ads->dec, NULL, 0, sampv, (int)(*sampc/ads->ch), 0); - if (n < 0) - return EPROTO; + switch (fmt) { + + case AUFMT_S16LE: + n = opus_decode(ads->dec, NULL, 0, + sampv, (int)(*sampc/ads->ch), 0); + if (n < 0) + return EPROTO; + break; + + case AUFMT_FLOAT: + n = opus_decode_float(ads->dec, NULL, 0, + sampv, (int)(*sampc/ads->ch), 0); + if (n < 0) + return EPROTO; + break; + + default: + return ENOTSUP; + } *sampc = n * ads->ch; diff --git a/modules/opus/encode.c b/modules/opus/encode.c index 7ee1ca2..7baf4f8 100644 --- a/modules/opus/encode.c +++ b/modules/opus/encode.c @@ -5,6 +5,7 @@ */ #include <re.h> +#include <rem.h> #include <baresip.h> #include <opus/opus.h> #include "opus.h" @@ -134,6 +135,10 @@ int opus_encode_update(struct auenc_state **aesp, const struct aucodec *ac, fch = prm.stereo ? OPUS_AUTO : 1; vbr = prm.cbr ? 0 : 1; + /* override local bitrate */ + if (param && param->bitrate) + prm.bitrate = param->bitrate; + (void)opus_encoder_ctl(aes->enc, OPUS_SET_MAX_BANDWIDTH(srate2bw(prm.srate))); (void)opus_encoder_ctl(aes->enc, OPUS_SET_BITRATE(prm.bitrate)); @@ -167,18 +172,37 @@ int opus_encode_update(struct auenc_state **aesp, const struct aucodec *ac, int opus_encode_frm(struct auenc_state *aes, uint8_t *buf, size_t *len, - const int16_t *sampv, size_t sampc) + int fmt, const void *sampv, size_t sampc) { opus_int32 n; if (!aes || !buf || !len || !sampv) return EINVAL; - n = opus_encode(aes->enc, sampv, (int)(sampc/aes->ch), - buf, (opus_int32)(*len)); - if (n < 0) { - warning("opus: encode error: %s\n", opus_strerror((int)n)); - return EPROTO; + switch (fmt) { + + case AUFMT_S16LE: + n = opus_encode(aes->enc, sampv, (int)(sampc/aes->ch), + buf, (opus_int32)(*len)); + if (n < 0) { + warning("opus: encode error: %s\n", + opus_strerror((int)n)); + return EPROTO; + } + break; + + case AUFMT_FLOAT: + n = opus_encode_float(aes->enc, sampv, (int)(sampc/aes->ch), + buf, (opus_int32)(*len)); + if (n < 0) { + warning("opus: float encode error: %s\n", + opus_strerror((int)n)); + return EPROTO; + } + break; + + default: + return ENOTSUP; } *len = n; diff --git a/modules/opus/opus.h b/modules/opus/opus.h index d521652..70fa0e3 100644 --- a/modules/opus/opus.h +++ b/modules/opus/opus.h @@ -19,15 +19,17 @@ struct opus_param { int opus_encode_update(struct auenc_state **aesp, const struct aucodec *ac, struct auenc_param *prm, const char *fmtp); int opus_encode_frm(struct auenc_state *aes, uint8_t *buf, size_t *len, - const int16_t *sampv, size_t sampc); + int fmt, const void *sampv, size_t sampc); /* Decode */ int opus_decode_update(struct audec_state **adsp, const struct aucodec *ac, const char *fmtp); -int opus_decode_frm(struct audec_state *ads, int16_t *sampv, size_t *sampc, +int opus_decode_frm(struct audec_state *ads, + int fmt, void *sampv, size_t *sampc, const uint8_t *buf, size_t len); -int opus_decode_pkloss(struct audec_state *st, int16_t *sampv, size_t *sampc); +int opus_decode_pkloss(struct audec_state *st, + int fmt, void *sampv, size_t *sampc); /* SDP */ diff --git a/modules/rst/video.c b/modules/rst/video.c index bf59daf..c12ca0a 100644 --- a/modules/rst/video.c +++ b/modules/rst/video.c @@ -62,6 +62,8 @@ static void *video_thread(void *arg) while (st->run) { + uint64_t timestamp; + sys_msleep(4); now = tmr_jiffies(); @@ -69,8 +71,10 @@ static void *video_thread(void *arg) if (ts > now) continue; + timestamp = ts * VIDEO_TIMEBASE / 1000; + pthread_mutex_lock(&st->mutex); - st->frameh(st->frame, st->arg); + st->frameh(st->frame, timestamp, st->arg); pthread_mutex_unlock(&st->mutex); ts += 1000/st->prm.fps; diff --git a/modules/silk/silk.c b/modules/silk/silk.c index dee296c..5bd9be5 100644 --- a/modules/silk/silk.c +++ b/modules/silk/silk.c @@ -4,6 +4,7 @@ * Copyright (C) 2010 - 2015 Creytiv.com */ #include <re.h> +#include <rem.h> #include <baresip.h> #include <silk/SKP_Silk_SDK_API.h> @@ -155,13 +156,15 @@ static int decode_update(struct audec_state **adsp, static int encode(struct auenc_state *st, uint8_t *buf, size_t *len, - const int16_t *sampv, size_t sampc) + int fmt, const void *sampv, size_t sampc) { int ret; int16_t nBytesOut; if (*len < MAX_BYTES_PER_FRAME) return ENOMEM; + if (fmt != AUFMT_S16LE) + return ENOTSUP; nBytesOut = *len; ret = SKP_Silk_SDK_Encode(st->enc, @@ -180,12 +183,15 @@ static int encode(struct auenc_state *st, uint8_t *buf, size_t *len, } -static int decode(struct audec_state *st, int16_t *sampv, +static int decode(struct audec_state *st, int fmt, void *sampv, size_t *sampc, const uint8_t *buf, size_t len) { int16_t nsamp = *sampc; int ret; + if (fmt != AUFMT_S16LE) + return ENOTSUP; + ret = SKP_Silk_SDK_Decode(st->dec, &st->decControl, 0, @@ -203,11 +209,14 @@ static int decode(struct audec_state *st, int16_t *sampv, } -static int plc(struct audec_state *st, int16_t *sampv, size_t *sampc) +static int plc(struct audec_state *st, int fmt, void *sampv, size_t *sampc) { int16_t nsamp = *sampc; int ret; + if (fmt != AUFMT_S16LE) + return ENOTSUP; + ret = SKP_Silk_SDK_Decode(st->dec, &st->decControl, 1, diff --git a/modules/speex/speex.c b/modules/speex/speex.c index 33b52e2..66a7ff4 100644 --- a/modules/speex/speex.c +++ b/modules/speex/speex.c @@ -465,19 +465,19 @@ static struct aucodec speexv[] = { /* Stereo Speex */ {LE_INIT, 0, "speex", 32000, 32000, 2, speex_fmtp_wb, - encode_update, encode, decode_update, decode, pkloss, 0, 0}, + encode_update, encode, decode_update, decode, pkloss, 0, 0, 0, 0}, {LE_INIT, 0, "speex", 16000, 16000, 2, speex_fmtp_wb, - encode_update, encode, decode_update, decode, pkloss, 0, 0}, + encode_update, encode, decode_update, decode, pkloss, 0, 0, 0, 0}, {LE_INIT, 0, "speex", 8000, 8000, 2, speex_fmtp_nb, - encode_update, encode, decode_update, decode, pkloss, 0, 0}, + encode_update, encode, decode_update, decode, pkloss, 0, 0, 0, 0}, /* Standard Speex */ {LE_INIT, 0, "speex", 32000, 32000, 1, speex_fmtp_wb, - encode_update, encode, decode_update, decode, pkloss, 0, 0}, + encode_update, encode, decode_update, decode, pkloss, 0, 0, 0, 0}, {LE_INIT, 0, "speex", 16000, 16000, 1, speex_fmtp_wb, - encode_update, encode, decode_update, decode, pkloss, 0, 0}, + encode_update, encode, decode_update, decode, pkloss, 0, 0, 0, 0}, {LE_INIT, 0, "speex", 8000, 8000, 1, speex_fmtp_nb, - encode_update, encode, decode_update, decode, pkloss, 0, 0}, + encode_update, encode, decode_update, decode, pkloss, 0, 0, 0, 0}, }; diff --git a/modules/speex_aec/module.mk b/modules/speex_aec/module.mk index 9e29696..caa88a6 100644 --- a/modules/speex_aec/module.mk +++ b/modules/speex_aec/module.mk @@ -6,10 +6,6 @@ MOD := speex_aec $(MOD)_SRCS += speex_aec.c -ifneq ($(HAVE_SPEEXDSP),) $(MOD)_LFLAGS += "-lspeexdsp" -else -$(MOD)_LFLAGS += "-lspeex" -endif include mk/mod.mk diff --git a/modules/speex_pp/module.mk b/modules/speex_pp/module.mk index fad5f88..adcaac2 100644 --- a/modules/speex_pp/module.mk +++ b/modules/speex_pp/module.mk @@ -6,10 +6,6 @@ MOD := speex_pp $(MOD)_SRCS += speex_pp.c -ifneq ($(HAVE_SPEEXDSP),) $(MOD)_LFLAGS += "-lspeexdsp" -else -$(MOD)_LFLAGS += "-lspeex" -endif include mk/mod.mk diff --git a/modules/swscale/swscale.c b/modules/swscale/swscale.c index 5cd8905..a33a3ae 100644 --- a/modules/swscale/swscale.c +++ b/modules/swscale/swscale.c @@ -26,6 +26,7 @@ static enum AVPixelFormat vidfmt_to_avpixfmt(enum vidfmt fmt) switch (fmt) { case VID_FMT_YUV420P: return AV_PIX_FMT_YUV420P; + case VID_FMT_YUV444P: return AV_PIX_FMT_YUV444P; case VID_FMT_NV12: return AV_PIX_FMT_NV12; case VID_FMT_NV21: return AV_PIX_FMT_NV21; default: return AV_PIX_FMT_NONE; diff --git a/modules/v4l/v4l.c b/modules/v4l/v4l.c index a171021..9f65cf6 100644 --- a/modules/v4l/v4l.c +++ b/modules/v4l/v4l.c @@ -117,13 +117,14 @@ static int v4l_get_win(int fd, int width, int height) } -static void call_frame_handler(struct vidsrc_st *st, uint8_t *buf) +static void call_frame_handler(struct vidsrc_st *st, uint8_t *buf, + uint64_t timestamp) { struct vidframe frame; vidframe_init_buf(&frame, st->fmt, &st->size, buf); - st->frameh(&frame, st->arg); + st->frameh(&frame, timestamp, st->arg); } @@ -133,6 +134,7 @@ static void *read_thread(void *arg) while (st->run) { ssize_t n; + uint64_t timestamp; n = read(st->fd, st->mb->buf, st->mb->size); if ((ssize_t)st->mb->size != n) { @@ -141,7 +143,10 @@ static void *read_thread(void *arg) continue; } - call_frame_handler(st, st->mb->buf); + /* XXX: review this */ + timestamp = tmr_jiffies() * 1000; + + call_frame_handler(st, st->mb->buf, timestamp); } return NULL; diff --git a/modules/v4l2/v4l2.c b/modules/v4l2/v4l2.c index 983ad53..228ec10 100644 --- a/modules/v4l2/v4l2.c +++ b/modules/v4l2/v4l2.c @@ -106,6 +106,28 @@ static void print_video_input(const struct vidsrc_st *st) } +static void print_framerate(const struct vidsrc_st *st) +{ + struct v4l2_streamparm streamparm; + struct v4l2_fract tpf; + double fps; + + memset(&streamparm, 0, sizeof(streamparm)); + + streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (v4l2_ioctl(st->fd, VIDIOC_G_PARM, &streamparm) != 0) { + warning("v4l2: VIDIOC_G_PARM error (%m)\n", errno); + return; + } + + tpf = streamparm.parm.capture.timeperframe; + fps = (double)tpf.denominator / (double)tpf.numerator; + + info("v4l2: current framerate is %.2f fps\n", fps); +} + + static int xioctl(int fd, unsigned long int request, void *arg) { int r; @@ -331,19 +353,22 @@ static int start_capturing(struct vidsrc_st *st) } -static void call_frame_handler(struct vidsrc_st *st, uint8_t *buf) +static void call_frame_handler(struct vidsrc_st *st, uint8_t *buf, + uint64_t timestamp) { struct vidframe frame; vidframe_init_buf(&frame, match_fmt(st->pixfmt), &st->sz, buf); - st->frameh(&frame, st->arg); + st->frameh(&frame, timestamp, st->arg); } static int read_frame(struct vidsrc_st *st) { struct v4l2_buffer buf; + struct timeval ts; + uint64_t timestamp; memset(&buf, 0, sizeof(buf)); @@ -371,7 +396,11 @@ static int read_frame(struct vidsrc_st *st) warning("v4l2: index >= n_buffers\n"); } - call_frame_handler(st, st->buffers[buf.index].start); + ts = buf.timestamp; + timestamp = 1000000U * ts.tv_sec + ts.tv_usec; + timestamp = timestamp * VIDEO_TIMEBASE / 1000000U; + + call_frame_handler(st, st->buffers[buf.index].start, timestamp); if (-1 == xioctl (st->fd, VIDIOC_QBUF, &buf)) { warning("v4l2: VIDIOC_QBUF\n"); @@ -470,6 +499,8 @@ static int alloc(struct vidsrc_st **stp, const struct vidsrc *vs, print_video_input(st); + print_framerate(st); + err = start_capturing(st); if (err) goto out; diff --git a/modules/v4l2_codec/v4l2_codec.c b/modules/v4l2_codec/v4l2_codec.c index 814a477..d15e7ac 100644 --- a/modules/v4l2_codec/v4l2_codec.c +++ b/modules/v4l2_codec/v4l2_codec.c @@ -277,7 +277,7 @@ static void enc_destructor(void *arg) } -static void encoders_read(uint32_t rtp_ts, const uint8_t *buf, size_t sz) +static void encoders_read(uint64_t rtp_ts, const uint8_t *buf, size_t sz) { struct le *le; int err; @@ -301,7 +301,7 @@ static void read_handler(int flags, void *arg) struct v4l2_buffer buf; bool keyframe = false; struct timeval ts; - uint32_t rtp_ts; + uint64_t rtp_ts; int err; if (flags & FD_EXCEPT) { @@ -349,10 +349,11 @@ static void read_handler(int flags, void *arg) rtp_ts = (90000ULL * (1000000*ts.tv_sec + ts.tv_usec)) / 1000000; #if 0 - debug("v4l2_codec: %s frame captured at %ldsec, %ldusec (%zu bytes)\n", + debug("v4l2_codec: %s frame captured at %ldsec, %ldusec" + " (%zu bytes) rtp_ts=%llu\n", keyframe ? "KEY" : " ", buf.timestamp.tv_sec, buf.timestamp.tv_usec, - (size_t)buf.bytesused); + (size_t)buf.bytesused, rtp_ts); #endif /* pass the frame to the encoders */ @@ -429,7 +430,7 @@ static int encode_update(struct videnc_state **vesp, const struct vidcodec *vc, list_append(&v4l2.encoderl, &st->le, st); - info("v4l2_codec: video encoder %s: %d fps, %d bit/s, pktsize=%u\n", + info("v4l2_codec: video encoder %s: %.2f fps, %d bit/s, pktsize=%u\n", vc->name, prm->fps, prm->bitrate, prm->pktsize); if (err) @@ -448,6 +449,14 @@ static int encode_packet(struct videnc_state *st, bool update, (void)st; (void)update; (void)frame; + + /* + * XXX: add support for KEY frame requests + */ + if (update) { + info("v4l2_codec: peer requested a KEY frame (ignored)\n"); + } + return 0; } diff --git a/modules/vidbridge/src.c b/modules/vidbridge/src.c index 5e7d08a..2ead7cc 100644 --- a/modules/vidbridge/src.c +++ b/modules/vidbridge/src.c @@ -30,11 +30,10 @@ int vidbridge_src_alloc(struct vidsrc_st **stp, const struct vidsrc *vs, struct vidsrc_st *st; int err; (void)ctx; - (void)prm; (void)fmt; (void)errorh; - if (!stp || !size || !frameh) + if (!stp || !prm || !size || !frameh) return EINVAL; st = mem_zalloc(sizeof(*st), destructor); @@ -44,6 +43,7 @@ int vidbridge_src_alloc(struct vidsrc_st **stp, const struct vidsrc *vs, st->vs = vs; st->frameh = frameh; st->arg = arg; + st->fps = prm->fps; err = str_dup(&st->device, dev); if (err) @@ -82,12 +82,15 @@ struct vidsrc_st *vidbridge_src_find(const char *device) } -void vidbridge_src_input(const struct vidsrc_st *st, +void vidbridge_src_input(struct vidsrc_st *st, const struct vidframe *frame) { if (!st || !frame) return; + /* XXX: Read from vidisp input */ + st->timestamp += VIDEO_TIMEBASE / st->fps; + if (st->frameh) - st->frameh((struct vidframe *)frame, st->arg); + st->frameh((struct vidframe *)frame, st->timestamp, st->arg); } diff --git a/modules/vidbridge/vidbridge.h b/modules/vidbridge/vidbridge.h index 0fbf68e..74b2e24 100644 --- a/modules/vidbridge/vidbridge.h +++ b/modules/vidbridge/vidbridge.h @@ -10,6 +10,8 @@ struct vidsrc_st { struct le le; struct vidisp_st *vidisp; + uint64_t timestamp; + double fps; char *device; vidsrc_frame_h *frameh; void *arg; @@ -43,5 +45,5 @@ int vidbridge_src_alloc(struct vidsrc_st **stp, const struct vidsrc *vs, const char *dev, vidsrc_frame_h *frameh, vidsrc_error_h *errorh, void *arg); struct vidsrc_st *vidbridge_src_find(const char *device); -void vidbridge_src_input(const struct vidsrc_st *st, +void vidbridge_src_input(struct vidsrc_st *st, const struct vidframe *frame); diff --git a/modules/vidloop/vidloop.c b/modules/vidloop/vidloop.c index d99fa49..ca7979b 100644 --- a/modules/vidloop/vidloop.c +++ b/modules/vidloop/vidloop.c @@ -32,12 +32,6 @@ */ -/** Internal pixel-format */ -#ifndef VIDLOOP_INTERNAL_FMT -#define VIDLOOP_INTERNAL_FMT (VID_FMT_YUV420P) -#endif - - /** Video Statistics */ struct vstat { uint64_t tsamp; @@ -58,13 +52,31 @@ struct video_loop { struct viddec_state *dec; struct vidisp_st *vidisp; struct vidsrc_st *vsrc; + struct vidsrc_prm srcprm; struct list filtencl; struct list filtdecl; struct vstat stat; struct tmr tmr_bw; + struct vidsz src_size; + struct vidsz disp_size; + enum vidfmt src_fmt; + uint64_t ts_start; /* usec */ + uint64_t ts_last; /* usec */ uint16_t seq; bool need_conv; + bool started; int err; + + struct { + uint64_t src_frames; + uint64_t enc_bytes; + uint64_t enc_packets; + uint64_t disp_frames; + } stats; + + bool timestamp_set; + uint64_t timestamp_base; /* lowest timestamp */ + uint64_t timestamp_last; /* most recent timestamp */ }; @@ -108,21 +120,27 @@ static int display(struct video_loop *vl, struct vidframe *frame) warning("vidloop: error in video-filters (%m)\n", err); } + /* save the displayed frame info */ + vl->disp_size = frame->size; + ++vl->stats.disp_frames; + /* display frame */ err = vidisp_display(vl->vidisp, "Video Loop", frame); if (err == ENODEV) { info("vidloop: video-display was closed\n"); vl->vidisp = mem_deref(vl->vidisp); vl->err = err; + goto out; } + out: mem_deref(frame_filt); return err; } -static int packet_handler(bool marker, uint32_t rtp_ts, +static int packet_handler(bool marker, uint64_t rtp_ts, const uint8_t *hdr, size_t hdr_len, const uint8_t *pld, size_t pld_len, void *arg) @@ -134,6 +152,9 @@ static int packet_handler(bool marker, uint32_t rtp_ts, int err = 0; (void)rtp_ts; + ++vl->stats.enc_packets; + vl->stats.enc_bytes += (hdr_len + pld_len); + mb = mbuf_alloc(hdr_len + pld_len); if (!mb) return ENOMEM; @@ -172,26 +193,65 @@ static int packet_handler(bool marker, uint32_t rtp_ts, } -static void vidsrc_frame_handler(struct vidframe *frame, void *arg) +static double stream_duration(const struct video_loop *vl) +{ + uint64_t dur; + + if (vl->timestamp_set) + dur = vl->timestamp_last - vl->timestamp_base; + else + dur = 0; + + return video_timestamp_to_seconds(dur); +} + + +static void vidsrc_frame_handler(struct vidframe *frame, uint64_t timestamp, + void *arg) { struct video_loop *vl = arg; struct vidframe *f2 = NULL; struct le *le; + const uint64_t now = tmr_jiffies_usec(); int err = 0; + /* save the timing info */ + if (!gvl->ts_start) + gvl->ts_start = now; + gvl->ts_last = now; + + /* save the video frame info */ + vl->src_size = frame->size; + vl->src_fmt = frame->fmt; + ++vl->stats.src_frames; + + /* Timestamp logic */ + if (vl->timestamp_set) { + if (timestamp <= vl->timestamp_base) { + info("vidloop: timestamp wrapped -- reset base\n"); + vl->timestamp_base = timestamp; + } + vl->timestamp_last = timestamp; + } + else { + vl->timestamp_base = timestamp; + vl->timestamp_last = timestamp; + vl->timestamp_set = true; + } + ++vl->stat.frames; - if (frame->fmt != VIDLOOP_INTERNAL_FMT) { + if (frame->fmt != (enum vidfmt)vl->cfg.enc_fmt) { if (!vl->need_conv) { info("vidloop: NOTE: pixel-format conversion" " needed: %s --> %s\n", vidfmt_name(frame->fmt), - vidfmt_name(VIDLOOP_INTERNAL_FMT)); + vidfmt_name(vl->cfg.enc_fmt)); vl->need_conv = true; } - if (vidframe_alloc(&f2, VIDLOOP_INTERNAL_FMT, &frame->size)) + if (vidframe_alloc(&f2, vl->cfg.enc_fmt, &frame->size)) return; vidconv(f2, frame, 0); @@ -209,21 +269,147 @@ static void vidsrc_frame_handler(struct vidframe *frame, void *arg) } if (vl->vc_enc && vl->enc) { - (void)vl->vc_enc->ench(vl->enc, false, frame); + err = vl->vc_enc->ench(vl->enc, false, frame); + if (err) { + warning("vidloop: encoder error (%m)\n", err); + goto out; + } } else { vl->stat.bytes += vidframe_size(frame->fmt, &frame->size); (void)display(vl, frame); } + out: mem_deref(f2); } +static int print_stats(struct re_printf *pf, const struct video_loop *vl) +{ + const struct config_video *cfg = &vl->cfg; + double real_dur = .0; + int err = 0; + + if (vl->ts_start) { + real_dur = stream_duration(vl); + } + + err |= re_hprintf(pf, "~~~~~ Videoloop summary: ~~~~~\n"); + + /* Source */ + if (vl->vsrc) { + struct vidsrc *vs = vidsrc_get(vl->vsrc); + double avg_fps = .0; + + if (vl->stats.src_frames >= 2) + avg_fps = (vl->stats.src_frames-1) / real_dur; + + err |= re_hprintf(pf, + "* Source\n" + " module %s\n" + " resolution %u x %u (actual %u x %u)\n" + " pixformat %s\n" + " frames %llu\n" + " framerate %.2f fps (avg %.2f fps)\n" + " duration %.3f sec\n" + "\n" + , + vs->name, + cfg->width, cfg->height, + vl->src_size.w, vl->src_size.h, + vidfmt_name(vl->src_fmt), + vl->stats.src_frames, + vl->srcprm.fps, avg_fps, + real_dur); + } + + /* Video conversion */ + if (vl->need_conv) { + err |= re_hprintf(pf, + "* Vidconv\n" + " pixformat %s\n" + "\n" + , + vidfmt_name(cfg->enc_fmt)); + } + + /* Filters */ + if (!list_isempty(baresip_vidfiltl())) { + struct le *le; + + err |= re_hprintf(pf, + "* Filters (%u):", + list_count(baresip_vidfiltl())); + + for (le = list_head(baresip_vidfiltl()); le; le = le->next) { + struct vidfilt *vf = le->data; + err |= re_hprintf(pf, " %s", vf->name); + } + err |= re_hprintf(pf, "\n\n"); + } + + /* Encoder */ + if (vl->vc_enc) { + double avg_bitrate; + double avg_pktrate; + + avg_bitrate = 8.0 * (double)vl->stats.enc_bytes / real_dur; + avg_pktrate = (double)vl->stats.enc_packets / real_dur; + + err |= re_hprintf(pf, + "* Encoder\n" + " module %s\n" + " bitrate %u bit/s (avg %.1f bit/s)\n" + " packets %llu (avg %.1f pkt/s)\n" + "\n" + , + vl->vc_enc->name, + cfg->bitrate, avg_bitrate, + vl->stats.enc_packets, avg_pktrate); + } + + /* Decoder */ + if (vl->vc_dec) { + err |= re_hprintf(pf, + "* Decoder\n" + " module %s\n" + " key-frames %zu\n" + "\n" + , + vl->vc_dec->name, + vl->stat.n_intra); + } + + /* Display */ + if (vl->vidisp) { + struct vidisp *vd = vidisp_get(vl->vidisp); + + err |= re_hprintf(pf, + "* Display\n" + " module %s\n" + " resolution %u x %u\n" + " fullscreen %s\n" + " frames %llu\n" + "\n" + , + vd->name, + vl->disp_size.w, vl->disp_size.h, + cfg->fullscreen ? "Yes" : "No", + vl->stats.disp_frames); + } + + return err; +} + + static void vidloop_destructor(void *arg) { struct video_loop *vl = arg; + if (vl->started) + re_printf("%H\n", print_stats, vl); + tmr_cancel(&vl->tmr_bw); mem_deref(vl->vsrc); mem_deref(vl->enc); @@ -253,7 +439,7 @@ static int enable_codec(struct video_loop *vl, const char *name) return ENOENT; } - info("vidloop: enabled encoder %s (%u fps, %u bit/s)\n", + info("vidloop: enabled encoder %s (%.2f fps, %u bit/s)\n", vl->vc_enc->name, prm.fps, prm.bitrate); vl->vc_dec = vidcodec_find_decoder(vidcodecl, name); @@ -287,10 +473,12 @@ static void print_status(struct video_loop *vl) { (void)re_fprintf(stdout, "\rstatus:" - " [%s] [%s] intra=%zu " + " %.3f sec [%s] [%s] fmt=%s intra=%zu " " EFPS=%.1f %u kbit/s \r", + stream_duration(vl), vl->vc_enc ? vl->vc_enc->name : "", vl->vc_dec ? vl->vc_dec->name : "", + vidfmt_name(vl->cfg.enc_fmt), vl->stat.n_intra, vl->stat.efps, vl->stat.bitrate); fflush(stdout); @@ -326,7 +514,7 @@ static void timeout_bw(void *arg) return; } - tmr_start(&vl->tmr_bw, 2000, timeout_bw, vl); + tmr_start(&vl->tmr_bw, 500, timeout_bw, vl); calc_bitrate(vl); print_status(vl); @@ -335,19 +523,18 @@ static void timeout_bw(void *arg) static int vsrc_reopen(struct video_loop *vl, const struct vidsz *sz) { - struct vidsrc_prm prm; int err; - info("vidloop: %s,%s: open video source: %u x %u at %u fps\n", + info("vidloop: %s,%s: open video source: %u x %u at %.2f fps\n", vl->cfg.src_mod, vl->cfg.src_dev, sz->w, sz->h, vl->cfg.fps); - prm.orient = VIDORIENT_PORTRAIT; - prm.fps = vl->cfg.fps; + vl->srcprm.orient = VIDORIENT_PORTRAIT; + vl->srcprm.fps = vl->cfg.fps; vl->vsrc = mem_deref(vl->vsrc); err = vidsrc_alloc(&vl->vsrc, baresip_vidsrcl(), - vl->cfg.src_mod, NULL, &prm, sz, + vl->cfg.src_mod, NULL, &vl->srcprm, sz, NULL, vl->cfg.src_dev, vidsrc_frame_handler, NULL, vl); if (err) { @@ -462,6 +649,8 @@ static int vidloop_start(struct re_printf *pf, void *arg) return err; } + gvl->started = true; + return err; } diff --git a/modules/vp8/encode.c b/modules/vp8/encode.c index 83f136b..6707ead 100644 --- a/modules/vp8/encode.c +++ b/modules/vp8/encode.c @@ -157,7 +157,7 @@ static inline void hdr_encode(uint8_t hdr[HDR_SIZE], bool noref, bool start, static inline int packetize(bool marker, const uint8_t *buf, size_t len, size_t maxlen, bool noref, uint8_t partid, - uint16_t picid, uint32_t rtp_ts, + uint16_t picid, uint64_t rtp_ts, videnc_packet_h *pkth, void *arg) { uint8_t hdr[HDR_SIZE]; @@ -236,7 +236,7 @@ int vp8_encode(struct videnc_state *ves, bool update, bool keyframe = false, marker = true; const vpx_codec_cx_pkt_t *pkt; uint8_t partid = 0; - uint32_t ts; + uint64_t ts; pkt = vpx_codec_get_cx_data(&ves->ctx, &iter); if (!pkt) diff --git a/modules/vp9/encode.c b/modules/vp9/encode.c index 9de4d73..2a98026 100644 --- a/modules/vp9/encode.c +++ b/modules/vp9/encode.c @@ -172,7 +172,7 @@ static inline void hdr_encode(uint8_t hdr[HDR_SIZE], bool start, bool end, static int send_packet(struct videnc_state *ves, bool marker, const uint8_t *hdr, size_t hdr_len, const uint8_t *pld, size_t pld_len, - uint32_t rtp_ts) + uint64_t rtp_ts) { ves->n_bytes += (hdr_len + pld_len); @@ -184,7 +184,7 @@ static int send_packet(struct videnc_state *ves, bool marker, static inline int packetize(struct videnc_state *ves, bool marker, const uint8_t *buf, size_t len, size_t maxlen, uint16_t picid, - uint32_t rtp_ts) + uint64_t rtp_ts) { uint8_t hdr[HDR_SIZE]; bool start = true; @@ -280,7 +280,7 @@ int vp9_encode(struct videnc_state *ves, bool update, for (;;) { bool marker = true; const vpx_codec_cx_pkt_t *pkt; - uint32_t ts; + uint64_t ts; pkt = vpx_codec_get_cx_data(&ves->ctx, &iter); if (!pkt) diff --git a/modules/x11grab/x11grab.c b/modules/x11grab/x11grab.c index d3aa287..36f37c1 100644 --- a/modules/x11grab/x11grab.c +++ b/modules/x11grab/x11grab.c @@ -100,13 +100,14 @@ static inline uint8_t *x11grab_read(struct vidsrc_st *st) } -static void call_frame_handler(struct vidsrc_st *st, uint8_t *buf) +static void call_frame_handler(struct vidsrc_st *st, uint8_t *buf, + uint64_t timestamp) { struct vidframe frame; vidframe_init_buf(&frame, st->pixfmt, &st->size, buf); - st->frameh(&frame, st->arg); + st->frameh(&frame, timestamp, st->arg); } @@ -118,6 +119,8 @@ static void *read_thread(void *arg) while (st->run) { + uint64_t timestamp; + if (tmr_jiffies() < ts) { sys_msleep(4); continue; @@ -127,9 +130,11 @@ static void *read_thread(void *arg) if (!buf) continue; + timestamp = ts * VIDEO_TIMEBASE / 1000; + ts += (1000/st->fps); - call_frame_handler(st, buf); + call_frame_handler(st, buf, timestamp); } return NULL; |