/** @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 checks and parses a "#EXT-X-STREAM-INF" tag. * * @param upipe description structure of the pipe * @param flow_def the current flow definition * @param line the trailing characters of the line * @return an error code */ static int upipe_m3u_reader_ext_x_stream_inf(struct upipe *upipe, struct uref *flow_def, const char *line) { if (!ubase_check(uref_flow_match_def(flow_def, M3U_FLOW_DEF)) && !ubase_check(uref_flow_match_def(flow_def, MASTER_FLOW_DEF))) return UBASE_ERR_INVALID; UBASE_RETURN(uref_flow_set_def(flow_def, MASTER_FLOW_DEF)); struct uref *item; UBASE_RETURN(upipe_m3u_reader_get_item(upipe, flow_def, &item)); const char *iterator = line; struct ustring name, value; while (ubase_check(attribute_iterate(&iterator, &name, &value)) && iterator != NULL) { char value_str[value.len + 1]; int err = ustring_cpy(value, value_str, sizeof (value_str)); if (unlikely(!ubase_check(err))) { upipe_err_va(upipe, "fail to copy ustring %.*s", (int)value.len, value.at); continue; } if (!ustring_cmp_str(name, "BANDWIDTH")) { char *endptr; uint64_t bandwidth = strtoull(value_str, &endptr, 10); if (endptr == value_str) return UBASE_ERR_INVALID; err = uref_m3u_master_set_bandwidth(item, bandwidth); if (unlikely(!ubase_check(err))) upipe_err_va(upipe, "fail to set bandwidth to %s", value_str); } else if (!ustring_cmp_str(name, "CODECS")) { err = uref_m3u_master_set_codecs(item, value_str); if (unlikely(!ubase_check(err))) upipe_err_va(upipe, "fail to set codecs to %s", value_str); } else if (!ustring_cmp_str(name, "AUDIO")) { err = uref_m3u_master_set_audio(item, value_str); if (unlikely(!ubase_check(err))) upipe_err_va(upipe, "fail to set audio to %s", value_str); } else if (!ustring_cmp_str(name, "RESOLUTION")) { err = uref_m3u_master_set_resolution(item, value_str); if (unlikely(!ubase_check(err))) upipe_err_va(upipe, "fail to set resolution to %s", value_str); } else { upipe_warn_va(upipe, "ignoring attribute %.*s (%.*s)", (int)name.len, name.at, (int)value.len, value.at); } } return UBASE_ERR_NONE; }
static bool upipe_dveo_asi_sink_write(struct upipe *upipe, struct uref *uref, bool *reset_first_timestamp) { struct upipe_dveo_asi_sink *upipe_dveo_asi_sink = upipe_dveo_asi_sink_from_upipe(upipe); for (;;) { int iovec_count = uref_block_iovec_count(uref, 0, -1); if (unlikely(iovec_count == -1)) { upipe_warn(upipe, "cannot read ubuf buffer"); break; } if (unlikely(iovec_count == 0)) { break; } struct iovec iovecs[iovec_count]; if (unlikely(!ubase_check(uref_block_iovec_read(uref, 0, -1, iovecs)))) { upipe_warn(upipe, "cannot read ubuf buffer"); break; } ssize_t ret = writev(upipe_dveo_asi_sink->fd, iovecs, iovec_count); uref_block_iovec_unmap(uref, 0, -1, iovecs); if (unlikely(ret == -1)) { switch (errno) { case EINTR: continue; case EAGAIN: #if EAGAIN != EWOULDBLOCK case EWOULDBLOCK: #endif //upipe_notice_va(upipe, "polling"); upipe_dveo_asi_sink_poll(upipe); return false; default: break; } upipe_warn_va(upipe, "write error to device %d (%m)", upipe_dveo_asi_sink->card_idx); upipe_dveo_asi_sink_set_upump(upipe, NULL); upipe_throw_sink_end(upipe); break; } size_t uref_size; if (ubase_check(uref_block_size(uref, &uref_size)) && uref_size == ret) { /* wrote succeeded */ *reset_first_timestamp = false; break; } uref_block_resize(uref, ret, -1); } return true; }
/** @internal @This changes the rotate interval * * @param upipe description structure of the pipe * @param rotate new rotate interval * @return false in case of error */ static bool _upipe_multicat_probe_set_rotate(struct upipe *upipe, uint64_t rotate) { struct upipe_multicat_probe *upipe_multicat_probe = upipe_multicat_probe_from_upipe(upipe); if (unlikely(rotate < 1)) { upipe_warn_va(upipe, "invalid rotate interval (%"PRIu64" < 1)", rotate); return false; } upipe_multicat_probe->rotate = rotate; upipe_notice_va(upipe, "setting rotate: %"PRIu64, rotate); return true; }
/** @internal @This handles input. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump_p reference to pump that generated the buffer */ static void upipe_htons_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct ubuf *ubuf; size_t size = 0; int remain, bufsize = -1, offset = 0; uint8_t *buf = NULL; /* block size */ if (unlikely(!ubase_check(uref_block_size(uref, &size)))) { upipe_warn(upipe, "could not read uref block size"); uref_free(uref); return; } /* copy ubuf if shared or not 16b-unaligned or segmented */ bufsize = -1; if (!ubase_check(uref_block_write(uref, 0, &bufsize, &buf)) || ((uintptr_t)buf & 1) || bufsize != size) { ubuf = ubuf_block_copy(uref->ubuf->mgr, uref->ubuf, 0, size); if (unlikely(!ubuf)) { upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); uref_free(uref); return; } uref_attach_ubuf(uref, ubuf); } else { uref_block_unmap(uref, 0); } /* process ubuf chunks */ while (size > 0) { bufsize = -1; if (unlikely(!ubase_check(uref_block_write(uref, offset, &bufsize, &buf)))) { upipe_warn(upipe, "unexpected buffer error"); uref_free(uref); return; } if (unlikely((uintptr_t)buf & 1)) { upipe_warn_va(upipe, "unaligned buffer: %p", buf); } for (remain = bufsize; remain > 1; remain -= 2) { *(uint16_t *)buf = htons(*(uint16_t *)buf); buf += 2; } uref_block_unmap(uref, offset); offset += bufsize; size -= bufsize; } upipe_htons_output(upipe, uref, upump_p); }
/** @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 and parses a "#EXT-X-VERSION" tag. * * @param upipe description structure of the pipe * @param flow_def the current flow definition * @param line the trailing characters of the line * @return an error code */ static int upipe_m3u_reader_process_version(struct upipe *upipe, struct uref *flow_def, const char *line) { UBASE_RETURN(uref_flow_match_def(flow_def, M3U_FLOW_DEF)); char *endptr; unsigned long int version = strtoul(line, &endptr, 10); if (line == endptr || *endptr != '\0' || version > UINT8_MAX) { upipe_warn_va(upipe, "invalid version %s", line); return UBASE_ERR_INVALID; } upipe_dbg_va(upipe, "version: %u", version); return uref_m3u_flow_set_version(flow_def, version); }
/** @internal @This receives data. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump_p reference to pump that generated the buffer */ static void upipe_agg_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_agg *upipe_agg = upipe_agg_from_upipe(upipe); size_t size = 0; const size_t output_size = upipe_agg->output_size; uref_block_size(uref, &size); /* check for invalid or too large size */ if (unlikely(size == 0 || size > output_size)) { upipe_warn_va(upipe, "received packet of invalid size: %zu (output_size == %zu)", size, output_size); uref_free(uref); return; } /* flush if incoming packet makes aggregated overflow */ if (upipe_agg->size + size > output_size) { upipe_agg_output(upipe, upipe_agg->aggregated, upump_p); upipe_agg->aggregated = NULL; } /* keep or attach incoming packet */ if (unlikely(!upipe_agg->aggregated)) { upipe_agg->aggregated = uref; upipe_agg->size = size; } else { struct ubuf *append = uref_detach_ubuf(uref); uref_free(uref); if (unlikely(!ubase_check(uref_block_append(upipe_agg->aggregated, append)))) { upipe_warn(upipe, "error appending packet"); ubuf_free(append); return; }; upipe_agg->size += size; } /* anticipate next packet size and flush now if necessary */ if (upipe_agg->input_size) size = upipe_agg->input_size; if (unlikely(upipe_agg->size + size > output_size)) { upipe_agg_output(upipe, upipe_agg->aggregated, upump_p); upipe_agg->aggregated = NULL; upipe_agg->size = 0; } }
/** @internal @This sets the input flow definition. * * @param upipe description structure of the pipe * @param flow_def flow definition packet * @return an error code */ static int upipe_fdec_set_flow_def(struct upipe *upipe, struct uref *flow_def) { struct upipe_fdec_mgr *fdec_mgr = upipe_fdec_mgr_from_upipe_mgr(upipe->mgr); struct upipe_fdec *upipe_fdec = upipe_fdec_from_upipe(upipe); if (flow_def == NULL) return UBASE_ERR_INVALID; if (upipe_fdec->last_inner != NULL) { if (ubase_check(upipe_set_flow_def(upipe_fdec->last_inner, flow_def))) return UBASE_ERR_NONE; } upipe_fdec_store_bin_input(upipe, NULL); upipe_fdec_store_bin_output(upipe, NULL); struct upipe *avcdec = upipe_void_alloc(fdec_mgr->avcdec_mgr, uprobe_pfx_alloc( uprobe_use(&upipe_fdec->last_inner_probe), UPROBE_LOG_VERBOSE, "avcdec")); if (unlikely(avcdec == NULL)) { upipe_err_va(upipe, "couldn't allocate avcdec"); return UBASE_ERR_UNHANDLED; } if (unlikely(!ubase_check(upipe_set_flow_def(avcdec, flow_def)))) { upipe_err_va(upipe, "couldn't set avcdec flow def"); upipe_release(avcdec); return UBASE_ERR_UNHANDLED; } if (upipe_fdec->options != NULL && upipe_fdec->options->udict != NULL) { const char *key = NULL; enum udict_type type = UDICT_TYPE_END; while (ubase_check(udict_iterate(upipe_fdec->options->udict, &key, &type)) && type != UDICT_TYPE_END) { const char *value; if (key == NULL || !ubase_check(udict_get_string(upipe_fdec->options->udict, &value, type, key))) continue; if (!ubase_check(upipe_set_option(avcdec, key, value))) upipe_warn_va(upipe, "option %s=%s invalid", key, value); } } upipe_fdec_store_bin_input(upipe, upipe_use(avcdec)); upipe_fdec_store_bin_output(upipe, avcdec); return UBASE_ERR_NONE; }
/** @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 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); }
/** @This sets the low resolution parameter, if supported by codec. * If some codec is already used, it is re-opened. * * @param upipe description structure of the pipe * @param lowres lowres parameter (0=disabled) * @return false in case of error */ static bool _upipe_avcdec_set_lowres(struct upipe *upipe, int lowres) { struct upipe_avcdec *upipe_avcdec = upipe_avcdec_from_upipe(upipe); const char *codec_def; bool ret = true; if (lowres < 0) { upipe_warn_va(upipe, "Invalid lowres parameter (%d)", lowres); return false; } upipe_avcdec->lowres = lowres; upipe_dbg_va(upipe, "Requesting lowres %d", lowres); if (upipe_avcdec->context && upipe_avcdec->context->codec) { codec_def = upipe_av_to_flow_def(upipe_avcdec->context->codec->id); ret = _upipe_avcdec_set_codec(upipe, codec_def, upipe_avcdec->context->extradata, upipe_avcdec->context->extradata_size); } return ret; }
/** @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 the presence of the sync word. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump_p reference to pump that generated the buffer */ static bool upipe_ts_check_check(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { const uint8_t *buffer; int size = 1; uint8_t word; if (unlikely(!ubase_check(uref_block_read(uref, 0, &size, &buffer)))) { uref_free(uref); upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return false; } assert(size == 1); word = *buffer; uref_block_unmap(uref, 0); if (word != TS_SYNC) { uref_free(uref); upipe_warn_va(upipe, "invalid TS sync 0x%"PRIx8, word); return false; } upipe_ts_check_output(upipe, uref, upump_p); return true; }
/** @internal @This provides a ubuf_mgr request. * * @param upipe description structure of the pipe * @param flow_format amended flow format * @return an error code */ static int upipe_audiobar_check_ubuf_mgr(struct upipe *upipe, struct uref *flow_format) { struct upipe_audiobar *upipe_audiobar = upipe_audiobar_from_upipe(upipe); if (flow_format == NULL) return UBASE_ERR_NONE; upipe_audiobar_store_flow_def(upipe, flow_format); UBASE_RETURN(uref_pic_flow_get_hsize(flow_format, &upipe_audiobar->hsize)) UBASE_RETURN(uref_pic_flow_get_vsize(flow_format, &upipe_audiobar->vsize)) upipe_audiobar->chan_width = upipe_audiobar->hsize / upipe_audiobar->channels; upipe_audiobar->chan_width -= upipe_audiobar->chan_width % 2; if (unlikely(upipe_audiobar->chan_width < 16)) { upipe_warn_va(upipe, "channel width is too small to have a separation (%"PRIu64")", upipe_audiobar->chan_width); } upipe_audiobar->sep_width = upipe_audiobar->chan_width / 4; upipe_audiobar->sep_width -= upipe_audiobar->sep_width % 4; upipe_audiobar->pad_width = upipe_audiobar->hsize - upipe_audiobar->chan_width * upipe_audiobar->channels; upipe_notice_va(upipe, "setting up chan %"PRIu64" sep %"PRIu64" pad %"PRIu64, upipe_audiobar->chan_width, upipe_audiobar->sep_width, upipe_audiobar->pad_width); bool was_buffered = !upipe_audiobar_check_input(upipe); upipe_audiobar_output_input(upipe); upipe_audiobar_unblock_input(upipe); if (was_buffered && upipe_audiobar_check_input(upipe)) { /* All packets have been output, release again the pipe that has been * used in @ref upipe_audiobar_input. */ upipe_release(upipe); } return UBASE_ERR_NONE; }
/** @internal @This parses and removes the PES header of a packet. * * @param upipe description structure of the pipe * @param upump_p reference to pump that generated the buffer */ static void upipe_ts_pesd_decaps(struct upipe *upipe, struct upump **upump_p) { struct upipe_ts_pesd *upipe_ts_pesd = upipe_ts_pesd_from_upipe(upipe); uint8_t buffer[PES_HEADER_SIZE]; const uint8_t *pes_header = uref_block_peek(upipe_ts_pesd->next_uref, 0, PES_HEADER_SIZE, buffer); if (unlikely(pes_header == NULL)) return; bool validate = pes_validate(pes_header); uint8_t streamid = pes_get_streamid(pes_header); uint16_t length = pes_get_length(pes_header); UBASE_FATAL(upipe, uref_block_peek_unmap(upipe_ts_pesd->next_uref, 0, buffer, pes_header)) if (unlikely(!validate)) { upipe_warn(upipe, "wrong PES header"); upipe_ts_pesd_flush(upipe); return; } if (unlikely(streamid == PES_STREAM_ID_PADDING)) { upipe_ts_pesd_flush(upipe); return; } if (length) upipe_ts_pesd->next_pes_size = length + PES_HEADER_SIZE; else upipe_ts_pesd->next_pes_size = 0; if (streamid == PES_STREAM_ID_PSM || streamid == PES_STREAM_ID_PRIVATE_2 || streamid == PES_STREAM_ID_ECM || streamid == PES_STREAM_ID_EMM || streamid == PES_STREAM_ID_PSD || streamid == PES_STREAM_ID_DSMCC || streamid == PES_STREAM_ID_H222_1_E) { UBASE_FATAL(upipe, uref_block_resize(upipe_ts_pesd->next_uref, PES_HEADER_SIZE, -1)) upipe_ts_pesd_check_output(upipe, upump_p); return; } if (unlikely(length != 0 && length < PES_HEADER_OPTIONAL_SIZE)) { upipe_warn(upipe, "wrong PES length"); upipe_ts_pesd_flush(upipe); return; } uint8_t buffer2[PES_HEADER_SIZE_NOPTS - PES_HEADER_SIZE]; pes_header = uref_block_peek(upipe_ts_pesd->next_uref, PES_HEADER_SIZE, PES_HEADER_OPTIONAL_SIZE, buffer2); if (unlikely(pes_header == NULL)) return; validate = pes_validate_header(pes_header - PES_HEADER_SIZE); bool alignment = pes_get_dataalignment(pes_header - PES_HEADER_SIZE); bool has_pts = pes_has_pts(pes_header - PES_HEADER_SIZE); bool has_dts = pes_has_dts(pes_header - PES_HEADER_SIZE); uint8_t headerlength = pes_get_headerlength(pes_header - PES_HEADER_SIZE); UBASE_FATAL(upipe, uref_block_peek_unmap(upipe_ts_pesd->next_uref, PES_HEADER_SIZE, buffer2, pes_header)) if (unlikely(!validate)) { upipe_warn(upipe, "wrong PES optional header"); upipe_ts_pesd_flush(upipe); return; } if (unlikely((length != 0 && headerlength + PES_HEADER_OPTIONAL_SIZE > length) || (has_pts && headerlength < PES_HEADER_SIZE_PTS - PES_HEADER_SIZE_NOPTS) || (has_dts && headerlength < PES_HEADER_SIZE_PTSDTS - PES_HEADER_SIZE_NOPTS))) { upipe_warn(upipe, "wrong PES header length"); upipe_ts_pesd_flush(upipe); return; } if (upipe_ts_pesd->next_uref_size < PES_HEADER_SIZE_NOPTS + headerlength) return; if (has_pts) { uint8_t buffer3[PES_HEADER_TS_SIZE * 2]; uint64_t pts, dts; const uint8_t *ts_fields = uref_block_peek(upipe_ts_pesd->next_uref, PES_HEADER_SIZE_NOPTS, PES_HEADER_TS_SIZE * (has_dts ? 2 : 1), buffer3); if (unlikely(ts_fields == NULL)) { upipe_ts_pesd_flush(upipe); upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return; } validate = pes_validate_pts(ts_fields - PES_HEADER_SIZE_NOPTS); pts = pes_get_pts(ts_fields - PES_HEADER_SIZE_NOPTS); if (has_dts) { validate = validate && pes_validate_dts(ts_fields - PES_HEADER_SIZE_NOPTS); dts = pes_get_dts(ts_fields - PES_HEADER_SIZE_NOPTS); } else dts = pts; UBASE_FATAL(upipe, uref_block_peek_unmap(upipe_ts_pesd->next_uref, PES_HEADER_SIZE_NOPTS, buffer3, ts_fields)) if (unlikely(!validate)) { upipe_warn(upipe, "wrong PES timestamp syntax"); #if 0 /* disable this because it is a common syntax error */ upipe_ts_pesd_flush(upipe); return; #endif } uint64_t dts_pts_delay = (POW2_33 + pts - dts) % POW2_33; if (dts_pts_delay > POW2_33 / 2) { upipe_warn_va(upipe, "invalid PTS field (%"PRIu64" < %"PRIu64")", pts, dts); dts_pts_delay = 0; } dts_pts_delay *= UCLOCK_FREQ / 90000; dts *= UCLOCK_FREQ / 90000; uref_clock_set_dts_orig(upipe_ts_pesd->next_uref, dts); uref_clock_set_dts_pts_delay(upipe_ts_pesd->next_uref, dts_pts_delay); upipe_throw_clock_ts(upipe, upipe_ts_pesd->next_uref); } if (alignment) uref_flow_set_random(upipe_ts_pesd->next_uref); UBASE_FATAL(upipe, uref_block_resize(upipe_ts_pesd->next_uref, PES_HEADER_SIZE_NOPTS + headerlength, -1)) upipe_ts_pesd_check_output(upipe, upump_p); }
/** @internal @This handles data. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump_p reference to pump that generated the buffer */ static inline void upipe_rtpd_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_rtpd *upipe_rtpd = upipe_rtpd_from_upipe(upipe); uint8_t rtp_buffer[RTP_HEADER_SIZE]; const uint8_t *rtp_header = uref_block_peek(uref, 0, RTP_HEADER_SIZE, rtp_buffer); if (unlikely(rtp_header == NULL)) { upipe_warn(upipe, "invalid buffer received"); uref_free(uref); return; } bool valid = rtp_check_hdr(rtp_header); bool extension = rtp_check_extension(rtp_header); uint8_t cc = rtp_get_cc(rtp_header); uint8_t type = rtp_get_type(rtp_header); uint16_t seqnum = rtp_get_seqnum(rtp_header); ptrdiff_t extension_offset = rtp_extension((uint8_t *)rtp_header) - rtp_header; uref_block_peek_unmap(uref, 0, rtp_buffer, rtp_header); if (unlikely(!valid)) { upipe_warn(upipe, "invalid RTP header"); uref_free(uref); return; } size_t offset = RTP_HEADER_SIZE + 4 * cc; if (extension) { rtp_header = uref_block_peek(uref, extension_offset, RTP_EXTENSION_SIZE, rtp_buffer); if (unlikely(rtp_header == NULL)) { upipe_warn(upipe, "invalid buffer received"); uref_free(uref); return; } offset += 4 * (1 + rtpx_get_length(rtp_header)); uref_block_peek_unmap(uref, extension_offset, rtp_buffer, rtp_header); } if (unlikely(upipe_rtpd->expected_seqnum != -1 && seqnum != upipe_rtpd->expected_seqnum)) { upipe_warn_va(upipe, "potentially lost %d RTP packets", (seqnum + UINT16_MAX + 1 - upipe_rtpd->expected_seqnum) & UINT16_MAX); uref_flow_set_discontinuity(uref); } upipe_rtpd->expected_seqnum = seqnum + 1; upipe_rtpd->expected_seqnum &= UINT16_MAX; if (unlikely(type != upipe_rtpd->type)) { assert(upipe_rtpd->flow_def_input != NULL); struct uref *flow_def = uref_dup(upipe_rtpd->flow_def_input); if (unlikely(flow_def == NULL)) { uref_free(uref); upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return; } switch (type) { case RTP_TYPE_TS: uref_flow_set_def(flow_def, "block.mpegtsaligned."); break; default: break; } upipe_rtpd->type = type; upipe_rtpd_store_flow_def(upipe, flow_def); } uref_block_resize(uref, offset, -1); upipe_rtpd_output(upipe, uref, upump_p); }
/** @internal @This outputs data to the file sink. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump_p reference to pump that generated the buffer * @return true if the uref was processed */ static bool upipe_dveo_asi_sink_output(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_dveo_asi_sink *upipe_dveo_asi_sink = upipe_dveo_asi_sink_from_upipe(upipe); const char *def; if (unlikely(ubase_check(uref_flow_get_def(uref, &def)))) { uref_free(uref); return true; } int fd = upipe_dveo_asi_sink->fd; if (unlikely(fd == -1)) { upipe_warn(upipe, "received a buffer before opening the device"); uref_free(uref); return true; } uint64_t cr_sys = 0; if (unlikely(!ubase_check(uref_clock_get_cr_sys(uref, &cr_sys))) || cr_sys == -1) { upipe_warn(upipe, "received non-dated buffer"); uref_free(uref); return true; } if (ubase_check(uref_flow_get_discontinuity(uref))) { upipe_warn_va(upipe, "DISCONTINUITY, resetting timestamp"); upipe_dveo_asi_sink->first_timestamp = true; } if (unlikely(upipe_dveo_asi_sink->first_timestamp)) { int val; if (ioctl(fd, ASI_IOC_TXGETTXD, &val) < 0) { upipe_err_va(upipe, "ioctl TXGETTXDfailed (%m)"); upipe_throw_fatal(upipe, UBASE_ERR_UNKNOWN); uref_free(uref); return true; } else if (val) { upipe_warn(upipe, "Waiting for transmission to stop"); uref_free(uref); return true; } } /* Make sure we set the counter */ bool reset_first_timestamp = upipe_dveo_asi_sink->first_timestamp; if (upipe_dveo_asi_sink_add_header(upipe, uref, cr_sys)) { uref_free(uref); return true; /* invalid uref, discarded */ } if (!upipe_dveo_asi_sink_write(upipe, uref, &reset_first_timestamp)) return false; /* would block */ uref_free(uref); if (reset_first_timestamp) upipe_dveo_asi_sink->first_timestamp = true; upipe_dveo_asi_sink_stats(upipe); return true; }
/** @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); } }
/** @internal @This configures a new codec context * * @param upipe description structure of the pipe * @param codec avcodec description structure * @param extradata pointer to extradata buffer * @param extradata_size extradata size * @return false if the buffer couldn't be accepted */ static bool upipe_avcdec_open_codec(struct upipe *upipe, AVCodec *codec, uint8_t *extradata, int extradata_size) { AVCodecContext *context = NULL; struct upipe_avcdec *upipe_avcdec = upipe_avcdec_from_upipe(upipe); assert(upipe); /* close previously opened context */ if (unlikely(upipe_avcdec->context)) { /* first send empty packet to flush retained frames */ upipe_dbg(upipe, "flushing frames in decoder"); while (upipe_avcdec_process_buf(upipe, NULL, 0, NULL)); /* now close codec and free extradata if any */ upipe_notice_va(upipe, "avcodec context (%s) closed (%d)", upipe_avcdec->context->codec->name, upipe_avcdec->counter); avcodec_close(upipe_avcdec->context); if (upipe_avcdec->context->extradata_size > 0) { free(upipe_avcdec->context->extradata); } av_free(upipe_avcdec->context); upipe_avcdec->context = NULL; upipe_avcdec_store_flow_def(upipe, NULL); } /* just closing, that's all */ if (!codec) { upipe_release(upipe); return false; } /* allocate and configure codec context */ context = avcodec_alloc_context3(codec); if (unlikely(!context)) { upipe_throw_aerror(upipe); upipe_release(upipe); return false; } context->opaque = upipe; context->extradata = extradata; context->extradata_size = extradata_size; switch (codec->type) { case AVMEDIA_TYPE_VIDEO: { if (upipe_avcdec->lowres > codec->max_lowres) { upipe_warn_va(upipe, "Unsupported lowres (%d > %hhu), setting to %hhu", upipe_avcdec->lowres, codec->max_lowres, codec->max_lowres); upipe_avcdec->lowres = codec->max_lowres; } context->get_buffer = upipe_avcdec_get_buffer; context->release_buffer = upipe_avcdec_release_buffer; context->flags |= CODEC_FLAG_EMU_EDGE; context->lowres = upipe_avcdec->lowres; context->skip_loop_filter = AVDISCARD_ALL; break; } case AVMEDIA_TYPE_AUDIO: { context->get_buffer = upipe_avcdec_get_buffer_audio; break; } default: { av_free(context); upipe_err_va(upipe, "Unsupported media type (%d)", codec->type); upipe_release(upipe); return false; break; } } /* open new context */ if (unlikely(avcodec_open2(context, codec, NULL) < 0)) { upipe_warn(upipe, "could not open codec"); av_free(context); upipe_release(upipe); return false; } upipe_avcdec->context = context; upipe_avcdec->counter = 0; upipe_notice_va(upipe, "codec %s (%s) %d opened", codec->name, codec->long_name, codec->id); upipe_release(upipe); return true; }
static int upipe_m3u_reader_key(struct upipe *upipe, struct uref *flow_def, const char *line) { struct upipe_m3u_reader *upipe_m3u_reader = upipe_m3u_reader_from_upipe(upipe); if (unlikely(!ubase_check(uref_flow_match_def(flow_def, M3U_FLOW_DEF))) && unlikely(!ubase_check(uref_flow_match_def(flow_def, PLAYLIST_FLOW_DEF)))) return UBASE_ERR_INVALID; UBASE_RETURN(uref_flow_set_def(flow_def, PLAYLIST_FLOW_DEF)); struct uref *key = uref_sibling_alloc_control(flow_def); if (unlikely(key == NULL)) { upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return UBASE_ERR_ALLOC; } if (upipe_m3u_reader->key) uref_free(upipe_m3u_reader->key); upipe_m3u_reader->key = key; const char *iterator = line; struct ustring name, value; while (ubase_check(attribute_iterate(&iterator, &name, &value)) && iterator != NULL) { char value_str[value.len + 1]; int err = ustring_cpy(value, value_str, sizeof (value_str)); if (unlikely(!ubase_check(err))) { upipe_err_va(upipe, "fail to copy ustring %.*s", (int)value.len, value.at); continue; } if (!ustring_cmp_str(name, "METHOD")) { err = uref_m3u_playlist_key_set_method(key, value_str); if (unlikely(!ubase_check(err))) upipe_err_va(upipe, "fail to set key method to %s", value_str); } else if (!ustring_cmp_str(name, "URI")) { err = uref_m3u_playlist_key_set_uri(key, value_str); if (unlikely(!ubase_check(err))) upipe_err_va(upipe, "fail to set uri to %s", value_str); } else if (!ustring_cmp_str(name, "IV")) { size_t len = strlen(value_str); if (unlikely(len > 32)) { upipe_warn_va(upipe, "invalid initialization vector %s", value_str); continue; } for (unsigned i = 0; i < len; i += 2) { if (unlikely(!isxdigit(value_str[i])) || unlikely(!isxdigit(value_str[i + 1]))) { upipe_warn_va(upipe, "invalid initialization vector %s", value_str); continue; } //FIXME //iv[] = value_str[i] } } else { upipe_warn_va(upipe, "ignoring attribute %.*s (%.*s)", (int)name.len, name.at, (int)value.len, value.at); } } return UBASE_ERR_NONE; }
static int upipe_m3u_reader_process_media(struct upipe *upipe, struct uref *flow_def, const char *line) { struct upipe_m3u_reader *upipe_m3u_reader = upipe_m3u_reader_from_upipe(upipe); if (!ubase_check(uref_flow_match_def(flow_def, M3U_FLOW_DEF)) && !ubase_check(uref_flow_match_def(flow_def, MASTER_FLOW_DEF))) return UBASE_ERR_INVALID; UBASE_RETURN(uref_flow_set_def(flow_def, MASTER_FLOW_DEF)); struct uref *item = uref_sibling_alloc_control(flow_def); if (unlikely(item == NULL)) { upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return UBASE_ERR_ALLOC; } struct ustring name, value; while (ubase_check(attribute_iterate(&line, &name, &value)) && line) { if (!ustring_cmp_str(name, "URI")) { value = ustring_unframe(value, '"'); char val[value.len + 1]; int err = ustring_cpy(value, val, sizeof (val)); if (unlikely(!ubase_check(err))) { upipe_warn_va(upipe, "fail to copy %.*s", (int)value.len, value.at); continue; } err = uref_m3u_set_uri(item, val); if (unlikely(!ubase_check(err))) { upipe_warn_va(upipe, "fail to set uri %s", val); continue; } } else if (!ustring_cmp_str(name, "TYPE")) { char val[value.len + 1]; int err = ustring_cpy(value, val, sizeof (val)); if (unlikely(!ubase_check(err))) { upipe_warn_va(upipe, "fail to copy %.*s", (int)value.len, value.at); continue; } err = uref_m3u_master_set_media_type(item, val); if (unlikely(!ubase_check(err))) { upipe_warn_va(upipe, "fail to set media type %s", val); continue; } } else if (!ustring_cmp_str(name, "DEFAULT")) { if (!ustring_cmp_str(value, "YES")) { int err = uref_m3u_master_set_media_default(item); if (unlikely(!ubase_check(err))) continue; } else if (ustring_cmp_str(value, "NO")) { upipe_warn_va(upipe, "invalid DEFAULT value %.*s", (int)value.len, value.at); continue; } } else if (!ustring_cmp_str(name, "AUTOSELECT")) { if (!ustring_cmp_str(value, "YES")) { int err = uref_m3u_master_set_media_autoselect(item); if (unlikely(!ubase_check(err))) continue; } else if (ustring_cmp_str(value, "NO")) { upipe_warn_va(upipe, "invalid AUTOSELECT value %.*s", (int)value.len, value.at); continue; } } else if (!ustring_cmp_str(name, "NAME")) { value = ustring_unframe(value, '"'); char val[value.len + 1]; int err = ustring_cpy(value, val, sizeof (val)); if (unlikely(!ubase_check(err))) { upipe_warn_va(upipe, "fail to copy %.*s", (int)value.len, value.at); continue; } err = uref_m3u_master_set_media_name(item, val); if (unlikely(!ubase_check(err))) { upipe_warn_va(upipe, "fail to set media name %s", val); continue; } } else if (!ustring_cmp_str(name, "GROUP-ID")) { value = ustring_unframe(value, '"'); char val[value.len + 1]; int err = ustring_cpy(value, val, sizeof (val)); if (unlikely(!ubase_check(err))) { upipe_warn_va(upipe, "fail to copy %.*s", (int)value.len, value.at); continue; } err = uref_m3u_master_set_media_group(item, val); if (unlikely(!ubase_check(err))) { upipe_warn_va(upipe, "fail to set group id %s", val); continue; } } else { upipe_warn_va(upipe, "ignoring attribute %.*s (%.*s)", (int)name.len, name.at, (int)value.len, value.at); } } ulist_add(&upipe_m3u_reader->items, uref_to_uchain(item)); return UBASE_ERR_NONE; }
/** @internal @This configures a new codec context * * @param upipe description structure of the pipe * @param codec_def codec defintion string * @param extradata pointer to extradata buffer * @param extradata_size extradata size * @return false if the buffer couldn't be accepted */ static bool _upipe_avcdec_set_codec(struct upipe *upipe, const char *codec_def, uint8_t *extradata, int extradata_size) { AVCodec *codec = NULL; int codec_id = 0; struct upipe_avcdec *upipe_avcdec = upipe_avcdec_from_upipe(upipe); struct upipe_avcodec_open_params *params = &upipe_avcdec->open_params; uint8_t *extradata_padded = NULL; /* find codec */ if (codec_def) { codec_id = upipe_av_from_flow_def(codec_def); if (unlikely(!codec_id)) { upipe_warn_va(upipe, "codec %s not found", codec_def); } codec = avcodec_find_decoder(codec_id); if (unlikely(!codec)) { upipe_warn_va(upipe, "codec %s (%d) not found", codec_def, codec_id); } } /* copy extradata if present */ if (extradata && extradata_size > 0) { extradata_padded = upipe_avcdec_copy_extradata(upipe, extradata, extradata_size); if (unlikely(!extradata_padded)) { extradata_size = 0; } } /* use udeal/upump callback if available */ if (upipe_avcdec->upump_mgr) { upipe_dbg(upipe, "upump_mgr present, using udeal"); if (unlikely(upipe_avcdec->upump_av_deal)) { upipe_dbg(upipe, "previous upump_av_deal still running, cleaning first"); upipe_avcdec_abort_av_deal(upipe); } else { upipe_use(upipe); } struct upump *upump_av_deal = upipe_av_deal_upump_alloc(upipe_avcdec->upump_mgr, upipe_avcdec_open_codec_cb, upipe); if (unlikely(!upump_av_deal)) { upipe_err(upipe, "can't create dealer"); upipe_throw_upump_error(upipe); return false; } upipe_avcdec->upump_av_deal = upump_av_deal; memset(params, 0, sizeof(struct upipe_avcodec_open_params)); params->codec = codec; params->extradata = extradata_padded; params->extradata_size = extradata_size; /* fire */ upipe_av_deal_start(upump_av_deal); return true; } else { upipe_dbg(upipe, "no upump_mgr present, direct call to avcdec_open"); upipe_use(upipe); return upipe_avcdec_open_codec(upipe, codec, extradata_padded, extradata_size); } }
/** @internal @This parses a host:port string * * @param upipe description structure of the pipe * @param _string string to be parsed * @param stringend end of string pointer * @param default_port default port * @param if_index interface index */ static bool upipe_udp_parse_node_service(struct upipe *upipe, char *_string, char **stringend, uint16_t default_port, int *if_index, struct sockaddr_storage *ss) { int family = AF_INET; char port_buffer[6]; char *string = strdup(_string); char *node, *port = NULL, *end; struct addrinfo *res = NULL; struct addrinfo hint; int ret; if (string[0] == '[') { family = AF_INET6; node = string + 1; end = strchr(node, ']'); if (end == NULL) { upipe_warn_va(upipe, "invalid IPv6 address %s", _string); free(string); return false; } *end++ = '\0'; char *intf = strrchr(node, '%'); if (intf != NULL) { *intf++ = '\0'; if (if_index != NULL) { if (!upipe_udp_get_ifindex(upipe, intf, if_index)) { free(string); return false; }; } } } else { node = string; end = strpbrk(string, "@:,/"); } if (end != NULL && end[0] == ':') { *end++ = '\0'; port = end; end = strpbrk(port, "@:,/"); } if (end != NULL) { *end = '\0'; if (stringend != NULL) *stringend = _string + (end - string); } else if (stringend != NULL) { *stringend = _string + strlen(_string); } if (default_port != 0 && (port == NULL || !*port)) { sprintf(port_buffer, "%u", default_port); port = port_buffer; } if (node[0] == '\0') { node = "0.0.0.0"; } if (family != AF_INET6) { /* Give a try to inet_aton because experience shows that getaddrinfo() * fails in certain cases, like when network is down. */ struct in_addr addr; if (inet_aton(node, &addr) != 0) { struct sockaddr_in sin; memset(&sin, 0, sizeof (struct sockaddr_in)); sin.sin_family = AF_INET; sin.sin_port = port != NULL ? ntohs(atoi(port)) : 0; sin.sin_addr = addr; memcpy(ss, &sin, sizeof(struct sockaddr_in)); free(string); return true; } } memset(&hint, 0, sizeof(hint)); hint.ai_family = family; hint.ai_socktype = SOCK_DGRAM; hint.ai_protocol = 0; hint.ai_flags = AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV | AI_ADDRCONFIG; if ((ret = getaddrinfo(node, port, &hint, &res)) != 0) { //upipe_warn_va(upipe, "getaddrinfo error: %s", gai_strerror(ret)); free(string); return false; } memcpy(ss, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); free(string); return true; }
/** @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 parses _uri and opens IPv4 & IPv6 sockets * * @param upipe description structure of the pipe * @param _uri socket URI * @param ttl packets time-to-live * @param bind_port bind port * @param connect_port connect port * @param weight weight (UNUSED) * @param use_tcp Set this to open a tcp socket (instead of udp) * @param use_raw open RAW socket (udp) * @param raw_header user-provided buffer for RAW header (ip+udp) * @return socket fd, or -1 in case of error */ int upipe_udp_open_socket(struct upipe *upipe, const char *_uri, int ttl, uint16_t bind_port, uint16_t connect_port, unsigned int *weight, bool *use_tcp, bool *use_raw, uint8_t *raw_header) { union sockaddru bind_addr, connect_addr; int fd, i; char *uri = strdup(_uri); char *token = uri; char *token2 = NULL; int bind_if_index = 0, connect_if_index = 0; in_addr_t if_addr = INADDR_ANY; in_addr_t src_addr = INADDR_ANY; uint16_t src_port = 4242; int tos = 0; bool b_tcp; bool b_raw; int family; socklen_t sockaddr_len; #if !defined(__APPLE__) && !defined(__native_client__) char *ifname = NULL; #endif if (!uri) return -1; memset(&bind_addr, 0, sizeof(union sockaddru)); memset(&connect_addr, 0, sizeof(union sockaddru)); bind_addr.ss.ss_family = AF_UNSPEC; connect_addr.ss.ss_family = AF_UNSPEC; if (use_tcp == NULL) { use_tcp = &b_tcp; } *use_tcp = false; if (use_raw == NULL) { use_raw = &b_raw; } *use_raw = false; token2 = strrchr(uri, ','); if (token2) { *token2++ = '\0'; if (weight) { *weight = strtoul(token2, NULL, 0); } } else if (weight) { *weight = 1; } token2 = strchr(uri, '/'); if (token2) { *token2 = '\0'; } if (*token == '\0') { free(uri); return -1; } /* Hosts */ if (token[0] != '@') { if (!upipe_udp_parse_node_service(upipe, token, &token, connect_port, &connect_if_index, &connect_addr.ss)) { free(uri); return -1; } /* required on some architectures */ memset(&connect_addr.sin.sin_zero, 0, sizeof(connect_addr.sin.sin_zero)); } if (token[0] == '@') { token++; if (!upipe_udp_parse_node_service(upipe, token, &token, bind_port, &bind_if_index, &bind_addr.ss)) { free(uri); return -1; } /* required on some architectures */ memset(&bind_addr.sin.sin_zero, 0, sizeof(bind_addr.sin.sin_zero)); } if (bind_addr.ss.ss_family == AF_UNSPEC && connect_addr.ss.ss_family == AF_UNSPEC) { free(uri); return -1; } upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); /* Weights and options */ if (token2) { do { *token2++ = '\0'; #define IS_OPTION(option) (!strncasecmp(token2, option, strlen(option))) #define ARG_OPTION(option) (token2 + strlen(option)) if (IS_OPTION("ifindex=")) { bind_if_index = connect_if_index = strtol(ARG_OPTION("ifindex="), NULL, 0); } else if (IS_OPTION("ifaddr=")) { char *option = config_stropt(ARG_OPTION("ifaddr=")); if_addr = inet_addr(option); free( option ); #if !defined(__APPLE__) && !defined(__native_client__) } else if ( IS_OPTION("ifname=") ) { ifname = config_stropt( ARG_OPTION("ifname=") ); if (strlen(ifname) >= IFNAMSIZ) { ifname[IFNAMSIZ-1] = '\0'; } #endif } else if (IS_OPTION("srcaddr=")) { char *option = config_stropt(ARG_OPTION("srcaddr=")); src_addr = inet_addr(option); free(option); *use_raw = true; } else if (IS_OPTION("srcport=")) { src_port = strtol(ARG_OPTION("srcport="), NULL, 0); } else if (IS_OPTION("ttl=")) { ttl = strtol(ARG_OPTION("ttl="), NULL, 0); } else if (IS_OPTION("tos=")) { tos = strtol(ARG_OPTION("tos="), NULL, 0); } else if (IS_OPTION("tcp")) { *use_tcp = true; } else { upipe_warn_va(upipe, "unrecognized option %s", token2); } #undef IS_OPTION #undef ARG_OPTION } while ((token2 = strchr(token2, '/')) != NULL); } if (unlikely(*use_tcp && *use_raw)) { upipe_warn(upipe, "RAW sockets not implemented for tcp"); free(uri); return -1; } free(uri); /* Sanity checks */ if (bind_addr.ss.ss_family != AF_UNSPEC && connect_addr.ss.ss_family != AF_UNSPEC && bind_addr.ss.ss_family != connect_addr.ss.ss_family) { upipe_err(upipe, "incompatible address types"); return -1; } if (bind_addr.ss.ss_family != AF_UNSPEC) { family = bind_addr.ss.ss_family; } else if (connect_addr.ss.ss_family != AF_UNSPEC) { family = connect_addr.ss.ss_family; } else { upipe_err(upipe, "ambiguous address declaration"); return -1; } sockaddr_len = (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); if (bind_if_index && connect_if_index && bind_if_index != connect_if_index) { upipe_err(upipe, "incompatible bind and connect interfaces"); return -1; } if (connect_if_index) bind_if_index = connect_if_index; else connect_if_index = bind_if_index; /* RAW header */ if (*use_raw && raw_header) { upipe_udp_raw_fill_headers(upipe, raw_header, src_addr, connect_addr.sin.sin_addr.s_addr, src_port, ntohs(connect_addr.sin.sin_port), ttl, tos, 0); } /* Socket configuration */ int sock_type = SOCK_DGRAM; if (*use_tcp) sock_type = SOCK_STREAM; if (*use_raw) sock_type = SOCK_RAW; int sock_proto = (*use_raw ? IPPROTO_RAW : 0); if ((fd = socket(family, sock_type, sock_proto)) < 0) { upipe_err_va(upipe, "unable to open socket (%m)"); return -1; } #if !defined(__APPLE__) && !defined(__native_client__) if (*use_raw) { int hincl = 1; if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &hincl, sizeof(hincl)) < 0) { upipe_err_va(upipe, "unable to set IP_HDRINCL"); close(fd); return -1; } } #endif i = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&i, sizeof(i)) == -1) { upipe_err_va(upipe, "unable to set socket (%m)"); close(fd); return -1; } if (family == AF_INET6) { if (bind_if_index && setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, (void *)&bind_if_index, sizeof(bind_if_index)) < 0) { upipe_err(upipe, "couldn't set interface index"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } if (bind_addr.ss.ss_family != AF_UNSPEC) { #if !defined(__APPLE__) && !defined(__native_client__) if (IN6_IS_ADDR_MULTICAST(&bind_addr.sin6.sin6_addr)) { struct ipv6_mreq imr; union sockaddru bind_addr_any = bind_addr; bind_addr_any.sin6.sin6_addr = in6addr_any; if (bind(fd, &bind_addr_any.so, sizeof(bind_addr_any)) < 0) { upipe_err(upipe, "couldn't bind"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } imr.ipv6mr_multiaddr = bind_addr.sin6.sin6_addr; imr.ipv6mr_interface = bind_if_index; /* Join Multicast group without source filter */ if (setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&imr, sizeof(struct ipv6_mreq)) < 0) { upipe_err(upipe, "couldn't join multicast group"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } else #endif goto normal_bind; } } else if (bind_addr.ss.ss_family != AF_UNSPEC) { normal_bind: if (bind(fd, &bind_addr.so, sockaddr_len) < 0) { upipe_err(upipe, "couldn't bind"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } if (!*use_tcp) { /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to * avoid packet loss caused by scheduling problems */ i = 0x80000; if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void *) &i, sizeof(i))) upipe_warn(upipe, "fail to increase receive buffer"); /* Join the multicast group if the socket is a multicast address */ if (bind_addr.ss.ss_family == AF_INET && IN_MULTICAST(ntohl(bind_addr.sin.sin_addr.s_addr))) { #ifndef __native_client__ if (connect_addr.ss.ss_family != AF_UNSPEC) { /* Source-specific multicast */ struct ip_mreq_source imr; imr.imr_multiaddr = bind_addr.sin.sin_addr; imr.imr_interface.s_addr = if_addr; imr.imr_sourceaddr = connect_addr.sin.sin_addr; if (bind_if_index) { upipe_warn(upipe, "ignoring ifindex option in SSM"); } if (setsockopt(fd, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char *)&imr, sizeof(struct ip_mreq_source)) < 0) { upipe_err_va(upipe, "couldn't join multicast group (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } else if (bind_if_index) { /* Linux-specific interface-bound multicast */ struct ip_mreqn imr; imr.imr_multiaddr = bind_addr.sin.sin_addr; imr.imr_address.s_addr = if_addr; imr.imr_ifindex = bind_if_index; if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&imr, sizeof(struct ip_mreqn)) < 0) { upipe_err_va(upipe, "couldn't join multicast group (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } else #endif { /* Regular multicast */ struct ip_mreq imr; imr.imr_multiaddr = bind_addr.sin.sin_addr; imr.imr_interface.s_addr = if_addr; if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&imr, sizeof(struct ip_mreq)) < 0) { upipe_err_va(upipe, "couldn't join multicast group (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } #ifdef SO_BINDTODEVICE if (ifname) { /* linux specific, needs root or CAP_NET_RAW */ if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname) + 1) < 0) { upipe_err_va(upipe, "couldn't bind to device %s (%m)", ifname); free(ifname); close(fd); return -1; } ubase_clean_str(&ifname); } #endif } } if (connect_addr.ss.ss_family != AF_UNSPEC) { if (connect(fd, &connect_addr.so, sockaddr_len) < 0) { upipe_err_va(upipe, "cannot connect socket (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } if (!*use_tcp) { if (ttl) { if (family == AF_INET && IN_MULTICAST(ntohl(connect_addr.sin.sin_addr.s_addr))) { if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&ttl, sizeof(ttl)) == -1) { upipe_err_va(upipe, "couldn't set TTL (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } if (family == AF_INET6 && IN6_IS_ADDR_MULTICAST(&connect_addr.sin6.sin6_addr)) { if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (void *)&ttl, sizeof(ttl)) == -1) { upipe_err_va(upipe, "couldn't set TTL (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } } if (tos) { if (setsockopt(fd, IPPROTO_IP, IP_TOS, (void *)&tos, sizeof(tos)) == -1) { upipe_err_va(upipe, "couldn't set TOS (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } } } else if (*use_tcp) { /* Open in listen mode - wait for an incoming connection */ int new_fd; if (listen(fd, 1) < 0) { upipe_err_va(upipe, "couldn't listen (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } while ((new_fd = accept(fd, NULL, NULL)) < 0) { if (errno != EINTR) { upipe_err_va(upipe, "couldn't accept (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } close(fd); return new_fd; } return fd; }
/** @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 is called back to fill NaCl audio buffer. * Please note that this function runs in a different thread. * * @param sample_buffer buffer to fill * @param buffer_size size of the buffer in octets * @param latency how long before the audio data is to be presented * @param user_data opaque pointing to the pipe */ static void upipe_nacl_audio_worker(void *sample_buffer, uint32_t buffer_size, PP_TimeDelta latency, void *user_data) { struct upipe *upipe = (struct upipe *)user_data; struct upipe_nacl_audio *upipe_nacl_audio = upipe_nacl_audio_from_upipe(upipe); uint64_t next_pts = UINT64_MAX; if (likely(upipe_nacl_audio->uclock != NULL)) { /* This is slightly off. */ next_pts = uclock_now(upipe_nacl_audio->uclock) + (uint64_t)(latency * UCLOCK_FREQ); } uint32_t frames = buffer_size / 4; while (frames > 0) { if (upipe_nacl_audio->uref == NULL) upipe_nacl_audio->uref = uqueue_pop(&upipe_nacl_audio->uqueue, struct uref *); if (unlikely(upipe_nacl_audio->uref == NULL)) { upipe_dbg_va(upipe, "playing %u frames of silence (empty)", frames); memset(sample_buffer, 0, frames * 4); break; } struct uref *uref = upipe_nacl_audio->uref; if (next_pts != UINT64_MAX) { uint64_t uref_pts; if (unlikely(!ubase_check(uref_clock_get_pts_sys(uref, &uref_pts)))) { upipe_nacl_audio->uref = NULL; uref_free(uref); upipe_warn(upipe, "non-dated uref received"); continue; } int64_t tolerance = uref_pts - next_pts; if (tolerance > (int64_t)PTS_TOLERANCE) { uint32_t silence_frames = tolerance * SAMPLE_RATE / UCLOCK_FREQ; if (silence_frames > frames) silence_frames = frames; upipe_dbg_va(upipe, "playing %u frames of silence (wait)", silence_frames); memset(sample_buffer, 0, silence_frames * 4); sample_buffer += silence_frames * 4; frames -= silence_frames; continue; } else if (-tolerance > (int64_t)PTS_TOLERANCE) { uint32_t dropped_frames = (-tolerance) * SAMPLE_RATE / UCLOCK_FREQ; upipe_warn_va(upipe, "late buffer received, dropping %u frames", dropped_frames); upipe_nacl_audio_consume(upipe, dropped_frames); continue; } } size_t size; const void *uref_buffer; if (unlikely(!ubase_check(uref_sound_size(uref, &size, NULL)) || !ubase_check(uref_sound_plane_read_void(uref, "lr", 0, -1, &uref_buffer)))) { upipe_nacl_audio->uref = NULL; uref_free(uref); upipe_warn(upipe, "cannot read ubuf buffer"); continue; } uint32_t copied_frames = size < frames ? size : frames; memcpy(sample_buffer, uref_buffer, copied_frames * 4); uref_sound_plane_unmap(uref, "lr", 0, -1); sample_buffer += copied_frames * 4; upipe_nacl_audio_consume(upipe, copied_frames); frames -= copied_frames; } }