/** @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 interpolates the PCRs for packets without a PCR. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump_p reference to pump that generated the buffer */ static void upipe_ts_pcr_interpolator_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_ts_pcr_interpolator *upipe_ts_pcr_interpolator = upipe_ts_pcr_interpolator_from_upipe(upipe); bool discontinuity = ubase_check(uref_flow_get_discontinuity(uref)); if (discontinuity) { upipe_ts_pcr_interpolator->last_pcr = 0; upipe_ts_pcr_interpolator->packets = 0; upipe_ts_pcr_interpolator->pcr_packets = 0; upipe_ts_pcr_interpolator->pcr_delta = 0; upipe_ts_pcr_interpolator->discontinuity = true; upipe_notice_va(upipe, "Clearing state"); } upipe_ts_pcr_interpolator->packets++; uint64_t pcr_prog = 0; uref_clock_get_cr_prog(uref, &pcr_prog); if (pcr_prog) { uint64_t delta = pcr_prog - upipe_ts_pcr_interpolator->last_pcr; upipe_ts_pcr_interpolator->last_pcr = pcr_prog; upipe_verbose_va(upipe, "pcr_prog %"PRId64" offset %"PRId64" stored offset %"PRIu64" bitrate %"PRId64" bps", pcr_prog, delta, upipe_ts_pcr_interpolator->pcr_delta, INT64_C(27000000) * upipe_ts_pcr_interpolator->packets * 188 * 8 / delta); if (upipe_ts_pcr_interpolator->pcr_delta) upipe_ts_pcr_interpolator->pcr_packets = upipe_ts_pcr_interpolator->packets; upipe_ts_pcr_interpolator->pcr_delta = delta; upipe_ts_pcr_interpolator->packets = 0; } else if (upipe_ts_pcr_interpolator->pcr_packets) { uint64_t offset = upipe_ts_pcr_interpolator->pcr_delta * upipe_ts_pcr_interpolator->packets / upipe_ts_pcr_interpolator->pcr_packets; uint64_t prog = upipe_ts_pcr_interpolator->last_pcr + offset; uref_clock_set_date_prog(uref, prog, UREF_DATE_CR); upipe_throw_clock_ts(upipe, uref); } if (!upipe_ts_pcr_interpolator->pcr_packets) { uref_free(uref); return; } if (upipe_ts_pcr_interpolator->discontinuity) { uref_flow_set_discontinuity(uref); upipe_ts_pcr_interpolator->discontinuity = false; } upipe_ts_pcr_interpolator_output(upipe, uref, upump_p); }
/** @internal @This checks an URI. * * @param upipe description structure of the pipe * @param flow_def the current flow definition * @param uri the uri * @return an error code */ static int upipe_m3u_reader_process_uri(struct upipe *upipe, struct uref *flow_def, const char *uri) { struct upipe_m3u_reader *upipe_m3u_reader = upipe_m3u_reader_from_upipe(upipe); upipe_verbose_va(upipe, "uri %s", uri); UBASE_RETURN(uref_flow_match_def(flow_def, M3U_FLOW_DEF)); struct uref *item; UBASE_RETURN(upipe_m3u_reader_get_item(upipe, flow_def, &item)); UBASE_RETURN(uref_m3u_set_uri(item, uri)); if (upipe_m3u_reader->key) UBASE_RETURN(uref_m3u_playlist_key_copy(item, upipe_m3u_reader->key)); upipe_m3u_reader->item = NULL; ulist_add(&upipe_m3u_reader->items, uref_to_uchain(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 */ 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; size_t samples; if (unlikely(!ubase_check(uref_sound_size(uref, &samples, NULL)))) { upipe_warn(upipe, "invalid sound buffer"); uref_free(uref); return; } const char *channel = NULL; const int16_t *buf = NULL; if (ubase_check(uref_sound_plane_iterate(uref, &channel)) && channel) { if (unlikely(!ubase_check(uref_sound_plane_read_int16_t(uref, channel, 0, -1, &buf)))) { upipe_warn(upipe, "error mapping sound buffer"); uref_free(uref); return; } if (unlikely((uintptr_t)buf & 1)) { upipe_warn(upipe, "unaligned buffer"); } ebur128_add_frames_short(upipe_filter_ebur128->st, buf, samples); uref_sound_plane_unmap(uref, channel, 0, -1); } 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); }
/** @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_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); }
/** @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; }