Пример #1
0
/** @internal @This allocates a hls pipe.
 *
 * @param mgr management structure for this pipe type
 * @param uprobe structure used to raise events
 * @param signature signature of the pipe allocator
 * @param args optional arguments
 * @return pointer to allocated pipe, or NULL in case of failure
 */
static struct upipe *upipe_hls_alloc(struct upipe_mgr *mgr,
                                     struct uprobe *uprobe,
                                     uint32_t signature,
                                     va_list args)
{
    struct upipe *upipe =
        upipe_hls_alloc_void(mgr, uprobe, signature, args);
    if (unlikely(upipe == NULL))
        return NULL;

    struct upipe_hls *upipe_hls = upipe_hls_from_upipe(upipe);

    upipe_hls_init_urefcount(upipe);
    upipe_hls_init_urefcount_real(upipe);
    upipe_hls_init_probe_reader(upipe);
    upipe_hls_init_probe_master(upipe);
    upipe_hls_init_probe_null(upipe);
    upipe_hls_init_first_inner(upipe);
    upipe_hls_init_last_inner(upipe);
    upipe_hls_init_bin_input(upipe);
    upipe_hls_init_bin_output(upipe);
    upipe_hls->flow_def = NULL;
    upipe_hls->item = NULL;
    upipe_hls->sub_pipe_mgr = NULL;

    upipe_throw_ready(upipe);

    struct upipe *inner = NULL;
    struct upipe_mgr *upipe_m3u_reader_mgr = upipe_m3u_reader_mgr_alloc();
    if (unlikely(!upipe_m3u_reader_mgr)) {
        upipe_err(upipe, "fail to allocate m3u reader manager");
        upipe_release(upipe);
        return NULL;
    }

    inner = upipe_void_alloc(
        upipe_m3u_reader_mgr,
        uprobe_pfx_alloc(uprobe_use(&upipe_hls->probe_reader),
                         UPROBE_LOG_VERBOSE, "reader"));
    upipe_mgr_release(upipe_m3u_reader_mgr);
    if (unlikely(!inner)) {
        upipe_err(upipe, "fail to allocate m3u reader");
        upipe_release(upipe);
        return NULL;
    }
    upipe_hls_store_bin_input(upipe, inner);

    return upipe;
}
Пример #2
0
/** @internal @This parses and outputs a m3u file.
 *
 * @param upipe description structure of the pipe
 * @return an error code
 */
static void upipe_m3u_reader_process(struct upipe *upipe)
{
    struct upipe_m3u_reader *upipe_m3u_reader =
        upipe_m3u_reader_from_upipe(upipe);

    if (unlikely(upipe_m3u_reader->current_flow_def == NULL)) {
        upipe_m3u_reader->current_flow_def =
            uref_dup(upipe_m3u_reader->flow_def);
        if (unlikely(upipe_m3u_reader->current_flow_def == NULL)) {
            upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
            return;
        }
    }

    /* parse m3u */
    int ret = UBASE_ERR_NONE;
    struct uref *uref = upipe_m3u_reader->next_uref;
    for (size_t offset = 0;
         uref &&
         ubase_check(ret) &&
         ubase_check(uref_block_scan(uref, &offset, '\n'));
         offset = 0, uref = upipe_m3u_reader->next_uref) {
        struct uref *line =
            upipe_m3u_reader_extract_uref_stream(upipe, offset + 1);
        ret = upipe_m3u_reader_process_line(
            upipe, upipe_m3u_reader->current_flow_def, line);
        uref_free(line);
    }

    if (!ubase_check(ret))
        upipe_err(upipe, "invalid m3u");
}
Пример #3
0
static bool upipe_dveo_asi_sink_add_header(struct upipe *upipe, struct uref *uref,
    uint64_t pts)
{
    struct upipe_dveo_asi_sink *upipe_dveo_asi_sink = upipe_dveo_asi_sink_from_upipe(upipe);

    uint64_t hdr_size;
    if (!ubase_check(uref_block_get_header_size(uref, &hdr_size)))
        hdr_size = 0;

    if (hdr_size != 0)
        return false;

    /* alloc header */
    struct ubuf *header = ubuf_block_alloc(uref->ubuf->mgr, 8);
    if (unlikely(!header)) {
        return true;
    }

    /* 63-bits timestamp */
    union {
        uint8_t timestamp[8];
        uint64_t pts;
    } timestamp;

    timestamp.pts = pts;
    if (upipe_dveo_asi_sink->first_timestamp) {
        upipe_dveo_asi_sink->first_timestamp = false;
        // FIXME: set the counter in an empty packet, and account for latency
        timestamp.pts |= 1LLU << 63; /* Set MSB = Set the counter */
    }

    /* write header, 64 bits little-endian :
     * https://github.com/kierank/dveo-linux-master/blob/450e4b9e4292c2f71acd4d3d2e0a0cd0879d473a/doc/ASI/features.txt#L62 */
    int size = 8;
    uint8_t *header_write_ptr;
    if (!ubase_check(ubuf_block_write(header, 0, &size, &header_write_ptr))) {
        upipe_err(upipe, "could not write header");
        ubuf_free(header);
        return true;
    }
    memcpy(header_write_ptr, timestamp.timestamp, 8);
    uref_block_unmap(uref, 0);

    /* append payload (current ubuf) to header to form segmented ubuf */
    struct ubuf *payload = uref_detach_ubuf(uref);
    if (unlikely(!ubase_check(ubuf_block_append(header, payload)))) {
        upipe_warn(upipe, "could not append payload to header");
        ubuf_free(header);
        ubuf_free(payload);
        return true;
    }
    uref_attach_ubuf(uref, header);
    uref_block_set_header_size(uref, 8);

    return false;
}
Пример #4
0
static void upipe_rtp_h264_input(struct upipe *upipe,
                                struct uref *uref,
                                struct upump **upump_p)
{
    size_t bz = 0;
    if (!ubase_check(uref_block_size(uref, &bz))) {
        upipe_err(upipe, "fail to get uref block size");
        return upipe_rtp_h264_drop(upipe, uref);
    }

    uint8_t buf[bz];
    int size = bz;
    if (unlikely(!ubase_check(uref_block_extract(uref, 0, size, buf)))) {
        upipe_err(upipe, "fail to read from uref");
        return upipe_rtp_h264_drop(upipe, uref);
    }

    uint8_t s_len;
    const uint8_t *s = upipe_mpeg_scan(buf, buf + size, &s_len);
    if (s != buf) {
        upipe_err(upipe, "uref does not start with a mpeg start code");
        return upipe_rtp_h264_drop(upipe, uref);
    }

    while (s < buf + size) {
        uint8_t e_len = 0;
        const uint8_t *e = upipe_mpeg_scan(s + s_len, buf + size, &e_len);
        if (!e)
            e = buf + size;

        struct uref *part = uref_block_splice(uref, s - buf + s_len + 1,
                                              e - (s + s_len + 1));
        upipe_rtp_h264_output_nalu(upipe, *(s + s_len), part, upump_p);
        s = e;
        s_len = e_len;
    }

    uref_free(uref);
}
Пример #5
0
/** @internal @This is the open_codec upump callback
 * It calls _open_codec_cb.
 *
 * @param upump description structure of the pump
 */
static void upipe_avcdec_open_codec_cb(struct upump *upump)
{
    assert(upump);
    struct upipe *upipe = upump_get_opaque(upump, struct upipe*);
    struct upipe_avcdec *upipe_avcdec = upipe_avcdec_from_upipe(upipe);
    struct upipe_avcodec_open_params *params = &upipe_avcdec->open_params;
    struct upump *upump_av_deal = upipe_avcdec->upump_av_deal;

    /* check udeal */
    if (upump_av_deal) {
        if (unlikely(!upipe_av_deal_grab())) {
            upipe_dbg(upipe, "could not grab resource, return");
            return;
        }
        upipe_avcdec->upump_av_deal = NULL;
    }

    /* real open_codec function */
    bool ret = upipe_avcdec_open_codec(upipe, params->codec,
                  params->extradata, params->extradata_size);

    /* clean dealer */
    if (unlikely(!upipe_av_deal_yield(upump_av_deal))) {
        upump_free(upump_av_deal);
        upump_av_deal = NULL;
        upipe_err(upipe, "can't stop dealer");
        upipe_throw_upump_error(upipe);
        if (upipe_avcdec->context) {
            avcodec_close(upipe_avcdec->context);
            av_free(upipe_avcdec->context);
            upipe_avcdec->context = NULL;
        }
        return;
    }
    upump_free(upump_av_deal);
    upump_av_deal = NULL;

    if (!ret) {
        return;
    }

    /* unblock input pump*/
    if (upipe_avcdec->saved_upump_mgr) {
        upipe_dbg(upipe, "unblocking saved upump_mgr");
        upump_mgr_sink_unblock(upipe_avcdec->saved_upump_mgr);
        upump_mgr_release(upipe_avcdec->saved_upump_mgr);
        upipe_avcdec->saved_upump_mgr = NULL;
    }
}
Пример #6
0
/** @This starts the watcher waiting for the sink to unblock.
 *
 * @param upipe description structure of the pipe
 */
static void upipe_dveo_asi_sink_poll(struct upipe *upipe)
{
    struct upipe_dveo_asi_sink *upipe_dveo_asi_sink = upipe_dveo_asi_sink_from_upipe(upipe);
    if (unlikely(!ubase_check(upipe_dveo_asi_sink_check_upump_mgr(upipe)))) {
        upipe_err_va(upipe, "can't get upump_mgr");
        upipe_throw_fatal(upipe, UBASE_ERR_UPUMP);
        return;
    }
    struct upump *watcher = upump_alloc_fd_write(upipe_dveo_asi_sink->upump_mgr,
            upipe_dveo_asi_sink_watcher, upipe, upipe->refcount, upipe_dveo_asi_sink->fd);
    if (unlikely(watcher == NULL)) {
        upipe_err(upipe, "can't create watcher");
        upipe_throw_fatal(upipe, UBASE_ERR_UPUMP);
    } else {
        upipe_dveo_asi_sink_set_upump(upipe, watcher);
        upump_start(watcher);
    }
}
Пример #7
0
/** @internal @This checks a "#EXTM3U" tag.
 *
 * @param upipe description structure of the pipe
 * @param flow_def the current flow definition
 * @param line the trailing characters of the line
 * @return an error code
 */
static int upipe_m3u_reader_process_m3u(struct upipe *upipe,
                                        struct uref *flow_def,
                                        const char *line)
{
    while (isspace(*line))
        line++;

    if (strlen(line)) {
        upipe_err(upipe, "invalid EXTM3U tag");
        return UBASE_ERR_INVALID;
    }
    upipe_verbose(upipe, "found EXTM3U tag");

    const char *def;
    UBASE_RETURN(uref_flow_get_def(flow_def, &def));
    if (strcmp(def, EXPECTED_FLOW_DEF))
        return UBASE_ERR_INVALID;
    return uref_flow_set_def(flow_def, M3U_FLOW_DEF);
}
Пример #8
0
/** @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);
}
Пример #9
0
/** @internal @This allocates a blank source 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_blksrc_alloc(struct upipe_mgr *mgr,
                                        struct uprobe *uprobe,
                                        uint32_t signature, va_list args)
{
    struct uref *flow_def;
    struct upipe *upipe = upipe_blksrc_alloc_flow(mgr, uprobe, signature, args,
                                                  &flow_def);
    if (unlikely(!upipe)) {
        return NULL;
    }

    struct upipe_blksrc *upipe_blksrc = upipe_blksrc_from_upipe(upipe);
    upipe_blksrc_init_urefcount(upipe);
    upipe_blksrc_init_urefcount_real(upipe);
    upipe_blksrc_init_src_probe(upipe);
    upipe_blksrc_init_blk_probe(upipe);
    upipe_blksrc_init_bin_input(upipe);
    upipe_blksrc_init_bin_output(upipe);
    upipe_blksrc->flow_def = flow_def;

    upipe_throw_ready(upipe);

    uint64_t duration = 0;
    if (ubase_check(uref_flow_match_def(flow_def, UREF_PIC_FLOW_DEF))) {
        struct urational fps;
        if (unlikely(!ubase_check(uref_pic_flow_get_fps(flow_def, &fps)))) {
            upipe_release(upipe);
            return NULL;
        }
        duration = (uint64_t)UCLOCK_FREQ * fps.den / fps.num;
    }
    else if (ubase_check(uref_flow_match_def(flow_def, UREF_SOUND_FLOW_DEF))) {
        uint64_t samples, rate;
        if (unlikely(!ubase_check(uref_sound_flow_get_samples(flow_def,
                                                              &samples))) ||
            unlikely(!ubase_check(uref_sound_flow_get_rate(flow_def,
                                                           &rate)))) {
            upipe_release(upipe);
            return NULL;
        }
        duration = samples * UCLOCK_FREQ / rate;
    }
    else {
        upipe_warn(upipe, "unsupported flow def");
        upipe_release(upipe);
        return NULL;
    }

    struct uref *src_flow_def = uref_void_flow_alloc_def(flow_def->mgr);
    if (unlikely(!src_flow_def)) {
        upipe_err(upipe, "fail to allocate source pipe flow def");
        upipe_release(upipe);
        return NULL;
    }

    if (unlikely(!ubase_check(uref_clock_set_duration(src_flow_def,
                                                      duration)))) {
        uref_free(src_flow_def);
        upipe_release(upipe);
        return NULL;
    }

    struct upipe_mgr *upipe_voidsrc_mgr = upipe_voidsrc_mgr_alloc();
    if (unlikely(!upipe_voidsrc_mgr)) {
        upipe_err(upipe, "fail to get void source manager");
        uref_free(src_flow_def);
        upipe_release(upipe);
        return NULL;
    }

    struct upipe *src =
        upipe_flow_alloc(upipe_voidsrc_mgr,
                         uprobe_pfx_alloc(
                            uprobe_use(&upipe_blksrc->src_probe),
                                       UPROBE_LOG_VERBOSE,
                                       "src"),
                         src_flow_def);
    upipe_mgr_release(upipe_voidsrc_mgr);
    uref_free(src_flow_def);
    if (unlikely(!src)) {
        upipe_err(upipe, "fail to allocate source pipe");
        upipe_release(upipe);
        return NULL;
    }

    upipe_blksrc_store_bin_input(upipe, src);

    struct upipe_mgr *upipe_blk_mgr = NULL;
    if (ubase_check(uref_flow_match_def(flow_def, UREF_PIC_FLOW_DEF))) {
        upipe_blk_mgr = upipe_vblk_mgr_alloc();
    }
    else if (ubase_check(uref_flow_match_def(flow_def, UREF_SOUND_FLOW_DEF))) {
        upipe_blk_mgr = upipe_ablk_mgr_alloc();
    }

    if (unlikely(!upipe_blk_mgr)) {
        upipe_err(upipe, "fail to get blank generator manager");
        upipe_release(upipe);
        return NULL;
    }

    struct upipe *blk =
        upipe_flow_alloc(upipe_blk_mgr,
                         uprobe_pfx_alloc(
                            uprobe_use(&upipe_blksrc->blk_probe),
                                       UPROBE_LOG_VERBOSE,
                                       "blk"),
                         flow_def);
    upipe_mgr_release(upipe_blk_mgr);
    if (unlikely(!blk)) {
        upipe_err(upipe, "fail to allocate blank generator pipe");
        upipe_release(upipe);
        return NULL;
    }

    upipe_blksrc_store_bin_output(upipe, blk);

    if (unlikely(!ubase_check(upipe_set_output(src, blk)))) {
        upipe_release(upipe);
        return NULL;
    }

    return upipe;
}
Пример #10
0
/** @internal @This handles the input uref.
 *
 * @param upipe description structure of the pipe
 * @param uref input uref
 * @param upump_p reference to pump that generated the buffer
 */
static void upipe_vblk_input(struct upipe *upipe,
                             struct uref *uref,
                             struct upump **upump_p)
{
    struct upipe_vblk *upipe_vblk = upipe_vblk_from_upipe(upipe);
    struct uref *flow_def = upipe_vblk->flow_def;
    struct uref *input_flow_def = upipe_vblk->input_flow_def;

    if (uref->ubuf) {
        upipe_vblk_output(upipe, uref, upump_p);
        return;
    }

    if (unlikely(!input_flow_def)) {
        upipe_warn(upipe, "no input flow definition");
        uref_free(uref);
        return;
    }

    if (unlikely(!flow_def)) {
        upipe_warn(upipe, "no output flow definition");
        uref_free(uref);
        return;
    }

    if (unlikely(!upipe_vblk->ubuf_mgr)) {
        upipe_warn(upipe, "no ubuf manager set");
        uref_free(uref);
        return;
    }

    if (unlikely(!upipe_vblk->ubuf)) {
        upipe_verbose(upipe, "allocate blank picture");

        uint64_t hsize, vsize;
        ubase_assert(uref_pic_flow_get_hsize(upipe_vblk->flow_def, &hsize));
        ubase_assert(uref_pic_flow_get_vsize(upipe_vblk->flow_def, &vsize));

        upipe_vblk->ubuf = ubuf_pic_alloc(upipe_vblk->ubuf_mgr, hsize, vsize);
        if (unlikely(!upipe_vblk->ubuf)) {
            upipe_err(upipe, "fail to allocate picture");
            uref_free(uref);
            upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
            return;
        }
        ubuf_pic_clear(upipe_vblk->ubuf, 0, 0, -1, -1, 1);
    }

    struct ubuf *ubuf = ubuf_dup(upipe_vblk->ubuf);
    if (unlikely(!ubuf)) {
        upipe_err(upipe, "fail to duplicate blank picture");
        uref_free(uref);
        upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
        return;
    }

    uref_attach_ubuf(uref, ubuf);
    if (ubase_check(uref_pic_get_progressive(flow_def)))
        uref_pic_set_progressive(uref);

    upipe_vblk_output(upipe, uref, upump_p);
}
Пример #11
0
static void upipe_rtp_h264_output_nalu(struct upipe *upipe,
                                       uint8_t nalu,
                                       struct uref *uref,
                                       struct upump **upump_p)
{
    size_t size = 0;
    if (unlikely(!ubase_check(uref_block_size(uref, &size)))) {
        upipe_err(upipe, "fail to get block size");
        return;
    }

    bool split = size > RTP_SPLIT_SIZE;
    uint32_t fragment = 0;

    while (size) {
        bool last_fragment = size <= RTP_SPLIT_SIZE;
        size_t split_size = last_fragment ? size : RTP_SPLIT_SIZE;
        uint8_t hdr[2] = { nalu, 0 };
        size_t hdr_size = 1;

        if (split) {
            if (!fragment)
                hdr[1] = FU_START;
            else if (last_fragment)
                hdr[1] = FU_END;
            hdr[1] |= NALU_TYPE(hdr[0]);

            hdr[0] = NALU_F(hdr[0]) | NALU_NRI(hdr[0]) | FU_A;
            hdr_size++;
        }

        struct uref *next = NULL;
        if (!last_fragment) {
            next = uref_block_split(uref, split_size);
            if (unlikely(next == NULL)) {
                upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
                return;
            }
        }

        /* FIXME should require a ubuf_mgr */
        struct ubuf *header =
                ubuf_block_alloc(uref->ubuf->mgr, hdr_size * sizeof (hdr[0]));
        if (unlikely(header == NULL)) {
            upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
            uref_free(uref);
            uref_free(next);
            return;
        }

        uint8_t *buf = NULL;
        int buf_size = hdr_size;
        ubuf_block_write(header, 0, &buf_size, &buf);
        memcpy(buf, hdr, hdr_size * sizeof (hdr[0]));
        ubuf_block_unmap(header, 0);

        /* append payload (current ubuf) to header to form segmented ubuf */
        struct ubuf *payload = uref_detach_ubuf(uref);
        if (unlikely(!ubase_check(ubuf_block_append(header, payload)))) {
            upipe_warn(upipe, "could not append payload to header");
            ubuf_free(header);
            ubuf_free(payload);
            uref_free(uref);
            uref_free(next);
            return;
        }
        uref_attach_ubuf(uref, header);
        uref_clock_set_cr_dts_delay(uref, 0);
        upipe_rtp_h264_output(upipe, uref, upump_p);
        size -= split_size;
        fragment++;
        uref = next;
    }
}
Пример #12
0
/** @internal @This converts block urefs to sound urefs.
 *
 * @param upipe description structure of the pipe
 * @param uref uref structure
 * @param upump_p reference to pump that generated the buffer
 */
static void upipe_block_to_sound_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p)
{

    struct upipe_block_to_sound *upipe_block_to_sound = upipe_block_to_sound_from_upipe(upipe);

    /* get block size */
    size_t block_size = 0;
    uref_block_size(uref, &block_size);

    /* drop incomplete samples */
    if ((block_size % upipe_block_to_sound->sample_size) != 0) {
        upipe_warn(upipe, "Incomplete samples detected");
    }

    int samples = block_size / upipe_block_to_sound->sample_size;
    block_size = samples * upipe_block_to_sound->sample_size;

    /* alloc sound ubuf */
    struct ubuf *ubuf_block_to_sound = ubuf_sound_alloc(upipe_block_to_sound->ubuf_mgr,
                                                        samples);

    if (unlikely(ubuf_block_to_sound == NULL)) {
        uref_free(uref);
        upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
        return;
    }


    /* map sound ubuf for writing */
    int32_t *w;

    if (unlikely(!ubase_check(ubuf_sound_write_int32_t(ubuf_block_to_sound, 0, -1, &w,
                                                            upipe_block_to_sound->planes)))) {
        upipe_err(upipe, "could not write uref, dropping samples");
        ubuf_sound_unmap(ubuf_block_to_sound, 0, -1, upipe_block_to_sound->planes);
        ubuf_free(ubuf_block_to_sound);
        uref_free(uref);
        upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
        return;
    }
    /* map block ubuf for reading */
    const uint8_t *r;
    int end = -1;
    if (unlikely(!ubase_check(uref_block_read(uref, 0, &end, &r)))) {
        upipe_err(upipe, "could not read uref, dropping samples");
        ubuf_sound_unmap(ubuf_block_to_sound, 0, -1, upipe_block_to_sound->planes);
        ubuf_block_unmap(uref->ubuf, 0);
        ubuf_free(ubuf_block_to_sound);
        uref_free(uref);
        upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
        return;
    }
    /* copy block to sound */
    memcpy(w, r, block_size);
    /* unmap ubufs */
    ubuf_sound_unmap(ubuf_block_to_sound, 0, -1, upipe_block_to_sound->planes);
    ubuf_block_unmap(uref->ubuf, 0);
    /* attach sound ubuf to uref */
    uref_attach_ubuf(uref, ubuf_block_to_sound);
    /* output pipe */

    upipe_block_to_sound_output(upipe, uref, upump_p);
}
Пример #13
0
/** @internal @This parses _uri and opens IPv4 & IPv6 sockets
 *
 * @param upipe description structure of the pipe
 * @param _uri socket URI
 * @param ttl packets time-to-live
 * @param bind_port bind port
 * @param connect_port connect port
 * @param weight weight (UNUSED)
 * @param use_tcp Set this to open a tcp socket (instead of udp)
 * @param use_raw open RAW socket (udp)
 * @param raw_header user-provided buffer for RAW header (ip+udp)
 * @return socket fd, or -1 in case of error
 */
int upipe_udp_open_socket(struct upipe *upipe, const char *_uri, int ttl,
                          uint16_t bind_port, uint16_t connect_port,
                          unsigned int *weight, bool *use_tcp,
                          bool *use_raw, uint8_t *raw_header)
{
    union sockaddru bind_addr, connect_addr;
    int fd, i;
    char *uri = strdup(_uri);
    char *token = uri;
    char *token2 = NULL;
    int bind_if_index = 0, connect_if_index = 0;
    in_addr_t if_addr = INADDR_ANY;
    in_addr_t src_addr = INADDR_ANY;
    uint16_t src_port = 4242;
    int tos = 0;
    bool b_tcp;
    bool b_raw;
    int family;
    socklen_t sockaddr_len;
#if !defined(__APPLE__) && !defined(__native_client__)
    char *ifname = NULL;
#endif

    if (!uri)
        return -1;

    memset(&bind_addr, 0, sizeof(union sockaddru));
    memset(&connect_addr, 0, sizeof(union sockaddru));

    bind_addr.ss.ss_family = AF_UNSPEC;
    connect_addr.ss.ss_family = AF_UNSPEC;

    if (use_tcp == NULL) {
        use_tcp = &b_tcp;
    }
    *use_tcp = false;
    if (use_raw == NULL) {
        use_raw = &b_raw;
    }
    *use_raw = false;

    token2 = strrchr(uri, ',');
    if (token2) {
        *token2++ = '\0';
        if (weight) {
            *weight = strtoul(token2, NULL, 0);
        }
    } else if (weight) {
        *weight = 1;
    }

    token2 = strchr(uri, '/');
    if (token2) {
        *token2 = '\0';
    }

    if (*token == '\0') {
        free(uri);
        return -1;
    }

    /* Hosts */
    if (token[0] != '@') {
        if (!upipe_udp_parse_node_service(upipe, token, &token, connect_port,
                                        &connect_if_index, &connect_addr.ss)) {
            free(uri);
            return -1;
        }
        /* required on some architectures */
        memset(&connect_addr.sin.sin_zero, 0, sizeof(connect_addr.sin.sin_zero));
    }

    if (token[0] == '@') {
        token++;
        if (!upipe_udp_parse_node_service(upipe, token, &token, bind_port,
                                        &bind_if_index, &bind_addr.ss)) {
            free(uri);
            return -1;
        }
        /* required on some architectures */
        memset(&bind_addr.sin.sin_zero, 0, sizeof(bind_addr.sin.sin_zero));
    }

    if (bind_addr.ss.ss_family == AF_UNSPEC &&
         connect_addr.ss.ss_family == AF_UNSPEC) {
        free(uri);
        return -1;
    }

    upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr);

    /* Weights and options */
    if (token2) {
        do {
            *token2++ = '\0';
#define IS_OPTION(option) (!strncasecmp(token2, option, strlen(option)))
#define ARG_OPTION(option) (token2 + strlen(option))
            if (IS_OPTION("ifindex=")) {
                bind_if_index = connect_if_index =
                    strtol(ARG_OPTION("ifindex="), NULL, 0);
            } else if (IS_OPTION("ifaddr=")) {
                char *option = config_stropt(ARG_OPTION("ifaddr="));
                if_addr = inet_addr(option);
                free( option );
#if !defined(__APPLE__) && !defined(__native_client__)
            } else if ( IS_OPTION("ifname=") ) {
                ifname = config_stropt( ARG_OPTION("ifname=") );
                if (strlen(ifname) >= IFNAMSIZ) {
                    ifname[IFNAMSIZ-1] = '\0';
                }
#endif
            } else if (IS_OPTION("srcaddr=")) {
                char *option = config_stropt(ARG_OPTION("srcaddr="));
                src_addr = inet_addr(option);
                free(option);
                *use_raw = true;
            } else if (IS_OPTION("srcport=")) {
                src_port = strtol(ARG_OPTION("srcport="), NULL, 0);
            } else if (IS_OPTION("ttl=")) {
                ttl = strtol(ARG_OPTION("ttl="), NULL, 0);
            } else if (IS_OPTION("tos=")) {
                tos = strtol(ARG_OPTION("tos="), NULL, 0);
            } else if (IS_OPTION("tcp")) {
                *use_tcp = true;
            } else {
                upipe_warn_va(upipe, "unrecognized option %s", token2);
            }
#undef IS_OPTION
#undef ARG_OPTION
        } while ((token2 = strchr(token2, '/')) != NULL);
    }

    if (unlikely(*use_tcp && *use_raw)) {
        upipe_warn(upipe, "RAW sockets not implemented for tcp");
        free(uri);
        return -1;
    }

    free(uri);

    /* Sanity checks */
    if (bind_addr.ss.ss_family != AF_UNSPEC
          && connect_addr.ss.ss_family != AF_UNSPEC
          && bind_addr.ss.ss_family != connect_addr.ss.ss_family) {
        upipe_err(upipe, "incompatible address types");
        return -1;
    }
    if (bind_addr.ss.ss_family != AF_UNSPEC) {
        family = bind_addr.ss.ss_family;
    } else if (connect_addr.ss.ss_family != AF_UNSPEC) {
        family = connect_addr.ss.ss_family;
    } else {
        upipe_err(upipe, "ambiguous address declaration");
        return -1;
    }
    sockaddr_len = (family == AF_INET) ? sizeof(struct sockaddr_in) :
                     sizeof(struct sockaddr_in6);

    if (bind_if_index && connect_if_index
          && bind_if_index != connect_if_index) {
        upipe_err(upipe, "incompatible bind and connect interfaces");
        return -1;
    }
    if (connect_if_index) bind_if_index = connect_if_index;
    else connect_if_index = bind_if_index;

    /* RAW header */
    if (*use_raw && raw_header) {
        upipe_udp_raw_fill_headers(upipe, raw_header,
                src_addr, connect_addr.sin.sin_addr.s_addr, src_port,
                ntohs(connect_addr.sin.sin_port), ttl, tos, 0);
    }


    /* Socket configuration */
    int sock_type = SOCK_DGRAM;
    if (*use_tcp) sock_type = SOCK_STREAM;
    if (*use_raw) sock_type = SOCK_RAW;
    int sock_proto = (*use_raw ? IPPROTO_RAW : 0);

    if ((fd = socket(family, sock_type, sock_proto)) < 0) {
        upipe_err_va(upipe, "unable to open socket (%m)");
        return -1;
    }
    #if !defined(__APPLE__) && !defined(__native_client__)
    if (*use_raw) {
        int hincl = 1;
        if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &hincl, sizeof(hincl)) < 0) {
            upipe_err_va(upipe, "unable to set IP_HDRINCL");
            close(fd);
            return -1;
        }
    }
    #endif

    i = 1;
    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&i,
                     sizeof(i)) == -1) {
        upipe_err_va(upipe, "unable to set socket (%m)");
        close(fd);
        return -1;
    }

    if (family == AF_INET6) {
        if (bind_if_index
              && setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
                     (void *)&bind_if_index, sizeof(bind_if_index)) < 0) {
            upipe_err(upipe, "couldn't set interface index");
            upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr);
            close(fd);
            return -1;
        }

        if (bind_addr.ss.ss_family != AF_UNSPEC) {
            #if !defined(__APPLE__) && !defined(__native_client__)
            if (IN6_IS_ADDR_MULTICAST(&bind_addr.sin6.sin6_addr)) {
                struct ipv6_mreq imr;
                union sockaddru bind_addr_any = bind_addr;
                bind_addr_any.sin6.sin6_addr = in6addr_any;

                if (bind(fd, &bind_addr_any.so,
                           sizeof(bind_addr_any)) < 0) {
                    upipe_err(upipe, "couldn't bind");
                    upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr);
                    close(fd);
                    return -1;
                }

                imr.ipv6mr_multiaddr = bind_addr.sin6.sin6_addr;
                imr.ipv6mr_interface = bind_if_index;

                /* Join Multicast group without source filter */
                if (setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
                                 (char *)&imr, sizeof(struct ipv6_mreq)) < 0) {
                    upipe_err(upipe, "couldn't join multicast group");
                    upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr);
                    close(fd);
                    return -1;
                }
            } else
            #endif
                goto normal_bind;
        }
    }
    else if (bind_addr.ss.ss_family != AF_UNSPEC) {
normal_bind:
        if (bind(fd, &bind_addr.so, sockaddr_len) < 0) {
            upipe_err(upipe, "couldn't bind");
            upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr);
            close(fd);
            return -1;
        }
    }

    if (!*use_tcp) {
        /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to
         * avoid packet loss caused by scheduling problems */
        i = 0x80000;
        if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void *) &i, sizeof(i)))
            upipe_warn(upipe, "fail to increase receive buffer");

        /* Join the multicast group if the socket is a multicast address */
        if (bind_addr.ss.ss_family == AF_INET
              && IN_MULTICAST(ntohl(bind_addr.sin.sin_addr.s_addr))) {
#ifndef __native_client__
            if (connect_addr.ss.ss_family != AF_UNSPEC) {
                /* Source-specific multicast */
                struct ip_mreq_source imr;
                imr.imr_multiaddr = bind_addr.sin.sin_addr;
                imr.imr_interface.s_addr = if_addr;
                imr.imr_sourceaddr = connect_addr.sin.sin_addr;
                if (bind_if_index) {
                    upipe_warn(upipe, "ignoring ifindex option in SSM");
                }

                if (setsockopt(fd, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP,
                            (char *)&imr, sizeof(struct ip_mreq_source)) < 0) {
                    upipe_err_va(upipe, "couldn't join multicast group (%m)");
                    upipe_udp_print_socket(upipe, "socket definition:", &bind_addr,
                                 &connect_addr);
                    close(fd);
                    return -1;
                }
            } else if (bind_if_index) {
                /* Linux-specific interface-bound multicast */
                struct ip_mreqn imr;
                imr.imr_multiaddr = bind_addr.sin.sin_addr;
                imr.imr_address.s_addr = if_addr;
                imr.imr_ifindex = bind_if_index;

                if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
                                 (char *)&imr, sizeof(struct ip_mreqn)) < 0) {
                    upipe_err_va(upipe, "couldn't join multicast group (%m)");
                    upipe_udp_print_socket(upipe, "socket definition:", &bind_addr,
                                 &connect_addr);
                    close(fd);
                    return -1;
                }
            } else
#endif
            {
                /* Regular multicast */
                struct ip_mreq imr;
                imr.imr_multiaddr = bind_addr.sin.sin_addr;
                imr.imr_interface.s_addr = if_addr;

                if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
                                 (char *)&imr, sizeof(struct ip_mreq)) < 0) {
                    upipe_err_va(upipe, "couldn't join multicast group (%m)");
                    upipe_udp_print_socket(upipe, "socket definition:", &bind_addr,
                                 &connect_addr);
                    close(fd);
                    return -1;
                }
            }
#ifdef SO_BINDTODEVICE
            if (ifname) {
                /* linux specific, needs root or CAP_NET_RAW */
                if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
                               ifname, strlen(ifname) + 1) < 0) {
                    upipe_err_va(upipe, "couldn't bind to device %s (%m)",
                                 ifname);
                    free(ifname);
                    close(fd);
                    return -1;
                }
                ubase_clean_str(&ifname);
            }
#endif
        }
    }

    if (connect_addr.ss.ss_family != AF_UNSPEC) {
        if (connect(fd, &connect_addr.so, sockaddr_len) < 0) {
            upipe_err_va(upipe, "cannot connect socket (%m)");
            upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr);
            close(fd);
            return -1;
        }

        if (!*use_tcp) {
            if (ttl) {
                if (family == AF_INET
                      && IN_MULTICAST(ntohl(connect_addr.sin.sin_addr.s_addr))) {
                    if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
                                     (void *)&ttl, sizeof(ttl)) == -1) {
                        upipe_err_va(upipe, "couldn't set TTL (%m)");
                        upipe_udp_print_socket(upipe, "socket definition:", &bind_addr,
                                     &connect_addr);
                        close(fd);
                        return -1;
                    }
                }

                if (family == AF_INET6
                      && IN6_IS_ADDR_MULTICAST(&connect_addr.sin6.sin6_addr)) {
                    if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
                                     (void *)&ttl, sizeof(ttl)) == -1) {
                        upipe_err_va(upipe, "couldn't set TTL (%m)");
                        upipe_udp_print_socket(upipe, "socket definition:", &bind_addr,
                                     &connect_addr);
                        close(fd);
                        return -1;
                    }
                }
            }

            if (tos) {
                if (setsockopt(fd, IPPROTO_IP, IP_TOS,
                                 (void *)&tos, sizeof(tos)) == -1) {
                    upipe_err_va(upipe, "couldn't set TOS (%m)");
                    upipe_udp_print_socket(upipe, "socket definition:", &bind_addr,
                                 &connect_addr);
                    close(fd);
                    return -1;
                }
            }
        }
    } else if (*use_tcp) {
        /* Open in listen mode - wait for an incoming connection */
        int new_fd;
        if (listen(fd, 1) < 0) {
            upipe_err_va(upipe, "couldn't listen (%m)");
            upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr);
            close(fd);
            return -1;
        }

        while ((new_fd = accept(fd, NULL, NULL)) < 0) {
            if (errno != EINTR) {
                upipe_err_va(upipe, "couldn't accept (%m)");
                upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr);
                close(fd);
                return -1;
            }
        }
        close(fd);
        return new_fd;
    }

    return fd;
}
Пример #14
0
/** @internal @This handles input buffers.
 *
 * @param upipe description structure of the pipe
 * @param uref input buffer to handle
 * @param upump_p reference to the pump that generated the buffer
 */
static void upipe_dvbcsa_enc_input(struct upipe *upipe,
                                   struct uref *uref,
                                   struct upump **upump_p)
{
    struct upipe_dvbcsa_enc *upipe_dvbcsa_enc =
        upipe_dvbcsa_enc_from_upipe(upipe);
    struct upipe_dvbcsa_common *common =
        upipe_dvbcsa_enc_to_common(upipe_dvbcsa_enc);

    if (unlikely(!upipe_dvbcsa_enc->key))
        /* no key set, forward */
        return upipe_dvbcsa_enc_output(upipe, uref, upump_p);

    uint32_t ts_header_size = TS_HEADER_SIZE;
    uint8_t buf[TS_HEADER_SIZE];
    const uint8_t *ts_header = uref_block_peek(uref, 0, sizeof (buf), buf);
    if (unlikely(!ts_header)) {
        upipe_err(upipe, "fail to read ts header");
        uref_free(uref);
        return;
    }
    uint8_t scrambling = ts_get_scrambling(ts_header);
    bool has_payload = ts_has_payload(ts_header);
    bool has_adaptation = ts_has_adaptation(ts_header);
    uint16_t pid = ts_get_pid(ts_header);
    uref_block_peek_unmap(uref, 0, buf, ts_header);

    if (!has_payload || scrambling ||
        !upipe_dvbcsa_common_check_pid(common, pid))
        return upipe_dvbcsa_enc_output(upipe, uref, upump_p);

    if (unlikely(has_adaptation)) {
        uint8_t af_length;
        int ret = uref_block_extract(uref, ts_header_size, 1, &af_length);
        if (unlikely(!ubase_check(ret))) {
            upipe_err(upipe, "fail to extract adaptation field length");
            uref_free(uref);
            return;
        }
        if (unlikely(af_length >= 183)) {
            upipe_warn(upipe, "invalid adaptation field received");
            uref_free(uref);
            return;
        }
        ts_header_size += af_length + 1;
    }

    struct ubuf *ubuf = ubuf_block_copy(uref->ubuf->mgr, uref->ubuf, 0, -1);
    if (unlikely(!ubuf)) {
        upipe_err(upipe, "fail to allocate buffer");
        uref_free(uref);
        return;
    }
    uref_attach_ubuf(uref, ubuf);
    int size = -1;
    uint8_t *ts;
    int ret = ubuf_block_write(ubuf, 0, &size, &ts);
    if (unlikely(!ubase_check(ret))) {
        upipe_err(upipe, "fail to write buffer");
        uref_free(uref);
        return;
    }
    ts_set_scrambling(ts, 0x2);
    dvbcsa_encrypt(upipe_dvbcsa_enc->key,
                   ts + ts_header_size,
                   size - ts_header_size);
    return upipe_dvbcsa_enc_output(upipe, uref, upump_p);
}
Пример #15
0
/** @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;
}
Пример #16
0
/** @internal @This asks to open the given device.
 *
 * @param upipe description structure of the pipe
 * @param path relative or absolute path of the file
 * @return an error code
 */
static int upipe_dveo_asi_sink_open(struct upipe *upipe)
{
#define BYPASS_MODE 1

    struct upipe_dveo_asi_sink *upipe_dveo_asi_sink = upipe_dveo_asi_sink_from_upipe(upipe);
    char path[20], sys[50], buf[20];
    memset(buf, 0, sizeof(buf));

    static const char dev_fmt[] = "/dev/asitx%u";
    static const char sys_fmt[] = "/sys/class/asi/asitx%u/%s";
    static const char dvbm_sys_fmt[] = "/sys/class/dvbm/%u/%s";

    snprintf(sys, sizeof(sys), sys_fmt, upipe_dveo_asi_sink->card_idx, "timestamps");
    snprintf(buf, sizeof(buf), "%u\n", 2);
    if (util_write(sys, buf, sizeof(buf)) < 0) {
        upipe_err_va(upipe, "Couldn't set timestamp mode (%m)");
        return UBASE_ERR_EXTERNAL;
    }

    snprintf(sys, sizeof(sys), sys_fmt, upipe_dveo_asi_sink->card_idx, "bufsize");
    snprintf(buf, sizeof(buf), "%u\n", 6*(188+8)); /* minimum is 1024 */
    if (util_write(sys, buf, sizeof(buf)) < 0) {
        upipe_err_va(upipe, "Couldn't set buffer size (%m)");
        return UBASE_ERR_EXTERNAL;
    }

    snprintf(sys, sizeof(sys), sys_fmt, upipe_dveo_asi_sink->card_idx, "buffers");
    snprintf(buf, sizeof(buf), "%u\n", 500);
    if (util_write(sys, buf, sizeof(buf)) < 0) {
        upipe_err_va(upipe, "Couldn't set # of buffers (%m)");
        return UBASE_ERR_EXTERNAL;
    }

    snprintf(path, sizeof(path), dev_fmt, upipe_dveo_asi_sink->card_idx);
    int fd = open(path, O_WRONLY | O_NONBLOCK | O_CLOEXEC);
    if (unlikely(fd < 0)) {
        upipe_err_va(upipe, "can't open file %s (%m)", path);
        return UBASE_ERR_EXTERNAL;
    }

    snprintf(sys, sizeof(sys), dvbm_sys_fmt, upipe_dveo_asi_sink->card_idx, "bypass_mode");
    snprintf(buf, sizeof(buf), "%u", BYPASS_MODE);
    util_write(sys, buf, sizeof(buf)); /* Not all cards have this so don't fail */

    unsigned int cap;
    if (ioctl(fd, ASI_IOC_TXGETCAP, &cap) < 0) {
        upipe_err_va(upipe, "can't get tx caps (%m)");
        goto error;
    }

    unsigned long int bufsize;
    snprintf(sys, sizeof(sys), sys_fmt, upipe_dveo_asi_sink->card_idx, "bufsize");
    if (util_strtoul(sys, &bufsize) < 0) {
        upipe_err(upipe, "Couldn't read buffer size");
        goto error;
    }

    unsigned long int mode;
    snprintf(sys, sizeof(sys), sys_fmt, upipe_dveo_asi_sink->card_idx, "mode");
    if (util_strtoul(sys, &mode) < 0) {
        upipe_err(upipe, "Couldn't read buffer size");
        goto error;
    }

    switch (mode) {
        case ASI_CTL_TX_MODE_MAKE204:
            upipe_dbg(upipe, "Appending 0x00 bytes to make 204 bytes packets");
        case ASI_CTL_TX_MODE_188:
        case ASI_CTL_TX_MODE_204:
            break;
        default:
            upipe_err_va(upipe, "Unknown TX mode %lu", mode);
            goto error;
    }

    unsigned long int clock_source = 0;
    if (cap & ASI_CAP_TX_SETCLKSRC) {
        snprintf(sys, sizeof(sys), sys_fmt, upipe_dveo_asi_sink->card_idx, "clock_source");
        if (util_strtoul(sys, &clock_source) < 0) {
            upipe_err(upipe, "Couldn't read clock source");
            goto error;
        }
    }

    switch (clock_source) {
        case ASI_CTL_TX_CLKSRC_ONBOARD:
            upipe_dbg(upipe, "Using onboard oscillator");
            break;
        case ASI_CTL_TX_CLKSRC_EXT:
            upipe_dbg(upipe, "Using external reference clock");
            break;
        case ASI_CTL_TX_CLKSRC_RX:
            upipe_dbg(upipe, "Using recovered receive clock");
            break;
        default:
            upipe_dbg(upipe, "Unknown clock source");
                break;
    }

    if (!(cap & ASI_CAP_TX_TIMESTAMPS)) {
        upipe_err(upipe, "Device does not support timestamps");
        goto error;
    }

    upipe_dveo_asi_sink->fd = fd;

    upipe_notice_va(upipe, "opening file %s", path);
    return UBASE_ERR_NONE;

error:
    close(fd);
    return UBASE_ERR_EXTERNAL;
}
Пример #17
0
/** @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);
    }
}
Пример #18
0
/** @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
 */
static bool upipe_s337_encaps_handle(struct upipe *upipe, struct uref *uref,
                                    struct upump **upump_p)
{
    struct upipe_s337_encaps *upipe_s337_encaps = upipe_s337_encaps_from_upipe(upipe);

    if (!upipe_s337_encaps->ubuf_mgr)
        return false;

    size_t block_size;
    if (!ubase_check(uref_block_size(uref, &block_size))) {
        upipe_err(upipe, "Couldn't read block size");
        uref_free(uref);
        return true;
    }

    struct ubuf *ubuf = ubuf_sound_alloc(upipe_s337_encaps->ubuf_mgr, A52_FRAME_SAMPLES);
    if (!ubuf)
        return false;

    if (block_size / 2 > A52_FRAME_SAMPLES - 4) {
        upipe_err_va(upipe, "AC-3 block size %zu too big", block_size);
        block_size = (A52_FRAME_SAMPLES - 4) * 2;
    }

    int32_t *out_data;
    ubuf_sound_write_int32_t(ubuf, 0, -1, &out_data, 1);

    /* Pa, Pb, Pc, Pd */
    out_data[0] = (S337_PREAMBLE_A1 << 24) | (S337_PREAMBLE_A2 << 16);
    out_data[1] = (S337_PREAMBLE_B1 << 24) | (S337_PREAMBLE_B2 << 16);
    out_data[2] = (S337_TYPE_A52 << 16) | (S337_MODE_16 << 21) |
        (S337_TYPE_A52_REP_RATE_FLAG << 24);
    out_data[3] = ((block_size * 8) & 0xffff) << 16;

    int offset = 0;
    uint8_t tmp = 0;
    while (block_size) {
        const uint8_t *buf;
        int size = block_size;

        if (!ubase_check(uref_block_read(uref, offset, &size, &buf)) || size == 0) {
            ubuf_sound_unmap(ubuf, 0, -1, 1);
            ubuf_free(ubuf);
            uref_free(uref);
            return true;
        }

        if (offset & 1) {
            out_data[4 + offset/2] = (tmp << 24) | (buf[0] << 16);
            buf++;
            offset++;
            block_size--;
            size--;
        }

        for (int i = 0; i < size/2; i++)
            out_data[4 + offset/2 + i] = (buf[2*i + 0] << 24) | (buf[2*i + 1] << 16);
        if (size & 1)
            tmp = buf[size-1];

        uref_block_unmap(uref, offset);

        block_size -= size;
        offset += size;
    }

    memset(&out_data[4 + offset/2], 0, 4 * (A52_FRAME_SAMPLES*2 - 4 - offset/2));

    ubuf_sound_unmap(ubuf, 0, -1, 1);

    uref_attach_ubuf(uref, ubuf);
    upipe_s337_encaps_output(upipe, uref, upump_p);
    return true;
}