/** @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); }
/** @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; }
/** @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; }
/** @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; }
/** @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); }
/** @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; }
/** @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); }
/** @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); }
/** @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); }
/** 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); }
/** @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; }
/** @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; }
/** @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); }
/** @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); }
/** @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); }
/** @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; }
/** @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); }
/** @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); }
/** @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); }
/** @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; }
/** @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; }
/** @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, ¯opixel_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; }
/** @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, &litude, 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; }
/** @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; }
/** @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); }