Esempio n. 1
0
/** @internal @This checks and parses a "#EXT-X-BYTERANGE" 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_byte_range(struct upipe *upipe,
                                               struct uref *flow_def,
                                               const char *line)
{
    const char *def;
    struct uref *item;
    UBASE_RETURN(uref_flow_get_def(flow_def, &def));
    if (strcmp(def, M3U_FLOW_DEF) && strcmp(def, PLAYLIST_FLOW_DEF))
        return UBASE_ERR_INVALID;
    UBASE_RETURN(uref_flow_set_def(flow_def, PLAYLIST_FLOW_DEF));
    UBASE_RETURN(upipe_m3u_reader_get_item(upipe, flow_def, &item));

    char *endptr = NULL;
    unsigned long long byte_range_len = strtoull(line, &endptr, 10);
    if (endptr == line || (*endptr != '\0' && *endptr != '@')) {
        upipe_warn_va(upipe, "invalid byte range %s", line);
        return UBASE_ERR_INVALID;
    }

    upipe_verbose_va(upipe, "byte range length: %"PRIu64, byte_range_len);
    if (*endptr == '@') {
        line = endptr + 1;
        unsigned long long byte_range_off = strtoull(line, &endptr, 10);
        if (endptr == line || *endptr != '\0') {
            upipe_warn_va(upipe, "invalid byte range %s", line);
            return UBASE_ERR_INVALID;
        }

        upipe_verbose_va(upipe, "byte range offset: %"PRIu64, byte_range_off);
        UBASE_RETURN(uref_m3u_playlist_set_byte_range_off(
                item, byte_range_off));
    }

    return uref_m3u_playlist_set_byte_range_len(item, byte_range_len);
}
Esempio n. 2
0
/** @internal @This checks and parses a "#EXT-X-STREAM-INF" 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_ext_x_stream_inf(struct upipe *upipe,
                                             struct uref *flow_def,
                                             const char *line)
{
    if (!ubase_check(uref_flow_match_def(flow_def, M3U_FLOW_DEF)) &&
        !ubase_check(uref_flow_match_def(flow_def, MASTER_FLOW_DEF)))
        return UBASE_ERR_INVALID;
    UBASE_RETURN(uref_flow_set_def(flow_def, MASTER_FLOW_DEF));

    struct uref *item;
    UBASE_RETURN(upipe_m3u_reader_get_item(upipe, flow_def, &item));

    const char *iterator = line;
    struct ustring name, value;
    while (ubase_check(attribute_iterate(&iterator, &name, &value)) &&
           iterator != NULL) {
        char value_str[value.len + 1];
        int err = ustring_cpy(value, value_str, sizeof (value_str));
        if (unlikely(!ubase_check(err))) {
            upipe_err_va(upipe, "fail to copy ustring %.*s",
                         (int)value.len, value.at);
            continue;
        }

        if (!ustring_cmp_str(name, "BANDWIDTH")) {
            char *endptr;
            uint64_t bandwidth = strtoull(value_str, &endptr, 10);
            if (endptr == value_str)
                return UBASE_ERR_INVALID;
            err = uref_m3u_master_set_bandwidth(item, bandwidth);
            if (unlikely(!ubase_check(err)))
                upipe_err_va(upipe, "fail to set bandwidth to %s", value_str);
        }
        else if (!ustring_cmp_str(name, "CODECS")) {
            err = uref_m3u_master_set_codecs(item, value_str);
            if (unlikely(!ubase_check(err)))
                upipe_err_va(upipe, "fail to set codecs to %s", value_str);
        }
        else if (!ustring_cmp_str(name, "AUDIO")) {
            err = uref_m3u_master_set_audio(item, value_str);
            if (unlikely(!ubase_check(err)))
                upipe_err_va(upipe, "fail to set audio to %s", value_str);
        }
        else if (!ustring_cmp_str(name, "RESOLUTION")) {
            err = uref_m3u_master_set_resolution(item, value_str);
            if (unlikely(!ubase_check(err)))
                upipe_err_va(upipe, "fail to set resolution to %s", value_str);
        }
        else {
            upipe_warn_va(upipe, "ignoring attribute %.*s (%.*s)",
                          (int)name.len, name.at,
                          (int)value.len, value.at);
        }
    }

    return UBASE_ERR_NONE;
}
Esempio n. 3
0
static bool upipe_dveo_asi_sink_write(struct upipe *upipe, struct uref *uref,
        bool *reset_first_timestamp)
{
    struct upipe_dveo_asi_sink *upipe_dveo_asi_sink = upipe_dveo_asi_sink_from_upipe(upipe);
    for (;;) {
        int iovec_count = uref_block_iovec_count(uref, 0, -1);
        if (unlikely(iovec_count == -1)) {
            upipe_warn(upipe, "cannot read ubuf buffer");
            break;
        }
        if (unlikely(iovec_count == 0)) {
            break;
        }

        struct iovec iovecs[iovec_count];
        if (unlikely(!ubase_check(uref_block_iovec_read(uref, 0, -1,
                                                        iovecs)))) {
            upipe_warn(upipe, "cannot read ubuf buffer");
            break;
        }

        ssize_t ret = writev(upipe_dveo_asi_sink->fd, iovecs, iovec_count);
        uref_block_iovec_unmap(uref, 0, -1, iovecs);

        if (unlikely(ret == -1)) {
            switch (errno) {
                case EINTR:
                    continue;
                case EAGAIN:
#if EAGAIN != EWOULDBLOCK
                case EWOULDBLOCK:
#endif
                    //upipe_notice_va(upipe, "polling");
                    upipe_dveo_asi_sink_poll(upipe);
                    return false;
                default:
                    break;
            }
            upipe_warn_va(upipe, "write error to device %d (%m)", upipe_dveo_asi_sink->card_idx);
            upipe_dveo_asi_sink_set_upump(upipe, NULL);
            upipe_throw_sink_end(upipe);
            break;
        }

        size_t uref_size;
        if (ubase_check(uref_block_size(uref, &uref_size)) &&
            uref_size == ret) {
            /* wrote succeeded */
            *reset_first_timestamp = false;
            break;
        }
        uref_block_resize(uref, ret, -1);
    }

    return true;
}
Esempio n. 4
0
/** @internal @This changes the rotate interval
 *
 * @param upipe description structure of the pipe
 * @param rotate new rotate interval
 * @return false in case of error
 */
static bool  _upipe_multicat_probe_set_rotate(struct upipe *upipe, uint64_t rotate)
{
    struct upipe_multicat_probe *upipe_multicat_probe = upipe_multicat_probe_from_upipe(upipe);
    if (unlikely(rotate < 1)) {
        upipe_warn_va(upipe, "invalid rotate interval (%"PRIu64" < 1)", rotate);
        return false;
    }
    upipe_multicat_probe->rotate = rotate;
    upipe_notice_va(upipe, "setting rotate: %"PRIu64, rotate);
    return true;
}
Esempio n. 5
0
/** @internal @This handles input.
 *
 * @param upipe description structure of the pipe
 * @param uref uref structure
 * @param upump_p reference to pump that generated the buffer
 */
static void upipe_htons_input(struct upipe *upipe, struct uref *uref,
                              struct upump **upump_p)
{
    struct ubuf *ubuf;
    size_t size = 0;
    int remain, bufsize = -1, offset = 0;
    uint8_t *buf = NULL;

    /* block size */
    if (unlikely(!ubase_check(uref_block_size(uref, &size)))) {
        upipe_warn(upipe, "could not read uref block size");
        uref_free(uref);
        return;
    }
    /* copy ubuf if shared or not 16b-unaligned or segmented */
    bufsize = -1;
    if (!ubase_check(uref_block_write(uref, 0, &bufsize, &buf)) ||
        ((uintptr_t)buf & 1) || bufsize != size) {
        ubuf = ubuf_block_copy(uref->ubuf->mgr, uref->ubuf, 0, size);
        if (unlikely(!ubuf)) {
            upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
            uref_free(uref);
            return;
        }
        uref_attach_ubuf(uref, ubuf);
    } else {
        uref_block_unmap(uref, 0);
    }

    /* process ubuf chunks */
    while (size > 0) {
        bufsize = -1;
        if (unlikely(!ubase_check(uref_block_write(uref, offset, &bufsize,
                                  &buf)))) {
            upipe_warn(upipe, "unexpected buffer error");
            uref_free(uref);
            return;
        }
        if (unlikely((uintptr_t)buf & 1)) {
            upipe_warn_va(upipe, "unaligned buffer: %p", buf);
        }
        for (remain = bufsize; remain > 1; remain -= 2) {
            *(uint16_t *)buf = htons(*(uint16_t *)buf);
            buf += 2;
        }

        uref_block_unmap(uref, offset);
        offset += bufsize;
        size -= bufsize;
    }

    upipe_htons_output(upipe, uref, upump_p);
}
Esempio n. 6
0
/** @internal @This handles inner reader pipe events.
 *
 * @param uprobe structure used to raise events
 * @param inner pointer to inner pipe
 * @param event event thrown
 * @param args optional arguments
 * @return an error code
 */
static int probe_reader(struct uprobe *uprobe, struct upipe *inner,
                        int event, va_list args)
{
    struct upipe_hls *upipe_hls = upipe_hls_from_probe_reader(uprobe);
    struct upipe *upipe = upipe_hls_to_upipe(upipe_hls);

    if (event >= UPROBE_LOCAL)
        return UBASE_ERR_NONE;

    if (event == UPROBE_NEED_OUTPUT) {
        struct uref *flow_format = va_arg(args, struct uref *);
        const char *def;
        UBASE_RETURN(uref_flow_get_def(flow_format, &def));

        struct uref *flow_format_dup = uref_dup(flow_format);
        if (unlikely(!flow_format_dup)) {
            upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
            return UBASE_ERR_ALLOC;
        }
        upipe_hls_store_flow_def(upipe, flow_format_dup);

        if (!strcmp(def, "block.m3u.playlist.")) {
            struct upipe_mgr *upipe_null_mgr = upipe_null_mgr_alloc();
            struct upipe *output = upipe_void_alloc_output(
                inner, upipe_null_mgr,
                uprobe_pfx_alloc(uprobe_use(&upipe_hls->probe_null),
                                 UPROBE_LOG_VERBOSE, "null"));
            upipe_mgr_release(upipe_null_mgr);
            upipe_release(output);

            upipe_split_throw_update(upipe);
            return UBASE_ERR_NONE;
        }
        else if (!strcmp(def, "block.m3u.master.")) {
            struct upipe_mgr *upipe_hls_master_mgr =
                upipe_hls_master_mgr_alloc();
            UBASE_ALLOC_RETURN(upipe_hls_master_mgr);
            struct upipe *output = upipe_void_alloc_output(
                inner, upipe_hls_master_mgr,
                uprobe_pfx_alloc(uprobe_use(&upipe_hls->probe_master),
                                 UPROBE_LOG_VERBOSE, "master"));
            upipe_mgr_release(upipe_hls_master_mgr);
            UBASE_ALLOC_RETURN(output);
            upipe_hls_store_bin_output(upipe, output);
            return UBASE_ERR_NONE;
        }
        else
            upipe_warn_va(upipe, "unsupported flow format %s", def);
        return UBASE_ERR_INVALID;
    }

    return upipe_throw_proxy(upipe, inner, event, args);
}
Esempio n. 7
0
/** @internal @This checks and parses a "#EXT-X-VERSION" 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_version(struct upipe *upipe,
                                            struct uref *flow_def,
                                            const char *line)
{
    UBASE_RETURN(uref_flow_match_def(flow_def, M3U_FLOW_DEF));

    char *endptr;
    unsigned long int version = strtoul(line, &endptr, 10);
    if (line == endptr || *endptr != '\0' || version > UINT8_MAX) {
        upipe_warn_va(upipe, "invalid version %s", line);
        return UBASE_ERR_INVALID;
    }
    upipe_dbg_va(upipe, "version: %u", version);
    return uref_m3u_flow_set_version(flow_def, version);
}
Esempio n. 8
0
/** @internal @This receives 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_agg_input(struct upipe *upipe, struct uref *uref,
                            struct upump **upump_p)
{
    struct upipe_agg *upipe_agg = upipe_agg_from_upipe(upipe);
    size_t size = 0;
    const size_t output_size = upipe_agg->output_size;

    uref_block_size(uref, &size);

    /* check for invalid or too large size */
    if (unlikely(size == 0 || size > output_size)) {
        upipe_warn_va(upipe,
            "received packet of invalid size: %zu (output_size == %zu)", size, output_size);
        uref_free(uref);
        return;
    }

    /* flush if incoming packet makes aggregated overflow */
    if (upipe_agg->size + size > output_size) {
        upipe_agg_output(upipe, upipe_agg->aggregated, upump_p);
        upipe_agg->aggregated = NULL;
    }

    /* keep or attach incoming packet */
    if (unlikely(!upipe_agg->aggregated)) {
        upipe_agg->aggregated = uref;
        upipe_agg->size = size;
    } else {
        struct ubuf *append = uref_detach_ubuf(uref);
        uref_free(uref);
        if (unlikely(!ubase_check(uref_block_append(upipe_agg->aggregated,
                                                    append)))) {
            upipe_warn(upipe, "error appending packet");
            ubuf_free(append);
            return;
        };
        upipe_agg->size += size;
    }

    /* anticipate next packet size and flush now if necessary */
    if (upipe_agg->input_size)
        size = upipe_agg->input_size;
    if (unlikely(upipe_agg->size + size > output_size)) {
        upipe_agg_output(upipe, upipe_agg->aggregated, upump_p);
        upipe_agg->aggregated = NULL;
        upipe_agg->size = 0;
    }
}
Esempio n. 9
0
/** @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;
}
Esempio n. 10
0
/** @internal @This checks and parses a "#EXT-X-MEDIA-SEQUENCE" 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_ext_x_media_sequence(struct upipe *upipe,
                                                 struct uref *flow_def,
                                                 const char *line)
{
    const char *def;
    UBASE_RETURN(uref_flow_get_def(flow_def, &def));
    if (strcmp(def, M3U_FLOW_DEF) && strcmp(def, PLAYLIST_FLOW_DEF))
        return UBASE_ERR_INVALID;
    UBASE_RETURN(uref_flow_set_def(flow_def, PLAYLIST_FLOW_DEF));

    char *endptr;
    uint64_t media_sequence = strtoull(line, &endptr, 10);
    if (endptr == line || strlen(endptr)) {
        upipe_warn_va(upipe, "invalid media sequence %s", line);
        return UBASE_ERR_INVALID;
    }
    upipe_dbg_va(upipe, "media sequence %"PRIu64, media_sequence);
    return uref_m3u_playlist_flow_set_media_sequence(flow_def, media_sequence);
}
Esempio n. 11
0
/** @internal @This checks and parses a "#EXT-X-TARGETDURATION" 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_target_duration(struct upipe *upipe,
                                                    struct uref *flow_def,
                                                    const char *line)
{
    const char *def;
    UBASE_RETURN(uref_flow_get_def(flow_def, &def));
    if (strcmp(def, M3U_FLOW_DEF) && strcmp(def, PLAYLIST_FLOW_DEF))
        return UBASE_ERR_INVALID;
    UBASE_RETURN(uref_flow_set_def(flow_def, PLAYLIST_FLOW_DEF));

    const char *endptr;
    uint64_t duration;
    UBASE_RETURN(duration_to_uclock(line, &endptr, &duration));
    if (line == endptr || strlen(endptr)) {
        upipe_warn_va(upipe, "invalid target duration %s", line);
        return UBASE_ERR_INVALID;
    }
    upipe_dbg_va(upipe, "target duration: %"PRIu64, duration);
    return uref_m3u_playlist_flow_set_target_duration(flow_def, duration);
}
Esempio n. 12
0
/** @This sets the low resolution parameter, if supported by codec.
 * If some codec is already used, it is re-opened.
 *
 * @param upipe description structure of the pipe
 * @param lowres lowres parameter (0=disabled)
 * @return false in case of error
 */
static bool _upipe_avcdec_set_lowres(struct upipe *upipe, int lowres)
{
    struct upipe_avcdec *upipe_avcdec = upipe_avcdec_from_upipe(upipe);
    const char *codec_def;
    bool ret = true;

    if (lowres < 0) {
        upipe_warn_va(upipe, "Invalid lowres parameter (%d)", lowres);
        return false;
    }
    upipe_avcdec->lowres = lowres;
    upipe_dbg_va(upipe, "Requesting lowres %d", lowres);

    if (upipe_avcdec->context && upipe_avcdec->context->codec) {
        codec_def = upipe_av_to_flow_def(upipe_avcdec->context->codec->id);
        ret = _upipe_avcdec_set_codec(upipe, codec_def, upipe_avcdec->context->extradata,
                                upipe_avcdec->context->extradata_size);
    }
    return ret;
}
Esempio n. 13
0
/** @internal @This checks and parses a "#EXT-X-PLAYLIST-TYPE" 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_playlist_type(struct upipe *upipe,
                                                  struct uref *flow_def,
                                                  const char *line)
{
    const char *def;
    UBASE_RETURN(uref_flow_get_def(flow_def, &def));
    if (strcmp(def, M3U_FLOW_DEF) && strcmp(def, PLAYLIST_FLOW_DEF))
        return UBASE_ERR_INVALID;
    UBASE_RETURN(uref_flow_set_def(flow_def, PLAYLIST_FLOW_DEF));

    static const char *types[] = { "VOD", "EVENT" };
    const char *type = NULL;
    for (unsigned i = 0; !type && i < UBASE_ARRAY_SIZE(types); i++)
        if (!strcmp(line, types[i]))
            type = types[i];
    if (unlikely(type == NULL)) {
        upipe_warn_va(upipe, "invalid playlist type `%s'", line);
        return UBASE_ERR_INVALID;
    }
    upipe_dbg_va(upipe, "playlist type %s", type);
    return uref_m3u_playlist_flow_set_type(flow_def, type);
}
Esempio n. 14
0
/** @internal @This checks the presence of the sync word.
 *
 * @param upipe description structure of the pipe
 * @param uref uref structure
 * @param upump_p reference to pump that generated the buffer
 */
static bool upipe_ts_check_check(struct upipe *upipe, struct uref *uref,
                                 struct upump **upump_p)
{
    const uint8_t *buffer;
    int size = 1;
    uint8_t word;
    if (unlikely(!ubase_check(uref_block_read(uref, 0, &size, &buffer)))) {
        uref_free(uref);
        upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
        return false;
    }
    assert(size == 1);
    word = *buffer;
    uref_block_unmap(uref, 0);
    if (word != TS_SYNC) {
        uref_free(uref);
        upipe_warn_va(upipe, "invalid TS sync 0x%"PRIx8, word);
        return false;
    }

    upipe_ts_check_output(upipe, uref, upump_p);
    return true;
}
Esempio n. 15
0
/** @internal @This provides a ubuf_mgr request.
 *
 * @param upipe description structure of the pipe
 * @param flow_format amended flow format
 * @return an error code
 */
static int upipe_audiobar_check_ubuf_mgr(struct upipe *upipe,
                                         struct uref *flow_format)
{
    struct upipe_audiobar *upipe_audiobar = upipe_audiobar_from_upipe(upipe);
    if (flow_format == NULL)
        return UBASE_ERR_NONE;

    upipe_audiobar_store_flow_def(upipe, flow_format);
    UBASE_RETURN(uref_pic_flow_get_hsize(flow_format, &upipe_audiobar->hsize))
    UBASE_RETURN(uref_pic_flow_get_vsize(flow_format, &upipe_audiobar->vsize))
    upipe_audiobar->chan_width =
        upipe_audiobar->hsize / upipe_audiobar->channels;
    upipe_audiobar->chan_width -= upipe_audiobar->chan_width % 2;
    if (unlikely(upipe_audiobar->chan_width < 16)) {
        upipe_warn_va(upipe,
                "channel width is too small to have a separation (%"PRIu64")",
                upipe_audiobar->chan_width);
    }
    upipe_audiobar->sep_width = upipe_audiobar->chan_width / 4;
    upipe_audiobar->sep_width -= upipe_audiobar->sep_width % 4;
    upipe_audiobar->pad_width = upipe_audiobar->hsize -
        upipe_audiobar->chan_width * upipe_audiobar->channels;
    upipe_notice_va(upipe,
            "setting up chan %"PRIu64" sep %"PRIu64" pad %"PRIu64,
            upipe_audiobar->chan_width, upipe_audiobar->sep_width,
            upipe_audiobar->pad_width);

    bool was_buffered = !upipe_audiobar_check_input(upipe);
    upipe_audiobar_output_input(upipe);
    upipe_audiobar_unblock_input(upipe);
    if (was_buffered && upipe_audiobar_check_input(upipe)) {
        /* All packets have been output, release again the pipe that has been
         * used in @ref upipe_audiobar_input. */
        upipe_release(upipe);
    }
    return UBASE_ERR_NONE;
}
Esempio n. 16
0
/** @internal @This parses and removes the PES header of a packet.
 *
 * @param upipe description structure of the pipe
 * @param upump_p reference to pump that generated the buffer
 */
static void upipe_ts_pesd_decaps(struct upipe *upipe, struct upump **upump_p)
{
    struct upipe_ts_pesd *upipe_ts_pesd = upipe_ts_pesd_from_upipe(upipe);
    uint8_t buffer[PES_HEADER_SIZE];
    const uint8_t *pes_header = uref_block_peek(upipe_ts_pesd->next_uref,
                                                0, PES_HEADER_SIZE, buffer);
    if (unlikely(pes_header == NULL))
        return;

    bool validate = pes_validate(pes_header);
    uint8_t streamid = pes_get_streamid(pes_header);
    uint16_t length = pes_get_length(pes_header);
    UBASE_FATAL(upipe, uref_block_peek_unmap(upipe_ts_pesd->next_uref, 0,
                                             buffer, pes_header))

    if (unlikely(!validate)) {
        upipe_warn(upipe, "wrong PES header");
        upipe_ts_pesd_flush(upipe);
        return;
    }

    if (unlikely(streamid == PES_STREAM_ID_PADDING)) {
        upipe_ts_pesd_flush(upipe);
        return;
    }

    if (length)
        upipe_ts_pesd->next_pes_size = length + PES_HEADER_SIZE;
    else
        upipe_ts_pesd->next_pes_size = 0;

    if (streamid == PES_STREAM_ID_PSM ||
        streamid == PES_STREAM_ID_PRIVATE_2 ||
        streamid == PES_STREAM_ID_ECM ||
        streamid == PES_STREAM_ID_EMM ||
        streamid == PES_STREAM_ID_PSD ||
        streamid == PES_STREAM_ID_DSMCC ||
        streamid == PES_STREAM_ID_H222_1_E) {
        UBASE_FATAL(upipe, uref_block_resize(upipe_ts_pesd->next_uref,
                                             PES_HEADER_SIZE, -1))
        upipe_ts_pesd_check_output(upipe, upump_p);
        return;
    }

    if (unlikely(length != 0 && length < PES_HEADER_OPTIONAL_SIZE)) {
        upipe_warn(upipe, "wrong PES length");
        upipe_ts_pesd_flush(upipe);
        return;
    }

    uint8_t buffer2[PES_HEADER_SIZE_NOPTS - PES_HEADER_SIZE];
    pes_header = uref_block_peek(upipe_ts_pesd->next_uref, PES_HEADER_SIZE,
                                 PES_HEADER_OPTIONAL_SIZE, buffer2);
    if (unlikely(pes_header == NULL))
        return;

    validate = pes_validate_header(pes_header - PES_HEADER_SIZE);
    bool alignment = pes_get_dataalignment(pes_header - PES_HEADER_SIZE);
    bool has_pts = pes_has_pts(pes_header - PES_HEADER_SIZE);
    bool has_dts = pes_has_dts(pes_header - PES_HEADER_SIZE);
    uint8_t headerlength = pes_get_headerlength(pes_header - PES_HEADER_SIZE);
    UBASE_FATAL(upipe, uref_block_peek_unmap(upipe_ts_pesd->next_uref,
                PES_HEADER_SIZE, buffer2, pes_header))

    if (unlikely(!validate)) {
        upipe_warn(upipe, "wrong PES optional header");
        upipe_ts_pesd_flush(upipe);
        return;
    }

    if (unlikely((length != 0 &&
                  headerlength + PES_HEADER_OPTIONAL_SIZE > length) ||
                 (has_pts && headerlength < PES_HEADER_SIZE_PTS -
                                            PES_HEADER_SIZE_NOPTS) ||
                 (has_dts && headerlength < PES_HEADER_SIZE_PTSDTS -
                                            PES_HEADER_SIZE_NOPTS))) {
        upipe_warn(upipe, "wrong PES header length");
        upipe_ts_pesd_flush(upipe);
        return;
    }

    if (upipe_ts_pesd->next_uref_size < PES_HEADER_SIZE_NOPTS + headerlength)
        return;

    if (has_pts) {
        uint8_t buffer3[PES_HEADER_TS_SIZE * 2];
        uint64_t pts, dts;
        const uint8_t *ts_fields = uref_block_peek(upipe_ts_pesd->next_uref,
                PES_HEADER_SIZE_NOPTS, PES_HEADER_TS_SIZE * (has_dts ? 2 : 1),
                buffer3);
        if (unlikely(ts_fields == NULL)) {
            upipe_ts_pesd_flush(upipe);
            upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
            return;
        }

        validate = pes_validate_pts(ts_fields - PES_HEADER_SIZE_NOPTS);
        pts = pes_get_pts(ts_fields - PES_HEADER_SIZE_NOPTS);
        if (has_dts) {
            validate = validate &&
                       pes_validate_dts(ts_fields - PES_HEADER_SIZE_NOPTS);
            dts = pes_get_dts(ts_fields - PES_HEADER_SIZE_NOPTS);
        } else
            dts = pts;
        UBASE_FATAL(upipe, uref_block_peek_unmap(upipe_ts_pesd->next_uref,
                                    PES_HEADER_SIZE_NOPTS, buffer3, ts_fields))

        if (unlikely(!validate)) {
            upipe_warn(upipe, "wrong PES timestamp syntax");
#if 0
            /* disable this because it is a common syntax error */
            upipe_ts_pesd_flush(upipe);
            return;
#endif
        }

        uint64_t dts_pts_delay = (POW2_33 + pts - dts) % POW2_33;
        if (dts_pts_delay > POW2_33 / 2) {
            upipe_warn_va(upipe, "invalid PTS field (%"PRIu64" < %"PRIu64")",
                          pts, dts);
            dts_pts_delay = 0;
        }
        dts_pts_delay *= UCLOCK_FREQ / 90000;
        dts *= UCLOCK_FREQ / 90000;
        uref_clock_set_dts_orig(upipe_ts_pesd->next_uref, dts);
        uref_clock_set_dts_pts_delay(upipe_ts_pesd->next_uref, dts_pts_delay);
        upipe_throw_clock_ts(upipe, upipe_ts_pesd->next_uref);
    }

    if (alignment)
        uref_flow_set_random(upipe_ts_pesd->next_uref);

    UBASE_FATAL(upipe, uref_block_resize(upipe_ts_pesd->next_uref,
                            PES_HEADER_SIZE_NOPTS + headerlength, -1))
    upipe_ts_pesd_check_output(upipe, upump_p);
}
Esempio n. 17
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 inline void upipe_rtpd_input(struct upipe *upipe, struct uref *uref,
                                    struct upump **upump_p)
{
    struct upipe_rtpd *upipe_rtpd = upipe_rtpd_from_upipe(upipe);
    uint8_t rtp_buffer[RTP_HEADER_SIZE];
    const uint8_t *rtp_header = uref_block_peek(uref, 0, RTP_HEADER_SIZE, 
                                                rtp_buffer);
    if (unlikely(rtp_header == NULL)) {
        upipe_warn(upipe, "invalid buffer received");
        uref_free(uref);
        return;
    }

    bool valid = rtp_check_hdr(rtp_header);
    bool extension = rtp_check_extension(rtp_header);
    uint8_t cc = rtp_get_cc(rtp_header);
    uint8_t type = rtp_get_type(rtp_header);
    uint16_t seqnum = rtp_get_seqnum(rtp_header);
    ptrdiff_t extension_offset = rtp_extension((uint8_t *)rtp_header) -
                                 rtp_header;
    uref_block_peek_unmap(uref, 0, rtp_buffer, rtp_header);

    if (unlikely(!valid)) {
        upipe_warn(upipe, "invalid RTP header");
        uref_free(uref);
        return;
    }

    size_t offset = RTP_HEADER_SIZE + 4 * cc;

    if (extension) {
        rtp_header = uref_block_peek(uref, extension_offset,
                                     RTP_EXTENSION_SIZE, rtp_buffer);
        if (unlikely(rtp_header == NULL)) {
            upipe_warn(upipe, "invalid buffer received");
            uref_free(uref);
            return;
        }
        offset += 4 * (1 + rtpx_get_length(rtp_header));
        uref_block_peek_unmap(uref, extension_offset, rtp_buffer, rtp_header);
    }

    if (unlikely(upipe_rtpd->expected_seqnum != -1 &&
                 seqnum != upipe_rtpd->expected_seqnum)) {
        upipe_warn_va(upipe, "potentially lost %d RTP packets",
                      (seqnum + UINT16_MAX + 1 - upipe_rtpd->expected_seqnum) &
                      UINT16_MAX);
        uref_flow_set_discontinuity(uref);
    }
    upipe_rtpd->expected_seqnum = seqnum + 1;
    upipe_rtpd->expected_seqnum &= UINT16_MAX;

    if (unlikely(type != upipe_rtpd->type)) {
        assert(upipe_rtpd->flow_def_input != NULL);
        struct uref *flow_def = uref_dup(upipe_rtpd->flow_def_input);
        if (unlikely(flow_def == NULL)) {
            uref_free(uref);
            upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
            return;
        }

        switch (type) {
            case RTP_TYPE_TS:
                uref_flow_set_def(flow_def, "block.mpegtsaligned.");
                break;
            default:
                break;
        }
        upipe_rtpd->type = type;
        upipe_rtpd_store_flow_def(upipe, flow_def);
    }

    uref_block_resize(uref, offset, -1);
    upipe_rtpd_output(upipe, uref, upump_p);
}
Esempio n. 18
0
/** @internal @This outputs data to the file sink.
 *
 * @param upipe description structure of the pipe
 * @param uref uref structure
 * @param upump_p reference to pump that generated the buffer
 * @return true if the uref was processed
 */
static bool upipe_dveo_asi_sink_output(struct upipe *upipe, struct uref *uref,
                               struct upump **upump_p)
{
    struct upipe_dveo_asi_sink *upipe_dveo_asi_sink = upipe_dveo_asi_sink_from_upipe(upipe);
    const char *def;
    if (unlikely(ubase_check(uref_flow_get_def(uref, &def)))) {
        uref_free(uref);
        return true;
    }

    int fd = upipe_dveo_asi_sink->fd;

    if (unlikely(fd == -1)) {
        upipe_warn(upipe, "received a buffer before opening the device");
        uref_free(uref);
        return true;
    }

    uint64_t cr_sys = 0;
    if (unlikely(!ubase_check(uref_clock_get_cr_sys(uref, &cr_sys))) || cr_sys == -1) {
        upipe_warn(upipe, "received non-dated buffer");
        uref_free(uref);
        return true;
    }

    if (ubase_check(uref_flow_get_discontinuity(uref))) {
        upipe_warn_va(upipe, "DISCONTINUITY, resetting timestamp");
        upipe_dveo_asi_sink->first_timestamp = true;
    }

    if (unlikely(upipe_dveo_asi_sink->first_timestamp)) {
        int val;
        if (ioctl(fd, ASI_IOC_TXGETTXD, &val) < 0) {
            upipe_err_va(upipe, "ioctl TXGETTXDfailed (%m)");
            upipe_throw_fatal(upipe, UBASE_ERR_UNKNOWN);
            uref_free(uref);
            return true;
        } else if (val) {
            upipe_warn(upipe, "Waiting for transmission to stop");
            uref_free(uref);
            return true;
        }
    }

    /* Make sure we set the counter */
    bool reset_first_timestamp = upipe_dveo_asi_sink->first_timestamp;

    if (upipe_dveo_asi_sink_add_header(upipe, uref, cr_sys)) {
        uref_free(uref);
        return true; /* invalid uref, discarded */
    }

    if (!upipe_dveo_asi_sink_write(upipe, uref, &reset_first_timestamp))
        return false; /* would block */

    uref_free(uref);

    if (reset_first_timestamp)
        upipe_dveo_asi_sink->first_timestamp = true;

    upipe_dveo_asi_sink_stats(upipe);

    return true;
}
Esempio n. 19
0
/** @internal @This outputs data to the file sink.
 *
 * @param upipe description structure of the pipe
 * @param uref uref structure
 * @param upump_p reference to pump that generated the buffer
 * @return true if the uref was processed
 */
static bool upipe_fsink_output(struct upipe *upipe, struct uref *uref,
                               struct upump **upump_p)
{
    struct upipe_fsink *upipe_fsink = upipe_fsink_from_upipe(upipe);
    const char *def;
    if (unlikely(ubase_check(uref_flow_get_def(uref, &def)))) {
        uint64_t latency = 0;
        uref_clock_get_latency(uref, &latency);
        if (latency > upipe_fsink->latency)
            upipe_fsink->latency = latency;
        uref_free(uref);
        return true;
    }

    if (unlikely(upipe_fsink->fd == -1)) {
        uref_free(uref);
        upipe_warn(upipe, "received a buffer before opening a file");
        return true;
    }

    if (likely(upipe_fsink->uclock == NULL))
        goto write_buffer;

    uint64_t cr_sys = 0;
    if (unlikely(!ubase_check(uref_clock_get_cr_sys(uref, &cr_sys)))) {
        upipe_warn(upipe, "received non-dated buffer");
        goto write_buffer;
    }

    uint64_t now = uclock_now(upipe_fsink->uclock);
    cr_sys += upipe_fsink->latency;
    if (unlikely(now < cr_sys)) {
        upipe_fsink_wait_upump(upipe, cr_sys - now, upipe_fsink_watcher);
        return false;
    }

write_buffer:
    for ( ; ; ) {
        int iovec_count = uref_block_iovec_count(uref, 0, -1);
        if (unlikely(iovec_count == -1)) {
            uref_free(uref);
            upipe_warn(upipe, "cannot read ubuf buffer");
            break;
        }
        if (unlikely(iovec_count == 0)) {
            uref_free(uref);
            break;
        }

        struct iovec iovecs[iovec_count];
        if (unlikely(!ubase_check(uref_block_iovec_read(uref, 0, -1,
                                                        iovecs)))) {
            uref_free(uref);
            upipe_warn(upipe, "cannot read ubuf buffer");
            break;
        }

        ssize_t ret = writev(upipe_fsink->fd, iovecs, iovec_count);
        uref_block_iovec_unmap(uref, 0, -1, iovecs);

        if (unlikely(ret == -1)) {
            switch (errno) {
                case EINTR:
                    continue;
                case EAGAIN:
#if EAGAIN != EWOULDBLOCK
                case EWOULDBLOCK:
#endif
                    upipe_fsink_poll(upipe);
                    return false;
                case EBADF:
                case EFBIG:
                case EINVAL:
                case EIO:
                case ENOSPC:
                case EPIPE:
                default:
                    break;
            }
            uref_free(uref);
            upipe_warn_va(upipe, "write error to %s (%m)", upipe_fsink->path);
            upipe_fsink_set_upump(upipe, NULL);
            upipe_throw_sink_end(upipe);
            return true;
        }

        size_t uref_size;
        if (ubase_check(uref_block_size(uref, &uref_size)) &&
            uref_size == ret) {
            uref_free(uref);
            break;
        }
        uref_block_resize(uref, ret, -1);
    }
    return true;
}
Esempio n. 20
0
/** @internal @This catches events of the video output of the demux.
 *
 * @param uprobe pointer to probe
 * @param upipe pointer to pipe throwing the event
 * @param event event thrown
 * @param args optional event-specific parameters
 * @return an error code
 */
static int upipe_glxplayer_catch_demux_output(struct uprobe *uprobe,
                                              struct upipe *upipe,
                                              int event, va_list args)
{
    struct upipe_glxplayer *glxplayer =
        container_of(uprobe, struct upipe_glxplayer, uprobe_demux_output_s);
    switch (event) {
        case UPROBE_NEED_OUTPUT: {
            struct uref *flow_def = va_arg(args, struct uref *);
            const char *def = "(none)";
            if (!ubase_check(uref_flow_get_def(flow_def, &def)) ||
                ubase_ncmp(def, "block.")) {
                upipe_warn_va(upipe, "flow def %s is not supported", def);
                return UBASE_ERR_UNHANDLED;
            }

            upipe_dbg_va(upipe, "add flow %s", def);

            /* prepare a queue to deport avcodec to a new thread */
            uprobe_throw(glxplayer->uprobe_logger, NULL, UPROBE_FREEZE_UPUMP_MGR);
            struct upipe *upipe_dec_qsrc =
                upipe_qsrc_alloc(glxplayer->upipe_qsrc_mgr,
                    uprobe_pfx_alloc_va(uprobe_use(&glxplayer->uprobe_dec_qsrc_s),
                                              glxplayer->loglevel, "dec qsrc"),
                    DEC_QUEUE_LENGTH);
            if (unlikely(upipe_dec_qsrc == NULL)) {
                return UBASE_ERR_ALLOC;
            }
            uprobe_throw(glxplayer->uprobe_logger, NULL, UPROBE_THAW_UPUMP_MGR);

            glxplayer->upipe_dec_qsink =
                upipe_qsink_alloc(glxplayer->upipe_qsink_mgr,
                    uprobe_pfx_alloc_va(
                            uprobe_use(glxplayer->uprobe_logger),
                            glxplayer->loglevel, "dec qsink"),
                    upipe_dec_qsrc);
            if (unlikely(glxplayer->upipe_dec_qsink == NULL)) {
                upipe_release(upipe_dec_qsrc);
                return UBASE_ERR_ALLOC;
            }
            upipe_set_output(upipe, glxplayer->upipe_dec_qsink);

            /* prepare to transfer the queue source */
            glxplayer->dec_xfer = upipe_xfer_mgr_alloc(XFER_QUEUE, XFER_POOL);
            if (unlikely(glxplayer->dec_xfer == NULL)) {
                upipe_release(upipe_dec_qsrc);
                return UBASE_ERR_ALLOC;
            }

            /* spawn a thread for the decoder */
            if (pthread_create(&glxplayer->dec_thread_id, NULL,
                               upipe_glxplayer_dec_thread, glxplayer)) {
                upipe_mgr_release(glxplayer->dec_xfer);
                upipe_release(upipe_dec_qsrc);
                return UBASE_ERR_ALLOC;
            }

            glxplayer->upipe_dec_qsrc_handle =
                upipe_xfer_alloc(glxplayer->dec_xfer,
                    uprobe_pfx_alloc(uprobe_use(glxplayer->uprobe_logger),
                                     glxplayer->loglevel, "dec qsrc xfer"),
                    upipe_dec_qsrc);
            if (unlikely(glxplayer->upipe_dec_qsrc_handle == NULL)) {
                upipe_mgr_release(glxplayer->dec_xfer);
                upipe_release(upipe_dec_qsrc);
                return UBASE_ERR_ALLOC;
            }
            upipe_attach_upump_mgr(glxplayer->upipe_dec_qsrc_handle);
            upipe_set_output(glxplayer->upipe_dec_qsink, glxplayer->upipe_dec_qsrc_handle);
            return UBASE_ERR_NONE;
        }
        case UPROBE_SOURCE_END: {
            upipe_flush(glxplayer->upipe_dec_qsink);
            upipe_release(glxplayer->upipe_dec_qsink);
            glxplayer->upipe_dec_qsink = NULL;

            /* set dec_qsrc output to null */
            struct upipe *null = upipe_void_alloc(glxplayer->upipe_null_mgr,
                    uprobe_pfx_alloc(uprobe_use(glxplayer->uprobe_logger),
                                     glxplayer->loglevel, "dec qsrc null"));
            if (likely(null != NULL)) {
                upipe_set_output(glxplayer->upipe_dec_qsrc_handle, null);
                upipe_release(null);
            }
            upipe_release(glxplayer->upipe_dec_qsrc_handle);
            return UBASE_ERR_NONE;
        }
        default:
            return uprobe_throw_next(uprobe, upipe, event, args);
    }
}
Esempio n. 21
0
/** @internal @This configures a new codec context
 *
 * @param upipe description structure of the pipe
 * @param codec avcodec description structure
 * @param extradata pointer to extradata buffer
 * @param extradata_size extradata size
 * @return false if the buffer couldn't be accepted
 */
static bool upipe_avcdec_open_codec(struct upipe *upipe, AVCodec *codec,
                                    uint8_t *extradata, int extradata_size)
{
    AVCodecContext *context = NULL;
    struct upipe_avcdec *upipe_avcdec = upipe_avcdec_from_upipe(upipe);
    assert(upipe);

    /* close previously opened context */
    if (unlikely(upipe_avcdec->context)) {
        /* first send empty packet to flush retained frames */
        upipe_dbg(upipe, "flushing frames in decoder");
        while (upipe_avcdec_process_buf(upipe, NULL, 0, NULL));

        /* now close codec and free extradata if any */
        upipe_notice_va(upipe, "avcodec context (%s) closed (%d)",
                    upipe_avcdec->context->codec->name, upipe_avcdec->counter);
        avcodec_close(upipe_avcdec->context);
        if (upipe_avcdec->context->extradata_size > 0) {
            free(upipe_avcdec->context->extradata);
        }
        av_free(upipe_avcdec->context);
        upipe_avcdec->context = NULL;
        upipe_avcdec_store_flow_def(upipe, NULL);
    }

    /* just closing, that's all */
    if (!codec) {
        upipe_release(upipe);
        return false;
    }

    /* allocate and configure codec context */
    context = avcodec_alloc_context3(codec);
    if (unlikely(!context)) {
        upipe_throw_aerror(upipe);
        upipe_release(upipe);
        return false;
    }
    context->opaque = upipe;
    context->extradata = extradata;
    context->extradata_size = extradata_size;

    switch (codec->type) {
        case AVMEDIA_TYPE_VIDEO: {
            if (upipe_avcdec->lowres > codec->max_lowres) {
                upipe_warn_va(upipe, "Unsupported lowres (%d > %hhu), setting to %hhu",
                    upipe_avcdec->lowres, codec->max_lowres, codec->max_lowres);
                upipe_avcdec->lowres = codec->max_lowres;
            }

            context->get_buffer = upipe_avcdec_get_buffer;
            context->release_buffer = upipe_avcdec_release_buffer;
            context->flags |= CODEC_FLAG_EMU_EDGE;
            context->lowres = upipe_avcdec->lowres;
            context->skip_loop_filter = AVDISCARD_ALL;

            break;
        }
        case AVMEDIA_TYPE_AUDIO: {
            context->get_buffer = upipe_avcdec_get_buffer_audio;
            break;
        }
        default: {
            av_free(context);
            upipe_err_va(upipe, "Unsupported media type (%d)", codec->type);
            upipe_release(upipe);
            return false;
            break;
        }
    }

    /* open new context */
    if (unlikely(avcodec_open2(context, codec, NULL) < 0)) {
        upipe_warn(upipe, "could not open codec");
        av_free(context);
        upipe_release(upipe);
        return false;
    }

    upipe_avcdec->context = context;
    upipe_avcdec->counter = 0;
    upipe_notice_va(upipe, "codec %s (%s) %d opened", codec->name, 
            codec->long_name, codec->id);

    upipe_release(upipe);
    return true;
}
Esempio n. 22
0
static int upipe_m3u_reader_key(struct upipe *upipe,
                                struct uref *flow_def,
                                const char *line)
{
    struct upipe_m3u_reader *upipe_m3u_reader =
        upipe_m3u_reader_from_upipe(upipe);

    if (unlikely(!ubase_check(uref_flow_match_def(flow_def,
                                                  M3U_FLOW_DEF))) &&
        unlikely(!ubase_check(uref_flow_match_def(flow_def,
                                                  PLAYLIST_FLOW_DEF))))
        return UBASE_ERR_INVALID;
    UBASE_RETURN(uref_flow_set_def(flow_def, PLAYLIST_FLOW_DEF));

    struct uref *key = uref_sibling_alloc_control(flow_def);
    if (unlikely(key == NULL)) {
        upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
        return UBASE_ERR_ALLOC;
    }

    if (upipe_m3u_reader->key)
        uref_free(upipe_m3u_reader->key);
    upipe_m3u_reader->key = key;

    const char *iterator = line;
    struct ustring name, value;
    while (ubase_check(attribute_iterate(&iterator, &name, &value)) &&
           iterator != NULL) {
        char value_str[value.len + 1];
        int err = ustring_cpy(value, value_str, sizeof (value_str));
        if (unlikely(!ubase_check(err))) {
            upipe_err_va(upipe, "fail to copy ustring %.*s",
                         (int)value.len, value.at);
            continue;
        }

        if (!ustring_cmp_str(name, "METHOD")) {
            err = uref_m3u_playlist_key_set_method(key, value_str);
            if (unlikely(!ubase_check(err)))
                upipe_err_va(upipe, "fail to set key method to %s", value_str);
        }
        else if (!ustring_cmp_str(name, "URI")) {
            err = uref_m3u_playlist_key_set_uri(key, value_str);
            if (unlikely(!ubase_check(err)))
                upipe_err_va(upipe, "fail to set uri to %s", value_str);
        }
        else if (!ustring_cmp_str(name, "IV")) {
            size_t len = strlen(value_str);
            if (unlikely(len > 32)) {
                upipe_warn_va(upipe, "invalid initialization vector %s",
                              value_str);
                continue;
            }
            for (unsigned i = 0; i < len; i += 2) {
                if (unlikely(!isxdigit(value_str[i])) ||
                    unlikely(!isxdigit(value_str[i + 1]))) {
                    upipe_warn_va(upipe, "invalid initialization vector %s",
                                  value_str);
                    continue;
                }
                //FIXME
                //iv[] = value_str[i]
            }

        }
        else {
            upipe_warn_va(upipe, "ignoring attribute %.*s (%.*s)",
                          (int)name.len, name.at, (int)value.len, value.at);
        }
    }

    return UBASE_ERR_NONE;
}
Esempio n. 23
0
static int upipe_m3u_reader_process_media(struct upipe *upipe,
                                          struct uref *flow_def,
                                          const char *line)
{
    struct upipe_m3u_reader *upipe_m3u_reader =
        upipe_m3u_reader_from_upipe(upipe);

    if (!ubase_check(uref_flow_match_def(flow_def, M3U_FLOW_DEF)) &&
        !ubase_check(uref_flow_match_def(flow_def, MASTER_FLOW_DEF)))
        return UBASE_ERR_INVALID;
    UBASE_RETURN(uref_flow_set_def(flow_def, MASTER_FLOW_DEF));

    struct uref *item = uref_sibling_alloc_control(flow_def);
    if (unlikely(item == NULL)) {
        upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
        return UBASE_ERR_ALLOC;
    }

    struct ustring name, value;
    while (ubase_check(attribute_iterate(&line, &name, &value)) && line) {
        if (!ustring_cmp_str(name, "URI")) {
            value = ustring_unframe(value, '"');
            char val[value.len + 1];
            int err = ustring_cpy(value, val, sizeof (val));
            if (unlikely(!ubase_check(err))) {
                upipe_warn_va(upipe, "fail to copy %.*s",
                              (int)value.len, value.at);
                continue;
            }
            err = uref_m3u_set_uri(item, val);
            if (unlikely(!ubase_check(err))) {
                upipe_warn_va(upipe, "fail to set uri %s", val);
                continue;
            }
        }
        else if (!ustring_cmp_str(name, "TYPE")) {
            char val[value.len + 1];
            int err = ustring_cpy(value, val, sizeof (val));
            if (unlikely(!ubase_check(err))) {
                upipe_warn_va(upipe, "fail to copy %.*s",
                              (int)value.len, value.at);
                continue;
            }
            err = uref_m3u_master_set_media_type(item, val);
            if (unlikely(!ubase_check(err))) {
                upipe_warn_va(upipe, "fail to set media type %s", val);
                continue;
            }
        }
        else if (!ustring_cmp_str(name, "DEFAULT")) {
            if (!ustring_cmp_str(value, "YES")) {
                int err = uref_m3u_master_set_media_default(item);
                if (unlikely(!ubase_check(err)))
                    continue;
            }
            else if (ustring_cmp_str(value, "NO")) {
                upipe_warn_va(upipe, "invalid DEFAULT value %.*s",
                              (int)value.len, value.at);
                continue;
            }
        }
        else if (!ustring_cmp_str(name, "AUTOSELECT")) {
            if (!ustring_cmp_str(value, "YES")) {
                int err = uref_m3u_master_set_media_autoselect(item);
                if (unlikely(!ubase_check(err)))
                    continue;
            }
            else if (ustring_cmp_str(value, "NO")) {
                upipe_warn_va(upipe, "invalid AUTOSELECT value %.*s",
                              (int)value.len, value.at);
                continue;
            }
        }
        else if (!ustring_cmp_str(name, "NAME")) {
            value = ustring_unframe(value, '"');
            char val[value.len + 1];
            int err = ustring_cpy(value, val, sizeof (val));
            if (unlikely(!ubase_check(err))) {
                upipe_warn_va(upipe, "fail to copy %.*s",
                              (int)value.len, value.at);
                continue;
            }
            err = uref_m3u_master_set_media_name(item, val);
            if (unlikely(!ubase_check(err))) {
                upipe_warn_va(upipe, "fail to set media name %s", val);
                continue;
            }
        }
        else if (!ustring_cmp_str(name, "GROUP-ID")) {
            value = ustring_unframe(value, '"');
            char val[value.len + 1];
            int err = ustring_cpy(value, val, sizeof (val));
            if (unlikely(!ubase_check(err))) {
                upipe_warn_va(upipe, "fail to copy %.*s",
                              (int)value.len, value.at);
                continue;
            }
            err = uref_m3u_master_set_media_group(item, val);
            if (unlikely(!ubase_check(err))) {
                upipe_warn_va(upipe, "fail to set group id %s", val);
                continue;
            }
        }
        else {
            upipe_warn_va(upipe, "ignoring attribute %.*s (%.*s)",
                          (int)name.len, name.at,
                          (int)value.len, value.at);
        }
    }
    ulist_add(&upipe_m3u_reader->items, uref_to_uchain(item));
    return UBASE_ERR_NONE;
}
Esempio n. 24
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);
    }
}
Esempio n. 25
0
/** @internal @This parses a host:port string
 *
 * @param upipe description structure of the pipe
 * @param _string string to be parsed
 * @param stringend end of string pointer
 * @param default_port default port
 * @param if_index interface index
 */
static bool upipe_udp_parse_node_service(struct upipe *upipe,
                                          char *_string, char **stringend,
                                          uint16_t default_port,
                                          int *if_index,
                                          struct sockaddr_storage *ss)
{
    int family = AF_INET;
    char port_buffer[6];
    char *string = strdup(_string);
    char *node, *port = NULL, *end;
    struct addrinfo *res = NULL;
    struct addrinfo hint;
    int ret;

    if (string[0] == '[') {
        family = AF_INET6;
        node = string + 1;
        end = strchr(node, ']');
        if (end == NULL) {
            upipe_warn_va(upipe, "invalid IPv6 address %s", _string);
            free(string);
            return false;
        }
        *end++ = '\0';

        char *intf = strrchr(node, '%');
        if (intf != NULL) {
            *intf++ = '\0';
            if (if_index != NULL) {
                if (!upipe_udp_get_ifindex(upipe, intf, if_index)) {
                    free(string);
                    return false;
                };
            }
        }
    } else {
        node = string;
        end = strpbrk(string, "@:,/");
    }

    if (end != NULL && end[0] == ':') {
        *end++ = '\0';
        port = end;
        end = strpbrk(port, "@:,/");
    }

    if (end != NULL) {
        *end = '\0';
        if (stringend != NULL)
            *stringend = _string + (end - string);
    } else if (stringend != NULL) {
        *stringend = _string + strlen(_string);
    }

    if (default_port != 0 && (port == NULL || !*port)) {
        sprintf(port_buffer, "%u", default_port);
        port = port_buffer;
    }

    if (node[0] == '\0') {
        node = "0.0.0.0";
    }

    if (family != AF_INET6) {
        /* Give a try to inet_aton because experience shows that getaddrinfo()
         * fails in certain cases, like when network is down. */
        struct in_addr addr;
        if (inet_aton(node, &addr) != 0) {
            struct sockaddr_in sin;
            memset(&sin, 0, sizeof (struct sockaddr_in));
            sin.sin_family = AF_INET;
            sin.sin_port = port != NULL ? ntohs(atoi(port)) : 0;
            sin.sin_addr = addr;
            memcpy(ss, &sin, sizeof(struct sockaddr_in));
            free(string);
            return true;
        }
    }

    memset(&hint, 0, sizeof(hint));
    hint.ai_family = family;
    hint.ai_socktype = SOCK_DGRAM;
    hint.ai_protocol = 0;
    hint.ai_flags = AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV | AI_ADDRCONFIG;
    if ((ret = getaddrinfo(node, port, &hint, &res)) != 0) {
        //upipe_warn_va(upipe, "getaddrinfo error: %s", gai_strerror(ret));
        free(string);
        return false;
    }

    memcpy(ss, res->ai_addr, res->ai_addrlen);
    freeaddrinfo(res);

    free(string);
    return true;
}
Esempio n. 26
0
/** @internal @This handles data.
 *
 * @param upipe description structure of the pipe
 * @param uref uref structure describing the picture
 * @param upump_p reference to pump that generated the buffer
 * @return true if the packet was handled
 */
static bool upipe_audiobar_handle(struct upipe *upipe, struct uref *uref,
                                  struct upump **upump_p)
{
    struct upipe_audiobar *upipe_audiobar = upipe_audiobar_from_upipe(upipe);
    const char *def;
    if (unlikely(ubase_check(uref_flow_get_def(uref, &def)))) {
        UBASE_FATAL(upipe,
                uref_sound_flow_get_channels(uref, &upipe_audiobar->channels))
        uref_sound_flow_clear_format(uref);
        UBASE_FATAL(upipe,
                uref_attr_import(uref, upipe_audiobar->flow_def_config))
        uref_pic_flow_clear_format(uref);
        UBASE_FATAL(upipe, uref_pic_flow_set_planes(uref, 0))
        UBASE_FATAL(upipe, uref_pic_flow_set_macropixel(uref, 1))
        UBASE_FATAL(upipe, uref_pic_flow_add_plane(uref, 1, 1, 1, "y8"))
        UBASE_FATAL(upipe, uref_pic_flow_add_plane(uref, 2, 1, 1, "u8"))
        UBASE_FATAL(upipe, uref_pic_flow_add_plane(uref, 2, 1, 1, "v8"))
        UBASE_FATAL(upipe, uref_pic_flow_add_plane(uref, 1, 1, 1, "a8"))
        UBASE_FATAL(upipe, uref_pic_set_progressive(uref))

        upipe_audiobar->hsize = upipe_audiobar->vsize =
            upipe_audiobar->sep_width = upipe_audiobar->pad_width = UINT64_MAX;
        upipe_audiobar_require_flow_format(upipe, uref);
        return true;
    }

    if (!upipe_audiobar->ubuf_mgr)
        return false;

    if (unlikely(upipe_audiobar->hsize == UINT64_MAX))
        return false;

    struct ubuf *ubuf = ubuf_pic_alloc(upipe_audiobar->ubuf_mgr,
                                       upipe_audiobar->hsize,
                                       upipe_audiobar->vsize);
    uref_attach_ubuf(uref, ubuf);

    uint8_t *dst[4];
    size_t strides[4];
    uint8_t hsubs[4];
    uint8_t vsubs[4];
    static const char *chroma[4] = { "y8", "u8", "v8", "a8" };
    for (int i = 0; i < 4; i++) {
        if (unlikely(!ubase_check(uref_pic_plane_write(uref, chroma[i],
                            0, 0, -1, -1, &dst[i])) ||
                     !ubase_check(uref_pic_plane_size(uref, chroma[i],
                             &strides[i], &hsubs[i], &vsubs[i], NULL)))) {
             upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
             uref_free(uref);
             return true;
        }
    }

    uint8_t alpha = upipe_audiobar->alpha;
    uint64_t h = upipe_audiobar->vsize;
    const int hred = h - (iec_scale(-8.) * h);
    const int hyellow = h - (iec_scale(-18.) * h);
    uint8_t transparent[4] = { 0x10, 0x80, 0x80, 0 };
    uint8_t black[4] = { 0x10, 0x80, 0x80, alpha };
    uint8_t red[2][4] = { { 76, 85, 0xff, alpha }, { 37, 106, 191, alpha } };
    uint8_t green[2][4] = { { 150, 44, 21, alpha }, { 74, 85, 74, alpha } };
    uint8_t yellow[2][4] = { { 226, 1, 148, alpha }, { 112, 64, 138, alpha } };

    uint64_t pts = 0;
    if (unlikely(!ubase_check(uref_clock_get_pts_prog(uref, &pts)))) {
        upipe_warn(upipe, "unable to read pts");
    }

    for (uint8_t chan = 0; chan < upipe_audiobar->channels; chan++) {
        double amplitude = 0.;
        if (unlikely(!ubase_check(uref_amax_get_amplitude(uref, &amplitude,
                                                          chan))))
            upipe_warn_va(upipe, "unable to get amplitude for channel %"PRIu8", assuming silence", chan);

        double scale = log10(amplitude) * 20;

        // IEC-268-18 return time speed is 20dB per 1.7s (+/- .3)
        if (upipe_audiobar->peak_date[chan])
            upipe_audiobar->peak[chan] -= 20 * (pts - upipe_audiobar->peak_date[chan]) / (1.7 * UCLOCK_FREQ);

        upipe_audiobar->peak_date[chan] = pts;

        if (scale >= upipe_audiobar->peak[chan]) /* higher than lowered peak */
            upipe_audiobar->peak[chan] = scale;
        else /* Current amplitude can not go below the lowered peak value */
            scale = upipe_audiobar->peak[chan];

        scale = iec_scale(scale);

        const int hmax = h - scale * h;
        for (int row = 0; row < h; row++) {
            bool bright = row > hmax;

            const uint8_t *color = row < hred ? red[!bright] :
                                   row < hyellow ? yellow[!bright] :
                                   green[!bright];

            copy_color(dst, strides, hsubs, vsubs, color, row,
                       chan * upipe_audiobar->chan_width,
                       upipe_audiobar->chan_width);
            if (chan && upipe_audiobar->sep_width)
                copy_color(dst, strides, hsubs, vsubs, black, row,
                           chan * upipe_audiobar->chan_width -
                           upipe_audiobar->sep_width / 2,
                           upipe_audiobar->sep_width);

            if (chan == upipe_audiobar->channels - 1 &&
                upipe_audiobar->pad_width)
                copy_color(dst, strides, hsubs, vsubs, transparent, row,
                           (chan + 1) * upipe_audiobar->chan_width,
                           upipe_audiobar->pad_width);
        }
    }

    /* dB marks */
    for (int i = 1; i <= 6; i++) {
        int row = h - (iec_scale(-10 * i) * h);
        copy_color(dst, strides, hsubs, vsubs, black, row, 0,
                   upipe_audiobar->hsize);
    }

    for (int i = 0; i < 4; i++)
        ubuf_pic_plane_unmap(ubuf, chroma[i], 0, 0, -1, -1);
    upipe_audiobar_output(upipe, uref, upump_p);
    return true;
}
Esempio n. 27
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;
}
Esempio n. 28
0
/** @internal @This handles input.
 *
 * @param upipe description structure of the pipe
 * @param uref uref structure
 * @param upump_p reference to upump structure
 */
static void upipe_filter_ebur128_input(struct upipe *upipe, struct uref *uref,
                                       struct upump **upump_p)
{
    struct upipe_filter_ebur128 *upipe_filter_ebur128 =
                                 upipe_filter_ebur128_from_upipe(upipe);
    double loud = 0, lra = 0, global = 0;

    if (unlikely(upipe_filter_ebur128->output_flow == NULL)) {
        upipe_err_va(upipe, "invalid input");
        uref_free(uref);
        return;
    }

    size_t samples;
    uint8_t sample_size;
    if (unlikely(!ubase_check(uref_sound_size(uref, &samples, &sample_size)))) {
        upipe_warn(upipe, "invalid sound buffer");
        uref_free(uref);
        return;
    }

    void *buf = NULL;
    const char *channel = NULL;
    if (upipe_filter_ebur128->planes == 1) {
        if (ubase_check(uref_sound_plane_iterate(uref, &channel)) && channel) {
            if (unlikely(!ubase_check(uref_sound_plane_read_void(uref,
                    channel, 0, -1, (const void **)&buf)))) {
                upipe_warn(upipe, "error mapping sound buffer");
                uref_free(uref);
                return;
            }
        }

    } else {
        buf = malloc(sample_size * upipe_filter_ebur128->channels * samples);
        if (buf == NULL) {
            upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
            uref_free(uref);
            return;
        }
        if (!ubase_check(uref_sound_interleave(uref, (uint8_t *)buf, 0,
                                               samples, sample_size,
                                               upipe_filter_ebur128->planes))) {
            upipe_warn(upipe, "error mapping sound buffer");
            uref_free(uref);
            return;
        }
    }

    if (unlikely((uintptr_t)buf & 1))
        upipe_warn(upipe, "unaligned buffer");

    switch (upipe_filter_ebur128->fmt) {
        case UPIPE_FILTER_EBUR128_SHORT:
            ebur128_add_frames_short(upipe_filter_ebur128->st, (short *)buf,
                                     samples);
            break;

        case UPIPE_FILTER_EBUR128_INT:
            ebur128_add_frames_int(upipe_filter_ebur128->st, (int *)buf,
                                   samples);
            break;

        case UPIPE_FILTER_EBUR128_FLOAT:
            ebur128_add_frames_float(upipe_filter_ebur128->st, (float *)buf,
                                     samples);
            break;

        case UPIPE_FILTER_EBUR128_DOUBLE:
            ebur128_add_frames_double(upipe_filter_ebur128->st, (double *)buf,
                                      samples);
            break;

        default:
            upipe_warn_va(upipe, "unknown sample format %d",
                          upipe_filter_ebur128->fmt);
            break;
    }

    if (upipe_filter_ebur128->planes == 1)
        uref_sound_plane_unmap(uref, channel, 0, -1);
    else
        free(buf);

    ebur128_loudness_momentary(upipe_filter_ebur128->st, &loud);
    ebur128_loudness_range(upipe_filter_ebur128->st, &lra);
    ebur128_loudness_global(upipe_filter_ebur128->st, &global);

    uref_ebur128_set_momentary(uref, loud);
    uref_ebur128_set_lra(uref, lra);
    uref_ebur128_set_global(uref, global);

    upipe_verbose_va(upipe, "loud %f lra %f global %f", loud, lra, global);

    upipe_filter_ebur128_output(upipe, uref, upump_p);
}
Esempio n. 29
0
/** @internal @This is called back to fill NaCl audio buffer.
 * Please note that this function runs in a different thread.
 *
 * @param sample_buffer buffer to fill
 * @param buffer_size size of the buffer in octets
 * @param latency how long before the audio data is to be presented
 * @param user_data opaque pointing to the pipe
 */
static void upipe_nacl_audio_worker(void *sample_buffer, uint32_t buffer_size,
                                    PP_TimeDelta latency, void *user_data)
{
    struct upipe *upipe = (struct upipe *)user_data;
    struct upipe_nacl_audio *upipe_nacl_audio =
        upipe_nacl_audio_from_upipe(upipe);
    uint64_t next_pts = UINT64_MAX;

    if (likely(upipe_nacl_audio->uclock != NULL)) {
        /* This is slightly off. */
        next_pts = uclock_now(upipe_nacl_audio->uclock) +
                   (uint64_t)(latency * UCLOCK_FREQ);
    }

    uint32_t frames = buffer_size / 4;

    while (frames > 0) {
        if (upipe_nacl_audio->uref == NULL)
            upipe_nacl_audio->uref = uqueue_pop(&upipe_nacl_audio->uqueue,
                                                struct uref *);
        if (unlikely(upipe_nacl_audio->uref == NULL)) {
            upipe_dbg_va(upipe, "playing %u frames of silence (empty)", frames);
            memset(sample_buffer, 0, frames * 4);
            break;
        }

        struct uref *uref = upipe_nacl_audio->uref;
        if (next_pts != UINT64_MAX) {
            uint64_t uref_pts;
            if (unlikely(!ubase_check(uref_clock_get_pts_sys(uref,
                                &uref_pts)))) {
                upipe_nacl_audio->uref = NULL;
                uref_free(uref);
                upipe_warn(upipe, "non-dated uref received");
                continue;
            }

            int64_t tolerance = uref_pts - next_pts;
            if (tolerance > (int64_t)PTS_TOLERANCE) {
                uint32_t silence_frames =
                    tolerance * SAMPLE_RATE / UCLOCK_FREQ;
                if (silence_frames > frames)
                    silence_frames = frames;
                upipe_dbg_va(upipe, "playing %u frames of silence (wait)",
                             silence_frames);
                memset(sample_buffer, 0, silence_frames * 4);
                sample_buffer += silence_frames * 4;
                frames -= silence_frames;
                continue;
            } else if (-tolerance > (int64_t)PTS_TOLERANCE) {
                uint32_t dropped_frames =
                    (-tolerance) * SAMPLE_RATE / UCLOCK_FREQ;
                upipe_warn_va(upipe, "late buffer received, dropping %u frames",
                              dropped_frames);
                upipe_nacl_audio_consume(upipe, dropped_frames);
                continue;
            }
        }

        size_t size;
        const void *uref_buffer;
        if (unlikely(!ubase_check(uref_sound_size(uref, &size, NULL)) ||
                     !ubase_check(uref_sound_plane_read_void(uref, "lr", 0, -1,
                                                             &uref_buffer)))) {
            upipe_nacl_audio->uref = NULL;
            uref_free(uref);
            upipe_warn(upipe, "cannot read ubuf buffer");
            continue;
        }

        uint32_t copied_frames = size < frames ? size : frames;
        memcpy(sample_buffer, uref_buffer, copied_frames * 4);
        uref_sound_plane_unmap(uref, "lr", 0, -1);
        sample_buffer += copied_frames * 4;

        upipe_nacl_audio_consume(upipe, copied_frames);
        frames -= copied_frames;
    }
}