/** @internal @This handles urefs (data & flows).
 *
 * @param upipe description structure of the pipe
 * @param uref uref structure
 * @param upump pump that generated the buffer
 */
static void upipe_multicat_probe_input(struct upipe *upipe, struct uref *uref,
                                      struct upump *upump)
{
    struct upipe_multicat_probe *upipe_multicat_probe = upipe_multicat_probe_from_upipe(upipe);
    const char *def;

    if (unlikely(uref_flow_get_def(uref, &def))) {
        upipe_dbg_va(upipe, "flow definition %s", def);
        upipe_multicat_probe_store_flow_def(upipe, uref);
        return;
    }

    if (unlikely(uref_flow_get_end(uref))) {
        uref_free(uref);
        upipe_throw_need_input(upipe);
        return;
    }

    if (unlikely(upipe_multicat_probe->flow_def == NULL)) {
        upipe_throw_flow_def_error(upipe, uref);
        uref_free(uref);
        return;
    }

    return _upipe_multicat_probe_input(upipe, uref, upump);
}
Exemple #2
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_s337_encaps_set_flow_def(struct upipe *upipe,
                                          struct uref *flow_def)
{
    if (flow_def == NULL)
        return UBASE_ERR_INVALID;

    const char *def;
    UBASE_RETURN(uref_flow_get_def(flow_def, &def))

    uint64_t rate;
    UBASE_RETURN(uref_sound_flow_get_rate(flow_def, &rate))

    if (ubase_ncmp(def, EXPECTED_FLOW_DEF))
        return UBASE_ERR_INVALID;

    struct uref *flow_def_dup = uref_dup(flow_def);
    if (flow_def_dup == NULL)
        return UBASE_ERR_ALLOC;

    uref_flow_set_def(flow_def_dup, "sound.s32.s337.a52.");
    uref_sound_flow_set_channels(flow_def_dup, 2);
    uref_sound_flow_set_sample_size(flow_def_dup, 2*4);

    if (!ubase_check(uref_sound_flow_add_plane(flow_def_dup, "lr")) ||
        !ubase_check(uref_sound_flow_set_rate(flow_def_dup, rate))) {
        uref_free(flow_def_dup);
        return UBASE_ERR_ALLOC;
    }

    upipe_s337_encaps_require_ubuf_mgr(upipe, flow_def_dup);
    return UBASE_ERR_NONE;
}
Exemple #3
0
/** @internal @This returns the sub pipe manager.
 *
 * @param upipe description structure of the pipe
 * @param upipe_mgr_p filled with the sub pipe manager
 * @return an error code
 */
static int upipe_hls_get_sub_mgr(struct upipe *upipe,
                                 struct upipe_mgr **upipe_mgr_p)
{
    struct upipe_hls *upipe_hls = upipe_hls_from_upipe(upipe);

    if (!upipe_mgr_p)
        return UBASE_ERR_INVALID;

    if (!upipe_hls->flow_def)
        return UBASE_ERR_INVALID;

    const char *def = NULL;
    uref_flow_get_def(upipe_hls->flow_def, &def);
    if (!def)
        return UBASE_ERR_INVALID;

    if (!strcmp(def, "block.m3u.master."))
        return upipe_get_sub_mgr(upipe_hls->last_inner, upipe_mgr_p);

    if (!upipe_hls->sub_pipe_mgr)
        upipe_hls->sub_pipe_mgr = upipe_hls_variant_mgr_alloc();

    *upipe_mgr_p = upipe_hls->sub_pipe_mgr;
    return UBASE_ERR_NONE;
}
Exemple #4
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_s337d_set_flow_def(struct upipe *upipe, struct uref *flow_def)
{
    if (flow_def == NULL)
        return UBASE_ERR_INVALID;
    const char *def;
    uint64_t rate;
    if (unlikely(!ubase_check(uref_flow_get_def(flow_def, &def)) ||
                 (ubase_ncmp(def, "block.s337.") &&
                  strcmp(def, "block.")) ||
                 !ubase_check(uref_sound_flow_get_rate(flow_def, &rate))))
        return UBASE_ERR_INVALID;

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

    struct upipe_s337d *upipe_s337d = upipe_s337d_from_upipe(upipe);
    upipe_s337d->sample_rate = rate;
    flow_def = upipe_s337d_store_flow_def_input(upipe, flow_def_dup);
    if (flow_def != NULL)
        upipe_s337d_store_flow_def(upipe, flow_def);
    return UBASE_ERR_NONE;
}
Exemple #5
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);
}
Exemple #6
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_a52f_set_flow_def(struct upipe *upipe, struct uref *flow_def)
{
    if (flow_def == NULL)
        return UBASE_ERR_INVALID;
    const char *def;
    if (unlikely(!ubase_check(uref_flow_get_def(flow_def, &def)) ||
                 (ubase_ncmp(def, "block.ac3.") &&
                  ubase_ncmp(def, "block.eac3.") &&
                  strcmp(def, "block."))))
        return UBASE_ERR_INVALID;
    struct uref *flow_def_dup;
    if (unlikely((flow_def_dup = uref_dup(flow_def)) == NULL)) {
        upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
        return UBASE_ERR_ALLOC;
    }

    struct upipe_a52f *upipe_a52f = upipe_a52f_from_upipe(upipe);
    upipe_a52f->input_latency = 0;
    uref_clock_get_latency(flow_def, &upipe_a52f->input_latency);

    if (unlikely(upipe_a52f->samplerate &&
                 !ubase_check(uref_clock_set_latency(flow_def_dup,
                                    upipe_a52f->input_latency +
                                    UCLOCK_FREQ * A52_FRAME_SAMPLES /
                                    upipe_a52f->samplerate))))
        upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
    flow_def = upipe_a52f_store_flow_def_input(upipe, flow_def_dup);
    if (flow_def != NULL)
        upipe_a52f_store_flow_def(upipe, flow_def);
    return UBASE_ERR_NONE;
}
Exemple #7
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);
}
Exemple #8
0
/** @internal @This checks a "#EXT-X-ENDLIST" 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_endlist(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));

    upipe_verbose(upipe, "endlist tag");
    return uref_m3u_playlist_flow_set_endlist(flow_def);
}
Exemple #9
0
/** @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_filter_ebur128_provide_flow_format(struct upipe *upipe,
                                                    struct urequest *request)
{
    const char *def;
    UBASE_RETURN(uref_flow_get_def(request->uref, &def))
    struct uref *flow = uref_dup(request->uref);
    UBASE_ALLOC_RETURN(flow);

    if (!ubase_ncmp(def, "sound.u8."))
        UBASE_FATAL(upipe, uref_flow_set_def(flow, "sound.s16."));

    return urequest_provide_flow_format(request, flow);
}
Exemple #10
0
/** helper phony pipe to count pictures */
static void count_input(struct upipe *upipe, struct uref *uref,
                        struct upump *upump)
{
    const char *def;
    if (uref_flow_get_def(uref, &def) && uref_flow_get_end(uref)) {
        uref_free(uref);
        return;
    }

    uint64_t uref_duration;
    if (uref_clock_get_duration(uref, &uref_duration))
        duration += uref_duration;
    uref_free(uref);
}
Exemple #11
0
/** @internal @This sets the input flow definition.
 *
 * @param upipe description structure of the pipe
 * @param flow flow definition packet
 * @return an error code
 */
static int upipe_filter_ebur128_set_flow_def(struct upipe *upipe,
                                             struct uref *flow)
{
    struct upipe_filter_ebur128 *upipe_filter_ebur128 =
                                 upipe_filter_ebur128_from_upipe(upipe);
    if (flow == NULL)
        return UBASE_ERR_INVALID;

    enum upipe_filter_ebur128_fmt fmt;
    const char *def;
    UBASE_RETURN(uref_flow_get_def(flow, &def))
    if (!ubase_ncmp(def, "sound.s16."))
        fmt = UPIPE_FILTER_EBUR128_SHORT;
    else if (!ubase_ncmp(def, "sound.s32."))
        fmt = UPIPE_FILTER_EBUR128_INT;
    else if (!ubase_ncmp(def, "sound.f32."))
        fmt = UPIPE_FILTER_EBUR128_FLOAT;
    else if (!ubase_ncmp(def, "sound.f64."))
        fmt = UPIPE_FILTER_EBUR128_DOUBLE;
    else
        return UBASE_ERR_INVALID;

    uint64_t rate;
    if (unlikely(!ubase_check(uref_sound_flow_get_rate(flow, &rate))
            || !ubase_check(uref_sound_flow_get_channels(flow,
                    &upipe_filter_ebur128->channels))
            || !ubase_check(uref_sound_flow_get_planes(flow,
                    &upipe_filter_ebur128->planes))))
        return UBASE_ERR_INVALID;

    struct uref *flow_dup;
    if (unlikely((flow_dup = uref_dup(flow)) == NULL)) {
        upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
        return UBASE_ERR_ALLOC;
    }
    upipe_filter_ebur128->fmt = fmt;

    if (unlikely(upipe_filter_ebur128->st)) {
        //ebur128_destroy(&upipe_filter_ebur128->st);
        ebur128_change_parameters(upipe_filter_ebur128->st,
                                  upipe_filter_ebur128->channels, rate);
    } else {
        upipe_filter_ebur128->st =
            ebur128_init(upipe_filter_ebur128->channels, rate,
            EBUR128_MODE_LRA | EBUR128_MODE_I | EBUR128_MODE_HISTOGRAM);
    }

    upipe_filter_ebur128_store_flow_def(upipe, flow_dup);
    return UBASE_ERR_NONE;
}
Exemple #12
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_tblk_set_flow_def(struct upipe *upipe, struct uref *flow_def)
{
    if (flow_def == NULL)
        return UBASE_ERR_INVALID;
    const char *def;
    if (unlikely(!ubase_check(uref_flow_get_def(flow_def, &def)) ||
                 (ubase_ncmp(def, "block.") && ubase_ncmp(def, "pic.") &&
                  ubase_ncmp(def, "sound."))))
        return UBASE_ERR_INVALID;

    struct uref *flow_def_dup;
    if ((flow_def_dup = uref_dup(flow_def)) == NULL)
        return UBASE_ERR_ALLOC;
    upipe_input(upipe, flow_def_dup, NULL);
    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_ts_pcr_interpolator_set_flow_def(struct upipe *upipe,
                                        struct uref *flow_def)
{
    if (flow_def == NULL)
        return UBASE_ERR_INVALID;
    const char *def;
    UBASE_RETURN(uref_flow_get_def(flow_def, &def))
    if (ubase_ncmp(def, EXPECTED_FLOW_DEF))
        return UBASE_ERR_INVALID;
    struct uref *flow_def_dup = uref_dup(flow_def);
    if (unlikely(flow_def_dup == NULL)) {
        upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
        return UBASE_ERR_ALLOC;
    }
    upipe_ts_pcr_interpolator_store_flow_def(upipe, flow_def_dup);
    return UBASE_ERR_NONE;
}
Exemple #14
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);
}
Exemple #15
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);
}
Exemple #16
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_telxf_set_flow_def(struct upipe *upipe, struct uref *flow_def)
{
    if (flow_def == NULL)
        return UBASE_ERR_INVALID;
    const char *def;
    if (unlikely(!ubase_check(uref_flow_get_def(flow_def, &def)) ||
                 (ubase_ncmp(def, "block.dvb_teletext.") &&
                  strcmp(def, "block."))))
        return UBASE_ERR_INVALID;
    struct uref *flow_def_dup;
    if (unlikely((flow_def_dup = uref_dup(flow_def)) == NULL)) {
        upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
        return UBASE_ERR_ALLOC;
    }
    flow_def = upipe_telxf_store_flow_def_input(upipe, flow_def_dup);
    if (flow_def != NULL)
        upipe_telxf_store_flow_def(upipe, flow_def);
    return UBASE_ERR_NONE;
}
/** @internal @This merges the new access unit into a possibly existing
 * incomplete PES, and outputs the PES if possible.
 *
 * @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_ts_pese_handle(struct upipe *upipe, struct uref *uref,
                                 struct upump **upump_p)
{
    struct upipe_ts_pese *upipe_ts_pese = upipe_ts_pese_from_upipe(upipe);
    const char *def;
    if (unlikely(ubase_check(uref_flow_get_def(uref, &def)))) {
        upipe_ts_pese_work(upipe, NULL);

        uref_ts_flow_get_pes_id(uref, &upipe_ts_pese->pes_id);
        upipe_ts_pese->pes_header_size = 0;
        uref_ts_flow_get_pes_header(uref, &upipe_ts_pese->pes_header_size);
        upipe_ts_pese->pes_min_duration = 0;
        uref_ts_flow_get_pes_min_duration(uref,
                                          &upipe_ts_pese->pes_min_duration);
        upipe_ts_pese->input_latency = 0;
        uref_clock_get_latency(uref, &upipe_ts_pese->input_latency);
        if (unlikely(!ubase_check(uref_clock_set_latency(uref,
                                        upipe_ts_pese->input_latency +
                                        upipe_ts_pese->pes_min_duration))))
            upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
        upipe_ts_pese_store_flow_def(upipe, NULL);
        upipe_ts_pese_require_ubuf_mgr(upipe, uref);
        return true;
    }

    if (upipe_ts_pese->flow_def == NULL)
        return false;

    uint64_t uref_duration = upipe_ts_pese->pes_min_duration;
    uref_clock_get_duration(uref, &uref_duration);
    size_t uref_size = 0;
    uref_block_size(uref, &uref_size);
    uref_block_delete_start(uref);

    ulist_add(&upipe_ts_pese->next_pes, uref_to_uchain(uref));
    upipe_ts_pese->next_pes_duration += uref_duration;
    upipe_ts_pese->next_pes_size += uref_size;

    if (upipe_ts_pese->next_pes_duration >= upipe_ts_pese->pes_min_duration)
        upipe_ts_pese_work(upipe, upump_p);
    return true;
}
/** @internal @This handles input uref.
 *
 * @param upipe description structure of the pipe
 * @param uref uref structure
 * @param upump upump structure
 */
static void upipe_avcdec_input(struct upipe *upipe, struct uref *uref,
                              struct upump *upump)
{
    struct upipe_avcdec *upipe_avcdec = upipe_avcdec_from_upipe(upipe);
    if (upipe_avcdec->uref_mgr == NULL) {
        upipe_throw_need_uref_mgr(upipe);
        if (unlikely(upipe_avcdec->uref_mgr == NULL)) {
            uref_free(uref);
            return;
        }
    }

    const char *def = NULL;
    if (unlikely(uref_flow_get_def(uref, &def))) {
        if (unlikely(ubase_ncmp(def, EXPECTED_FLOW))) {
            upipe_throw_flow_def_error(upipe, uref);
            uref_free(uref);
            _upipe_avcdec_set_codec(upipe, NULL, NULL, 0);
            return;
        }

        upipe_dbg_va(upipe, "flow definition %s", def);
        def += strlen(EXPECTED_FLOW);
        _upipe_avcdec_set_codec(upipe, def, NULL, 0);
        uref_free(uref);
        return;
    }

    if (unlikely(uref_flow_get_end(uref))) {
        uref_free(uref);
        upipe_throw_need_input(upipe);
        return;
    }

    if (unlikely(!uref->ubuf)) {
        upipe_warn(upipe, "uref has no ubuf, dropping");
        uref_free(uref);
        return;
    }

    upipe_avcdec_input_packet(upipe, uref, upump);
}
Exemple #19
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);
}
/** @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_ts_pesd_set_flow_def(struct upipe *upipe,
                                      struct uref *flow_def)
{
    if (flow_def == NULL)
        return UBASE_ERR_INVALID;
    const char *def;
    UBASE_RETURN(uref_flow_get_def(flow_def, &def))
    if (ubase_ncmp(def, EXPECTED_FLOW_DEF))
        return UBASE_ERR_INVALID;
    struct uref *flow_def_dup;
    if (unlikely((flow_def_dup = uref_dup(flow_def)) == NULL)) {
        upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
        return UBASE_ERR_ALLOC;
    }
    if (unlikely(!ubase_check(uref_flow_set_def_va(flow_def_dup, "block.%s",
                                       def + strlen(EXPECTED_FLOW_DEF)))))
        upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
    upipe_ts_pesd_store_flow_def(upipe, flow_def_dup);
    return UBASE_ERR_NONE;
}
Exemple #21
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);
}
Exemple #22
0
/** @internal @This checks and parses a "#EXTINF" 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_extinf(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));

    const char *endptr;
    uint64_t duration;
    UBASE_RETURN(duration_to_uclock(line, &endptr, &duration));
    if (line == endptr || *endptr != ',') {
        upipe_err_va(upipe, "invalid segment duration `%s'", line);
        return UBASE_ERR_INVALID;
    }
    upipe_verbose_va(upipe, "segment duration: %"PRIu64, duration);
    return uref_m3u_playlist_set_seq_duration(item, duration);
}
Exemple #23
0
/** @internal @This handles input.
 *
 * @param upipe description structure of the pipe
 * @param uref uref structure
 * @param upump pump that generated the buffer
 */
static void upipe_skip_input(struct upipe *upipe, struct uref *uref,
                               struct upump *upump)
{
    struct upipe_skip *upipe_skip = upipe_skip_from_upipe(upipe);
    const char *def;

    if (unlikely(uref_flow_get_def(uref, &def))) {
        if (unlikely(ubase_ncmp(def, EXPECTED_FLOW))) {
            upipe_throw_flow_def_error(upipe, uref);
            uref_free(uref);
            return;
        }

        upipe_dbg_va(upipe, "flow definition %s", def);
        if (unlikely(!uref_flow_set_def(uref, "block.")))
            upipe_throw_aerror(upipe);
        upipe_skip_store_flow_def(upipe, uref);
        return;
    }

    if (unlikely(uref_flow_get_end(uref))) {
        uref_free(uref);
        upipe_throw_need_input(upipe);
        return;
    }

    if (unlikely(upipe_skip->flow_def == NULL)) {
        upipe_throw_flow_def_error(upipe, uref);
        uref_free(uref);
        return;
    }

    if (unlikely(uref->ubuf == NULL)) {
        uref_free(uref);
        return;
    }

    upipe_skip_input_block(upipe, uref, upump);
}
Exemple #24
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_tblk_handle(struct upipe *upipe, struct uref *uref,
                              struct upump **upump_p)
{
    struct upipe_tblk *upipe_tblk = upipe_tblk_from_upipe(upipe);
    const char *def;
    if (unlikely(ubase_check(uref_flow_get_def(uref, &def)))) {
        if (!ubase_ncmp(def, "pic.")) {
            upipe_tblk->input_alloc = UBUF_ALLOC_PICTURE;
            uref_pic_flow_clear_format(uref);
            uref_flow_set_def(uref, "block.");
        } else if (!ubase_ncmp(def, "sound.")) {
            upipe_tblk->input_alloc = UBUF_ALLOC_SOUND;
            uref_sound_flow_clear_format(uref);
            uref_flow_set_def(uref, "block.");
        } else
            upipe_tblk->input_alloc = UBUF_ALLOC_BLOCK;

        upipe_tblk_store_flow_def(upipe, NULL);
        upipe_tblk_require_ubuf_mgr(upipe, uref);
        return true;
    }

    if (upipe_tblk->flow_def == NULL)
        return false;
    assert(upipe_tblk->ubuf_mgr != NULL);

    switch (upipe_tblk->input_alloc) {
        case UBUF_ALLOC_PICTURE:
            upipe_tblk_handle_pic(upipe, uref, upump_p);
            break;
        case UBUF_ALLOC_SOUND:
            upipe_tblk_handle_sound(upipe, uref, upump_p);
            break;
        default:
            upipe_tblk_output(upipe, uref, upump_p);
            break;
    }
    return true;
}
Exemple #25
0
/** @internal @This iterates the variant.
 *
 * @param upipe description structure of the pipe
 * @param uref_p filled with the next variant or NULL
 * @return an error code
 */
static int upipe_hls_split_iterate(struct upipe *upipe, struct uref **uref_p)
{
    struct upipe_hls *upipe_hls = upipe_hls_from_upipe(upipe);

    if (!uref_p)
        return UBASE_ERR_INVALID;

    if (!upipe_hls->flow_def)
        return UBASE_ERR_INVALID;

    const char *def = NULL;
    uref_flow_get_def(upipe_hls->flow_def, &def);
    if (!def)
        return UBASE_ERR_INVALID;

    if (!strcmp(def, "block.m3u.master."))
        return upipe_split_iterate(upipe_hls->last_inner, uref_p);

    if (*uref_p) {
        *uref_p = NULL;
    }
    else {
        if (!upipe_hls->item) {
            struct uref *item = uref_sibling_alloc_control(upipe_hls->flow_def);
            if (unlikely(!item))
                return UBASE_ERR_ALLOC;
            uref_flow_set_id(item, 1);
            char *uri = NULL;
            uref_uri_get_to_str(upipe_hls->flow_def, &uri);
            uref_m3u_set_uri(item, uri);
            free(uri);
            upipe_hls->item = item;
        }
        *uref_p = upipe_hls->item;
    }

    return UBASE_ERR_NONE;
}
Exemple #26
0
/** @internal @This handles input.
 *
 * @param upipe description structure of the pipe
 * @param uref uref structure
 * @param upump_p reference to upump structure
 * @return always true
 */
static bool upipe_filter_blend_handle(struct upipe *upipe, struct uref *uref,
                                      struct upump **upump_p)
{
    struct upipe_filter_blend *upipe_filter_blend = upipe_filter_blend_from_upipe(upipe);
    const char *def;
    if (unlikely(ubase_check(uref_flow_get_def(uref, &def)))) {
        upipe_filter_blend_store_flow_def(upipe, NULL);
        upipe_filter_blend_require_ubuf_mgr(upipe, uref);
        return true;
    }

    if (upipe_filter_blend->flow_def == NULL)
        return false;

    const uint8_t *in;
    uint8_t *out;
    uint8_t hsub, vsub, macropixel_size;
    size_t stride_in = 0, stride_out = 0, width, height;
    const char *chroma = NULL;
    struct ubuf *ubuf_deint = NULL;

    // Now process frames
    uref_pic_size(uref, &width, &height, NULL);
    upipe_verbose_va(upipe, "received pic (%zux%zu)", width, height);

    assert(upipe_filter_blend->ubuf_mgr);
    ubuf_deint = ubuf_pic_alloc(upipe_filter_blend->ubuf_mgr, width, height);
    if (unlikely(!ubuf_deint)) {
        upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
        goto error;
    }

    // Iterate planes
    while (ubase_check(uref_pic_plane_iterate(uref, &chroma)) && chroma) {
        // map all
        if (unlikely(!ubase_check(uref_pic_plane_size(uref, chroma, &stride_in,
                                                &hsub, &vsub, &macropixel_size)))) {
            upipe_err_va(upipe, "Could not read origin chroma %s", chroma);
            goto error;
        }
        if (unlikely(!ubase_check(ubuf_pic_plane_size(ubuf_deint, chroma, &stride_out,
                                                  NULL, NULL, NULL)))) {
            upipe_err_va(upipe, "Could not read dest chroma %s", chroma);
            goto error;
        }
        uref_pic_plane_read(uref, chroma, 0, 0, -1, -1, &in);
        ubuf_pic_plane_write(ubuf_deint, chroma, 0, 0, -1, -1, &out);

        // process plane
        upipe_filter_blend_plane(in, out, stride_in, stride_out, (size_t) height/vsub, macropixel_size);

        // unmap all
        uref_pic_plane_unmap(uref, chroma, 0, 0, -1, -1);
        ubuf_pic_plane_unmap(ubuf_deint, chroma, 0, 0, -1, -1);
    }

    // Attach new ubuf and output frame
    uref_attach_ubuf(uref, ubuf_deint);
    uref_pic_set_progressive(uref);
    uref_pic_delete_tff(uref);

    upipe_filter_blend_output(upipe, uref, upump_p);
    return true;

error:
    uref_free(uref);
    if (ubuf_deint) {
        ubuf_free(ubuf_deint);
    }
    return true;
}
Exemple #27
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;
}
Exemple #28
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;
}
Exemple #29
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);
    }
}
static int catch_uref(struct uprobe *uprobe,
                      struct upipe *upipe,
                      int event, va_list args)
{
    switch (event) {
    case UPROBE_NEW_FLOW_DEF: {
        struct uref *uref = va_arg(args, struct uref *);

        const char *flow_def;
        ubase_assert(uref_flow_get_def(uref, &flow_def));
        printf("flow definition: %s\n", flow_def);

        uint8_t version;
        if (ubase_check(uref_m3u_flow_get_version(uref, &version)))
            printf("version: %u\n", version);

        const char *playlist_type;
        if (ubase_check(uref_m3u_playlist_flow_get_type(uref, &playlist_type)))
            printf("playlist type: %s\n", playlist_type);

        uint64_t target_duration;
        if (ubase_check(uref_m3u_playlist_flow_get_target_duration(
                    uref, &target_duration)))
            printf("playlist target duration: %"PRIu64"\n", target_duration);

        uint64_t media_sequence;
        if (ubase_check(uref_m3u_playlist_flow_get_media_sequence(
                    uref, &media_sequence)))
            printf("playlist target duration: %"PRIu64"\n", media_sequence);

        if (ubase_check(uref_m3u_playlist_flow_get_endlist(uref)))
            printf("playlist end\n");

        return UBASE_ERR_NONE;
    }

    case UPROBE_PROBE_UREF: {
        UBASE_SIGNATURE_CHECK(args, UPIPE_PROBE_UREF_SIGNATURE)
        struct uref *uref = va_arg(args, struct uref *);

        const char *uri;
        if (ubase_check(uref_m3u_get_uri(uref, &uri)))
            printf("uri: %s\n", uri);

        uint64_t playlist_seq_duration;
        if (ubase_check(uref_m3u_playlist_get_seq_duration(
                    uref, &playlist_seq_duration)))
            printf("playlist sequence duration: %"PRIu64"\n",
                   playlist_seq_duration);

        uint64_t playlist_byte_range_len;
        if (ubase_check(uref_m3u_playlist_get_byte_range_len(
                    uref, &playlist_byte_range_len)))
            printf("playlist byte range length: %"PRIu64"\n",
                   playlist_byte_range_len);

        uint64_t playlist_byte_range_off;
        if (ubase_check(uref_m3u_playlist_get_byte_range_off(
                    uref, &playlist_byte_range_off)))
            printf("playlist byte range offset: %"PRIu64"\n",
                   playlist_byte_range_off);

        uint64_t master_bandwidth;
        if (ubase_check(uref_m3u_master_get_bandwidth(
                    uref, &master_bandwidth)))
            printf("master bandwidth: %"PRIu64"\n", master_bandwidth);

        const char *master_codecs;
        if (ubase_check(uref_m3u_master_get_codecs(
                    uref, &master_codecs)))
            printf("master codecs: %s\n", master_codecs);

        const char *resolution;
        if (ubase_check(uref_m3u_master_get_resolution(
                    uref, &resolution)))
            printf("master resolution: %s\n", resolution);

        const char *audio;
        if (ubase_check(uref_m3u_master_get_audio(
                    uref, &audio)))
            printf("master audio: %s\n", audio);

        const char *media_type;
        if (ubase_check(uref_m3u_master_get_media_type(
                    uref, &media_type)))
            printf("master media_type: %s\n", media_type);

        const char *media_name;
        if (ubase_check(uref_m3u_master_get_media_name(
                    uref, &media_name)))
            printf("master media_name: %s\n", media_name);

        const char *media_group;
        if (ubase_check(uref_m3u_master_get_media_group(
                    uref, &media_group)))
            printf("master media_group: %s\n", media_group);

        if (ubase_check(uref_m3u_master_get_media_default(uref)))
            printf("master media_default\n");

        if (ubase_check(uref_m3u_master_get_media_autoselect(uref)))
            printf("master media_autoselect\n");

        return UBASE_ERR_NONE;
    }
    }
    return uprobe_throw_next(uprobe, upipe, event, args);
}