/** @internal @This inputs data. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump_p reference to pump that generated the buffer */ static void upipe_audiobar_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { if (!upipe_audiobar_check_input(upipe)) { upipe_audiobar_hold_input(upipe, uref); upipe_audiobar_block_input(upipe, upump_p); } else if (!upipe_audiobar_handle(upipe, uref, upump_p)) { upipe_audiobar_hold_input(upipe, uref); upipe_audiobar_block_input(upipe, upump_p); /* Increment upipe refcount to avoid disappearing before all packets * have been sent. */ upipe_use(upipe); } }
/** @internal @This sets the input flow definition. * * @param upipe description structure of the pipe * @param flow_def flow definition packet * @return an error code */ static int upipe_fdec_set_flow_def(struct upipe *upipe, struct uref *flow_def) { struct upipe_fdec_mgr *fdec_mgr = upipe_fdec_mgr_from_upipe_mgr(upipe->mgr); struct upipe_fdec *upipe_fdec = upipe_fdec_from_upipe(upipe); if (flow_def == NULL) return UBASE_ERR_INVALID; if (upipe_fdec->last_inner != NULL) { if (ubase_check(upipe_set_flow_def(upipe_fdec->last_inner, flow_def))) return UBASE_ERR_NONE; } upipe_fdec_store_bin_input(upipe, NULL); upipe_fdec_store_bin_output(upipe, NULL); struct upipe *avcdec = upipe_void_alloc(fdec_mgr->avcdec_mgr, uprobe_pfx_alloc( uprobe_use(&upipe_fdec->last_inner_probe), UPROBE_LOG_VERBOSE, "avcdec")); if (unlikely(avcdec == NULL)) { upipe_err_va(upipe, "couldn't allocate avcdec"); return UBASE_ERR_UNHANDLED; } if (unlikely(!ubase_check(upipe_set_flow_def(avcdec, flow_def)))) { upipe_err_va(upipe, "couldn't set avcdec flow def"); upipe_release(avcdec); return UBASE_ERR_UNHANDLED; } if (upipe_fdec->options != NULL && upipe_fdec->options->udict != NULL) { const char *key = NULL; enum udict_type type = UDICT_TYPE_END; while (ubase_check(udict_iterate(upipe_fdec->options->udict, &key, &type)) && type != UDICT_TYPE_END) { const char *value; if (key == NULL || !ubase_check(udict_get_string(upipe_fdec->options->udict, &value, type, key))) continue; if (!ubase_check(upipe_set_option(avcdec, key, value))) upipe_warn_va(upipe, "option %s=%s invalid", key, value); } } upipe_fdec_store_bin_input(upipe, upipe_use(avcdec)); upipe_fdec_store_bin_output(upipe, avcdec); return UBASE_ERR_NONE; }
/** split callback */ static int split_catch(struct uprobe *uprobe, struct upipe *upipe, int event, va_list args) { if (event != UPROBE_NEED_OUTPUT) return uprobe_throw_next(uprobe, upipe, event, args); upipe_release(upipe_split_output); upipe_split_output = upipe_use(upipe); struct upipe *avcdec = upipe_void_alloc_output(upipe, upipe_avcdec_mgr, uprobe_pfx_alloc_va(uprobe_use(&uprobe_avcdec), loglevel, "avcdec")); if (avcdec == NULL) { upipe_err_va(upipe, "incompatible flow def"); upipe_release(upipe_source); return UBASE_ERR_UNHANDLED; } upipe_release(avcdec); return UBASE_ERR_NONE; }
/** @internal @This outputs the m3u. * * @param upipe description structure of the pipe * @param upump_p reference to source pump to block */ static void upipe_m3u_reader_output_all(struct upipe *upipe, struct upump **upump_p) { struct upipe_m3u_reader *upipe_m3u_reader = upipe_m3u_reader_from_upipe(upipe); struct uref *flow_def = upipe_m3u_reader->current_flow_def; upipe_m3u_reader->current_flow_def = NULL; if (unlikely(!ubase_check(uref_flow_match_def(flow_def, M3U_FLOW_DEF)))) { upipe_err(upipe, "invalid m3u"); uref_free(flow_def); return; } /* force new flow def */ upipe_m3u_reader_store_flow_def(upipe, NULL); /* set output flow def */ upipe_m3u_reader_store_flow_def(upipe, flow_def); /* output */ struct uchain *uchain; bool first = true; upipe_use(upipe); while ((uchain = ulist_pop(&upipe_m3u_reader->items)) != NULL) { struct uref *uref = uref_from_uchain(uchain); if (first) uref_block_set_start(uref); first = false; if (ulist_empty(&upipe_m3u_reader->items)) uref_block_set_end(uref); upipe_m3u_reader_output(upipe, uref, upump_p); } upipe_release(upipe); }
/** @internal @This allocates a wsrc pipe. * * @param mgr common management structure * @param uprobe structure used to raise events * @param signature signature of the pipe allocator * @param args optional arguments * @return pointer to upipe or NULL in case of allocation error */ static struct upipe *_upipe_wsrc_alloc(struct upipe_mgr *mgr, struct uprobe *uprobe, uint32_t signature, va_list args) { struct upipe_wsrc_mgr *wsrc_mgr = upipe_wsrc_mgr_from_upipe_mgr(mgr); if (unlikely(signature != UPIPE_WSRC_SIGNATURE || wsrc_mgr->xfer_mgr == NULL)) goto upipe_wsrc_alloc_err; struct upipe *remote = va_arg(args, struct upipe *); struct uprobe *uprobe_remote = va_arg(args, struct uprobe *); unsigned int queue_length = va_arg(args, unsigned int); assert(queue_length); if (unlikely(remote == NULL)) goto upipe_wsrc_alloc_err2; struct upipe_wsrc *upipe_wsrc = malloc(sizeof(struct upipe_wsrc)); if (unlikely(upipe_wsrc == NULL)) goto upipe_wsrc_alloc_err2; struct upipe *upipe = upipe_wsrc_to_upipe(upipe_wsrc); upipe_init(upipe, mgr, uprobe); upipe_wsrc_init_urefcount(upipe); urefcount_init(upipe_wsrc_to_urefcount_real(upipe_wsrc), upipe_wsrc_free); upipe_wsrc_init_last_inner_probe(upipe); upipe_wsrc_init_bin_output(upipe); upipe_wsrc->source = NULL; uprobe_init(&upipe_wsrc->proxy_probe, upipe_wsrc_proxy_probe, NULL); upipe_wsrc->proxy_probe.refcount = upipe_wsrc_to_urefcount_real(upipe_wsrc); uprobe_init(&upipe_wsrc->qsrc_probe, upipe_wsrc_qsrc_probe, &upipe_wsrc->last_inner_probe); upipe_wsrc->qsrc_probe.refcount = upipe_wsrc_to_urefcount_real(upipe_wsrc); upipe_throw_ready(upipe); /* output queue */ struct upipe *out_qsrc = upipe_qsrc_alloc(wsrc_mgr->qsrc_mgr, uprobe_pfx_alloc(uprobe_use(&upipe_wsrc->qsrc_probe), UPROBE_LOG_VERBOSE, "out_qsrc"), queue_length > UINT8_MAX ? UINT8_MAX : queue_length); if (unlikely(out_qsrc == NULL)) goto upipe_wsrc_alloc_err3; struct upipe *out_qsink = upipe_qsink_alloc(wsrc_mgr->qsink_mgr, uprobe_pfx_alloc(uprobe_remote, UPROBE_LOG_VERBOSE, "out_qsink"), out_qsrc); if (unlikely(out_qsink == NULL)) { upipe_release(out_qsrc); goto upipe_wsrc_alloc_err3; } if (queue_length > UINT8_MAX) upipe_set_max_length(out_qsink, queue_length - UINT8_MAX); upipe_attach_upump_mgr(out_qsrc); upipe_wsrc_store_bin_output(upipe, out_qsrc); /* last remote */ struct upipe *last_remote = upipe_use(remote); struct upipe *tmp; /* upipe_get_output is a control command and may trigger a need_upump_mgr * event */ uprobe_throw(upipe->uprobe, NULL, UPROBE_FREEZE_UPUMP_MGR); while (ubase_check(upipe_get_output(last_remote, &tmp)) && tmp != NULL) { upipe_use(tmp); upipe_release(last_remote); last_remote = tmp; } uprobe_throw(upipe->uprobe, NULL, UPROBE_THAW_UPUMP_MGR); struct upipe *last_remote_xfer = upipe_xfer_alloc(wsrc_mgr->xfer_mgr, uprobe_pfx_alloc(uprobe_use(&upipe_wsrc->proxy_probe), UPROBE_LOG_VERBOSE, "src_last_xfer"), last_remote); if (unlikely(last_remote_xfer == NULL)) { upipe_release(out_qsink); goto upipe_wsrc_alloc_err3; } upipe_attach_upump_mgr(last_remote_xfer); upipe_set_output(last_remote_xfer, out_qsink); upipe_release(out_qsink); /* remote */ if (last_remote != remote) { upipe_wsrc->source = upipe_xfer_alloc(wsrc_mgr->xfer_mgr, uprobe_pfx_alloc(uprobe_use(&upipe_wsrc->proxy_probe), UPROBE_LOG_VERBOSE, "src_xfer"), remote); if (unlikely(upipe_wsrc->source == NULL)) { upipe_release(out_qsink); upipe_release(upipe); return NULL; } upipe_attach_upump_mgr(upipe_wsrc->source); upipe_release(last_remote_xfer); } else { upipe_wsrc->source = last_remote_xfer; upipe_release(remote); } return upipe; upipe_wsrc_alloc_err3: upipe_release(remote); upipe_release(upipe); return NULL; upipe_wsrc_alloc_err2: uprobe_release(uprobe_remote); upipe_release(remote); upipe_wsrc_alloc_err: uprobe_release(uprobe); return NULL; }
/** @internal @This configures a new codec context * * @param upipe description structure of the pipe * @param codec_def codec defintion string * @param extradata pointer to extradata buffer * @param extradata_size extradata size * @return false if the buffer couldn't be accepted */ static bool _upipe_avcdec_set_codec(struct upipe *upipe, const char *codec_def, uint8_t *extradata, int extradata_size) { AVCodec *codec = NULL; int codec_id = 0; struct upipe_avcdec *upipe_avcdec = upipe_avcdec_from_upipe(upipe); struct upipe_avcodec_open_params *params = &upipe_avcdec->open_params; uint8_t *extradata_padded = NULL; /* find codec */ if (codec_def) { codec_id = upipe_av_from_flow_def(codec_def); if (unlikely(!codec_id)) { upipe_warn_va(upipe, "codec %s not found", codec_def); } codec = avcodec_find_decoder(codec_id); if (unlikely(!codec)) { upipe_warn_va(upipe, "codec %s (%d) not found", codec_def, codec_id); } } /* copy extradata if present */ if (extradata && extradata_size > 0) { extradata_padded = upipe_avcdec_copy_extradata(upipe, extradata, extradata_size); if (unlikely(!extradata_padded)) { extradata_size = 0; } } /* use udeal/upump callback if available */ if (upipe_avcdec->upump_mgr) { upipe_dbg(upipe, "upump_mgr present, using udeal"); if (unlikely(upipe_avcdec->upump_av_deal)) { upipe_dbg(upipe, "previous upump_av_deal still running, cleaning first"); upipe_avcdec_abort_av_deal(upipe); } else { upipe_use(upipe); } struct upump *upump_av_deal = upipe_av_deal_upump_alloc(upipe_avcdec->upump_mgr, upipe_avcdec_open_codec_cb, upipe); if (unlikely(!upump_av_deal)) { upipe_err(upipe, "can't create dealer"); upipe_throw_upump_error(upipe); return false; } upipe_avcdec->upump_av_deal = upump_av_deal; memset(params, 0, sizeof(struct upipe_avcodec_open_params)); params->codec = codec; params->extradata = extradata_padded; params->extradata_size = extradata_size; /* fire */ upipe_av_deal_start(upump_av_deal); return true; } else { upipe_dbg(upipe, "no upump_mgr present, direct call to avcdec_open"); upipe_use(upipe); return upipe_avcdec_open_codec(upipe, codec, extradata_padded, extradata_size); } }
/** avcdec callback */ static int avcdec_catch(struct uprobe *uprobe, struct upipe *upipe, int event, va_list args) { if (event != UPROBE_NEED_OUTPUT) return uprobe_throw_next(uprobe, upipe, event, args); struct uref *flow_def = va_arg(args, struct uref *); uint64_t hsize, vsize, wanted_hsize; struct urational sar; bool progressive; if (unlikely(!ubase_check(uref_pic_flow_get_hsize(flow_def, &hsize)) || !ubase_check(uref_pic_flow_get_vsize(flow_def, &vsize)) || !ubase_check(uref_pic_flow_get_sar(flow_def, &sar)))) { upipe_err_va(upipe, "incompatible flow def"); upipe_release(upipe_source); return UBASE_ERR_UNHANDLED; } wanted_hsize = (hsize * sar.num / sar.den / 2) * 2; progressive = ubase_check(uref_pic_get_progressive(flow_def)); struct uref *flow_def2 = uref_dup(flow_def); upipe_use(upipe); if (!progressive) { uref_pic_set_progressive(flow_def2); struct upipe *deint = upipe_void_alloc_output(upipe, upipe_filter_blend_mgr, uprobe_pfx_alloc(uprobe_use(logger), loglevel, "deint")); assert(deint != NULL); upipe_release(upipe); upipe = deint; } if (wanted_hsize != hsize) { uref_pic_flow_set_hsize(flow_def2, wanted_hsize); struct upipe *sws = upipe_flow_alloc_output(upipe, upipe_sws_mgr, uprobe_pfx_alloc_va(uprobe_use(logger), loglevel, "sws"), flow_def2); assert(sws != NULL); upipe_release(upipe); upipe = sws; } uref_pic_flow_clear_format(flow_def2); uref_flow_set_def(flow_def2, "block.mjpeg.pic."); struct upipe *jpegenc = upipe_flow_alloc_output(upipe, upipe_avcenc_mgr, uprobe_pfx_alloc_va(uprobe_use(logger), loglevel, "jpeg"), flow_def2); assert(jpegenc != NULL); upipe_release(upipe); upipe_set_option(jpegenc, "qmax", "2"); upipe = jpegenc; struct upipe *urefprobe = upipe_void_alloc_output(upipe, upipe_probe_uref_mgr, uprobe_pfx_alloc_va(uprobe_use(&uprobe_uref), loglevel, "urefprobe")); assert(urefprobe != NULL); upipe_release(upipe); upipe = urefprobe; struct upipe *fsink = upipe_void_alloc_output(upipe, upipe_fsink_mgr, uprobe_pfx_alloc_va(uprobe_use(logger), ((loglevel > UPROBE_LOG_DEBUG) ? UPROBE_LOG_WARNING : loglevel), "jpegsink")); assert(fsink != NULL); upipe_release(upipe); upipe_fsink_set_path(fsink, dstpath, UPIPE_FSINK_OVERWRITE); upipe = fsink; uref_free(flow_def2); upipe_release(upipe); return UBASE_ERR_NONE; }
/** @internal @This asks to open the given file. * * @param upipe description structure of the pipe * @param path relative or absolute path of the file * @param mode mode of opening the file * @return an error code */ static int _upipe_fsink_set_path(struct upipe *upipe, const char *path, enum upipe_fsink_mode mode) { struct upipe_fsink *upipe_fsink = upipe_fsink_from_upipe(upipe); if (unlikely(upipe_fsink->fd != -1)) { if (likely(upipe_fsink->path != NULL)) upipe_notice_va(upipe, "closing file %s", upipe_fsink->path); close(upipe_fsink->fd); } free(upipe_fsink->path); upipe_fsink->path = NULL; upipe_fsink_set_upump(upipe, NULL); if (!upipe_fsink_check_input(upipe)) /* Release the pipe used in @ref upipe_fsink_input. */ upipe_release(upipe); if (unlikely(path == NULL)) return UBASE_ERR_NONE; upipe_fsink_check_upump_mgr(upipe); const char *mode_desc = NULL; /* hush gcc */ int flags; switch (mode) { case UPIPE_FSINK_NONE: flags = 0; break; case UPIPE_FSINK_APPEND: mode_desc = "append"; flags = O_CREAT; break; case UPIPE_FSINK_OVERWRITE: mode_desc = "overwrite"; flags = O_CREAT | O_TRUNC; break; case UPIPE_FSINK_CREATE: mode_desc = "create"; flags = O_CREAT | O_EXCL; break; default: upipe_err_va(upipe, "invalid mode %d", mode); return UBASE_ERR_INVALID; } upipe_fsink->fd = open(path, O_WRONLY | O_NONBLOCK | O_CLOEXEC | flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (unlikely(upipe_fsink->fd == -1)) { upipe_err_va(upipe, "can't open file %s (%s)", path, mode_desc); return UBASE_ERR_EXTERNAL; } switch (mode) { /* O_APPEND seeks on each write, so use this instead */ case UPIPE_FSINK_APPEND: if (unlikely(lseek(upipe_fsink->fd, 0, SEEK_END) == -1)) { upipe_err_va(upipe, "can't append to file %s (%s)", path, mode_desc); close(upipe_fsink->fd); upipe_fsink->fd = -1; return UBASE_ERR_EXTERNAL; } break; default: break; } upipe_fsink->path = strdup(path); if (unlikely(upipe_fsink->path == NULL)) { close(upipe_fsink->fd); upipe_fsink->fd = -1; upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return UBASE_ERR_ALLOC; } if (!upipe_fsink_check_input(upipe)) /* Use again the pipe that we previously released. */ upipe_use(upipe); upipe_notice_va(upipe, "opening file %s in %s mode", upipe_fsink->path, mode_desc); return UBASE_ERR_NONE; }
/** @internal @This allocates a wsink pipe. * * @param mgr common management structure * @param uprobe structure used to raise events * @param signature signature of the pipe allocator * @param args optional arguments * @return pointer to upipe or NULL in case of allocation error */ static struct upipe *_upipe_wsink_alloc(struct upipe_mgr *mgr, struct uprobe *uprobe, uint32_t signature, va_list args) { struct upipe_wsink_mgr *wsink_mgr = upipe_wsink_mgr_from_upipe_mgr(mgr); if (unlikely(signature != UPIPE_WSINK_SIGNATURE || wsink_mgr->xfer_mgr == NULL)) goto upipe_wsink_alloc_err; struct upipe *remote = va_arg(args, struct upipe *); struct uprobe *uprobe_remote = va_arg(args, struct uprobe *); unsigned int queue_length = va_arg(args, unsigned int); assert(queue_length); if (unlikely(remote == NULL)) goto upipe_wsink_alloc_err2; struct upipe_wsink *upipe_wsink = malloc(sizeof(struct upipe_wsink)); if (unlikely(upipe_wsink == NULL)) goto upipe_wsink_alloc_err2; struct upipe *upipe = upipe_wsink_to_upipe(upipe_wsink); upipe_init(upipe, mgr, uprobe); upipe_wsink_init_urefcount(upipe); urefcount_init(upipe_wsink_to_urefcount_real(upipe_wsink), upipe_wsink_free); upipe_wsink_init_bin_input(upipe); uprobe_init(&upipe_wsink->proxy_probe, upipe_wsink_proxy_probe, NULL); upipe_wsink->proxy_probe.refcount = upipe_wsink_to_urefcount_real(upipe_wsink); uprobe_init(&upipe_wsink->in_qsrc_probe, upipe_wsink_in_qsrc_probe, uprobe_remote); upipe_wsink->in_qsrc_probe.refcount = upipe_wsink_to_urefcount_real(upipe_wsink); upipe_throw_ready(upipe); /* remote */ upipe_use(remote); struct upipe *remote_xfer = upipe_xfer_alloc(wsink_mgr->xfer_mgr, uprobe_pfx_alloc(uprobe_use(&upipe_wsink->proxy_probe), UPROBE_LOG_VERBOSE, "sink_xfer"), remote); if (unlikely(remote_xfer == NULL)) { upipe_release(remote); upipe_release(upipe); return NULL; } upipe_attach_upump_mgr(remote_xfer); /* input queue */ struct upipe *in_qsrc = upipe_qsrc_alloc(wsink_mgr->qsrc_mgr, uprobe_pfx_alloc( uprobe_use(&upipe_wsink->in_qsrc_probe), UPROBE_LOG_VERBOSE, "in_qsrc"), queue_length > UINT8_MAX ? UINT8_MAX : queue_length); if (unlikely(in_qsrc == NULL)) goto upipe_wsink_alloc_err3; struct upipe *in_qsink = upipe_qsink_alloc(wsink_mgr->qsink_mgr, uprobe_pfx_alloc(uprobe_use(&upipe_wsink->proxy_probe), UPROBE_LOG_VERBOSE, "in_qsink"), in_qsrc); if (unlikely(in_qsink == NULL)) goto upipe_wsink_alloc_err3; upipe_wsink_store_bin_input(upipe, in_qsink); if (queue_length > UINT8_MAX) upipe_set_max_length(upipe_wsink->in_qsink, queue_length - UINT8_MAX); struct upipe *in_qsrc_xfer = upipe_xfer_alloc(wsink_mgr->xfer_mgr, uprobe_pfx_alloc(uprobe_use(&upipe_wsink->proxy_probe), UPROBE_LOG_VERBOSE, "in_qsrc_xfer"), in_qsrc); if (unlikely(in_qsrc_xfer == NULL)) goto upipe_wsink_alloc_err3; upipe_set_output(in_qsrc_xfer, remote); upipe_attach_upump_mgr(in_qsrc_xfer); upipe_release(remote); upipe_release(remote_xfer); upipe_release(in_qsrc_xfer); return upipe; upipe_wsink_alloc_err3: upipe_release(remote); upipe_release(remote_xfer); upipe_release(upipe); return NULL; upipe_wsink_alloc_err2: upipe_release(remote); upipe_wsink_alloc_err: uprobe_release(uprobe); return NULL; }
/** @internal @This handles data. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump_p reference to pump that generated the buffer * @return false if the input must be blocked */ static bool upipe_speexdsp_handle(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_speexdsp *upipe_speexdsp = upipe_speexdsp_from_upipe(upipe); struct urational drift_rate; if (!ubase_check(uref_clock_get_rate(uref, &drift_rate))) drift_rate = (struct urational){ 1, 1 }; /* reinitialize resampler when drift rate changes */ if (urational_cmp(&drift_rate, &upipe_speexdsp->drift_rate)) { upipe_speexdsp->drift_rate = drift_rate; spx_uint32_t ratio_num = drift_rate.den; spx_uint32_t ratio_den = drift_rate.num; spx_uint32_t in_rate = upipe_speexdsp->rate * ratio_num / ratio_den; spx_uint32_t out_rate = upipe_speexdsp->rate; int err = speex_resampler_set_rate_frac(upipe_speexdsp->ctx, ratio_num, ratio_den, in_rate, out_rate); if (err) { upipe_err_va(upipe, "Couldn't resample from %u to %u: %s", in_rate, out_rate, speex_resampler_strerror(err)); } else { upipe_dbg_va(upipe, "Resampling from %u to %u", in_rate, out_rate); } } size_t size; if (!ubase_check(uref_sound_size(uref, &size, NULL /* sample_size */))) { uref_free(uref); return true; } struct ubuf *ubuf = ubuf_sound_alloc(upipe_speexdsp->ubuf_mgr, size + 10); if (!ubuf) return false; const void *in; uref_sound_read_void(uref, 0, -1, &in, 1); void *out; ubuf_sound_write_void(ubuf, 0, -1, &out, 1); spx_uint32_t in_len = size; /* input size */ spx_uint32_t out_len = size + 10; /* available output size */ int err; if (upipe_speexdsp->f32) err = speex_resampler_process_interleaved_float(upipe_speexdsp->ctx, in, &in_len, out, &out_len); else err = speex_resampler_process_interleaved_int(upipe_speexdsp->ctx, in, &in_len, out, &out_len); if (err) { upipe_err_va(upipe, "Could not resample: %s", speex_resampler_strerror(err)); } uref_sound_unmap(uref, 0, -1, 1); ubuf_sound_unmap(ubuf, 0, -1, 1); if (err) { ubuf_free(ubuf); } else { ubuf_sound_resize(ubuf, 0, out_len); uref_attach_ubuf(uref, ubuf); } upipe_speexdsp_output(upipe, uref, upump_p); return true; } /** @internal @This receives incoming uref. * * @param upipe description structure of the pipe * @param uref uref structure describing the picture * @param upump_p reference to pump that generated the buffer */ static void upipe_speexdsp_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { if (!upipe_speexdsp_check_input(upipe)) { upipe_speexdsp_hold_input(upipe, uref); upipe_speexdsp_block_input(upipe, upump_p); } else if (!upipe_speexdsp_handle(upipe, uref, upump_p)) { upipe_speexdsp_hold_input(upipe, uref); upipe_speexdsp_block_input(upipe, upump_p); /* Increment upipe refcount to avoid disappearing before all packets * have been sent. */ upipe_use(upipe); } } /** @internal @This receives a provided ubuf manager. * * @param upipe description structure of the pipe * @param flow_format amended flow format * @return an error code */ static int upipe_speexdsp_check(struct upipe *upipe, struct uref *flow_format) { struct upipe_speexdsp *upipe_speexdsp = upipe_speexdsp_from_upipe(upipe); if (flow_format != NULL) upipe_speexdsp_store_flow_def(upipe, flow_format); if (upipe_speexdsp->flow_def == NULL) return UBASE_ERR_NONE; bool was_buffered = !upipe_speexdsp_check_input(upipe); upipe_speexdsp_output_input(upipe); upipe_speexdsp_unblock_input(upipe); if (was_buffered && upipe_speexdsp_check_input(upipe)) { /* All packets have been output, release again the pipe that has been * used in @ref upipe_speexdsp_input. */ upipe_release(upipe); } return UBASE_ERR_NONE; } /** @internal @This sets the input flow definition. * * @param upipe description structure of the pipe * @param flow_def flow definition packet * @return an error code */ static int upipe_speexdsp_set_flow_def(struct upipe *upipe, struct uref *flow_def) { struct upipe_speexdsp *upipe_speexdsp = upipe_speexdsp_from_upipe(upipe); if (flow_def == NULL) return UBASE_ERR_INVALID; const char *def; UBASE_RETURN(uref_flow_get_def(flow_def, &def)) if (unlikely(ubase_ncmp(def, "sound.f32.") && ubase_ncmp(def, "sound.s16."))) return UBASE_ERR_INVALID; uint8_t in_planes; if (unlikely(!ubase_check(uref_sound_flow_get_planes(flow_def, &in_planes)))) return UBASE_ERR_INVALID; if (in_planes != 1) { upipe_err(upipe, "only interleaved audio is supported"); return UBASE_ERR_INVALID; } if (!ubase_check(uref_sound_flow_get_rate(flow_def, &upipe_speexdsp->rate))) { upipe_err(upipe, "no sound rate defined"); uref_dump(flow_def, upipe->uprobe); return UBASE_ERR_INVALID; } uint8_t channels; if (unlikely(!ubase_check(uref_sound_flow_get_channels(flow_def, &channels)))) return UBASE_ERR_INVALID; flow_def = uref_dup(flow_def); if (unlikely(flow_def == NULL)) { upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return UBASE_ERR_ALLOC; } upipe_speexdsp_require_ubuf_mgr(upipe, flow_def); if (upipe_speexdsp->ctx) speex_resampler_destroy(upipe_speexdsp->ctx); upipe_speexdsp->f32 = !ubase_ncmp(def, "sound.f32."); int err; upipe_speexdsp->ctx = speex_resampler_init(channels, upipe_speexdsp->rate, upipe_speexdsp->rate, upipe_speexdsp->quality, &err); if (!upipe_speexdsp->ctx) { upipe_err_va(upipe, "Could not create resampler: %s", speex_resampler_strerror(err)); return UBASE_ERR_INVALID; } return UBASE_ERR_NONE; } /** @internal @This provides a flow format suggestion. * * @param upipe description structure of the pipe * @param request description structure of the request * @return an error code */ static int upipe_speexdsp_provide_flow_format(struct upipe *upipe, struct urequest *request) { const char *def; UBASE_RETURN(uref_flow_get_def(request->uref, &def)) uint8_t channels; UBASE_RETURN(uref_sound_flow_get_channels(request->uref, &channels)) uint8_t planes; UBASE_RETURN(uref_sound_flow_get_planes(request->uref, &planes)) uint8_t sample_size; UBASE_RETURN(uref_sound_flow_get_sample_size(request->uref, &sample_size)) struct uref *flow = uref_dup(request->uref); UBASE_ALLOC_RETURN(flow); uref_sound_flow_clear_format(flow); uref_sound_flow_set_planes(flow, 0); uref_sound_flow_set_channels(flow, channels); uref_sound_flow_add_plane(flow, "all"); if (ubase_ncmp(def, "sound.s16.")) { uref_flow_set_def(flow, "sound.f32."); /* prefer f32 over s16 */ uref_sound_flow_set_sample_size(flow, 4 * channels); } else { uref_flow_set_def(flow, def); uref_sound_flow_set_sample_size(flow, (planes > 1) ? sample_size : sample_size / channels); } return urequest_provide_flow_format(request, flow); } /** @internal @This processes control commands on a speexdsp pipe. * * @param upipe description structure of the pipe * @param command type of command to process * @param args arguments of the command * @return an error code */ static int upipe_speexdsp_control(struct upipe *upipe, int command, va_list args) { struct upipe_speexdsp *upipe_speexdsp = upipe_speexdsp_from_upipe(upipe); switch (command) { /* generic commands */ case UPIPE_REGISTER_REQUEST: { struct urequest *request = va_arg(args, struct urequest *); if (request->type == UREQUEST_FLOW_FORMAT) return upipe_speexdsp_provide_flow_format(upipe, request); if (request->type == UREQUEST_UBUF_MGR) return upipe_throw_provide_request(upipe, request); return upipe_speexdsp_alloc_output_proxy(upipe, request); } case UPIPE_UNREGISTER_REQUEST: { struct urequest *request = va_arg(args, struct urequest *); if (request->type == UREQUEST_FLOW_FORMAT || request->type == UREQUEST_UBUF_MGR) return UBASE_ERR_NONE; return upipe_speexdsp_free_output_proxy(upipe, request); } case UPIPE_GET_OUTPUT: { struct upipe **p = va_arg(args, struct upipe **); return upipe_speexdsp_get_output(upipe, p); } case UPIPE_SET_OUTPUT: { struct upipe *output = va_arg(args, struct upipe *); return upipe_speexdsp_set_output(upipe, output); } case UPIPE_GET_FLOW_DEF: { struct uref **p = va_arg(args, struct uref **); return upipe_speexdsp_get_flow_def(upipe, p); } case UPIPE_SET_FLOW_DEF: { struct uref *flow = va_arg(args, struct uref *); return upipe_speexdsp_set_flow_def(upipe, flow); } case UPIPE_SET_OPTION: { const char *option = va_arg(args, const char *); const char *value = va_arg(args, const char *); if (strcmp(option, "quality")) return UBASE_ERR_INVALID; if (upipe_speexdsp->ctx) return UBASE_ERR_BUSY; int quality = atoi(value); if (quality > SPEEX_RESAMPLER_QUALITY_MAX) { quality = SPEEX_RESAMPLER_QUALITY_MAX; upipe_err_va(upipe, "Clamping quality to %d", SPEEX_RESAMPLER_QUALITY_MAX); } else if (quality < SPEEX_RESAMPLER_QUALITY_MIN) { quality = SPEEX_RESAMPLER_QUALITY_MIN; upipe_err_va(upipe, "Clamping quality to %d", SPEEX_RESAMPLER_QUALITY_MIN); } upipe_speexdsp->quality = quality; return UBASE_ERR_NONE; } default: return UBASE_ERR_UNHANDLED; } } /** @internal @This allocates a speexdsp pipe. * * @param mgr common management structure * @param uprobe structure used to raise events * @param signature signature of the pipe allocator * @param args optional arguments * @return pointer to upipe or NULL in case of allocation error */ static struct upipe *upipe_speexdsp_alloc(struct upipe_mgr *mgr, struct uprobe *uprobe, uint32_t signature, va_list args) { struct upipe *upipe = upipe_speexdsp_alloc_void(mgr, uprobe, signature, args); if (unlikely(upipe == NULL)) return NULL; struct upipe_speexdsp *upipe_speexdsp = upipe_speexdsp_from_upipe(upipe); upipe_speexdsp->ctx = NULL; upipe_speexdsp->drift_rate = (struct urational){ 0, 0 }; upipe_speexdsp->quality = SPEEX_RESAMPLER_QUALITY_MAX; upipe_speexdsp_init_urefcount(upipe); upipe_speexdsp_init_ubuf_mgr(upipe); upipe_speexdsp_init_output(upipe); upipe_speexdsp_init_flow_def(upipe); upipe_speexdsp_init_input(upipe); upipe_throw_ready(upipe); return upipe; } /** @This frees a upipe. * * @param upipe description structure of the pipe */ static void upipe_speexdsp_free(struct upipe *upipe) { struct upipe_speexdsp *upipe_speexdsp = upipe_speexdsp_from_upipe(upipe); if (likely(upipe_speexdsp->ctx)) speex_resampler_destroy(upipe_speexdsp->ctx); upipe_throw_dead(upipe); upipe_speexdsp_clean_input(upipe); upipe_speexdsp_clean_output(upipe); upipe_speexdsp_clean_flow_def(upipe); upipe_speexdsp_clean_ubuf_mgr(upipe); upipe_speexdsp_clean_urefcount(upipe); upipe_speexdsp_free_void(upipe); } /** module manager static descriptor */ static struct upipe_mgr upipe_speexdsp_mgr = { .refcount = NULL, .signature = UPIPE_SPEEXDSP_SIGNATURE, .upipe_alloc = upipe_speexdsp_alloc, .upipe_input = upipe_speexdsp_input, .upipe_control = upipe_speexdsp_control, .upipe_mgr_control = NULL }; /** @This returns the management structure for speexdsp pipes * * @return pointer to manager */ struct upipe_mgr *upipe_speexdsp_mgr_alloc(void) { return &upipe_speexdsp_mgr; }