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 handles data. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump pump that generated the buffer */ static inline void upipe_skip_input_block(struct upipe *upipe, struct uref *uref, struct upump *upump) { struct upipe_skip *upipe_skip = upipe_skip_from_upipe(upipe); // skip given length uref_block_resize(uref, upipe_skip->offset, -1); upipe_skip_output(upipe, uref, upump); }
/** @internal @This takes the payload of a TS packet and finds PSI sections * inside it. * * @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_psim_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_ts_psim *upipe_ts_psim = upipe_ts_psim_from_upipe(upipe); if (unlikely(ubase_check(uref_flow_get_discontinuity(uref)))) upipe_ts_psim_flush(upipe); if (ubase_check(uref_block_get_start(uref))) { if (likely(upipe_ts_psim->acquired)) { /* just remove pointer_field */ if (unlikely(!ubase_check(uref_block_resize(uref, 1, -1)))) { uref_free(uref); upipe_ts_psim_flush(upipe); return; } } else { /* jump to the start of the next section */ uint8_t pointer_field; if (unlikely(!ubase_check(uref_block_extract(uref, 0, 1, &pointer_field)) || !ubase_check(uref_block_resize(uref, 1 + pointer_field, -1)))) { uref_free(uref); return; } upipe_ts_psim_sync_acquired(upipe); } uref_block_delete_start(uref); } else if (unlikely(upipe_ts_psim->next_uref == NULL)) { uref_free(uref); upipe_ts_psim_flush(upipe); return; } while (upipe_ts_psim_merge(upipe, uref, upump_p)); uref_free(uref); }
/** @internal @This tries to find TS packets in the buffered input urefs. * * @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_check_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_ts_check *upipe_ts_check = upipe_ts_check_from_upipe(upipe); size_t size; if (unlikely(!ubase_check(uref_block_size(uref, &size)))) { uref_free(uref); upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return; } bool first = true; while (size > upipe_ts_check->output_size) { struct uref *output = uref_block_splice(uref, 0, upipe_ts_check->output_size); if (unlikely(output == NULL)) { uref_free(uref); upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return; } if (!first) uref_flow_delete_discontinuity(output); first = false; if (!upipe_ts_check_check(upipe, output, upump_p)) { uref_free(uref); return; } uref_block_resize(uref, upipe_ts_check->output_size, -1); size -= upipe_ts_check->output_size; } if (!first) uref_flow_delete_discontinuity(uref); if (size == upipe_ts_check->output_size) upipe_ts_check_check(upipe, uref, upump_p); }
/** @internal @This outputs audio buffers * * @param upipe description structure of the pipe * @param frame AVFrame structure * @param upump upump structure */ static void upipe_avcdec_output_audio(struct upipe *upipe, AVFrame *frame, struct upump *upump) { struct ubuf *ubuf; struct upipe_avcdec *upipe_avcdec = upipe_avcdec_from_upipe(upipe); struct uref *uref = frame->opaque; int bufsize = -1, avbufsize; size_t size = 0; uint8_t *buf; AVCodecContext *context = upipe_avcdec->context; /* fetch audio sample size (in case it has been reduced) */ avbufsize = av_samples_get_buffer_size(NULL, context->channels, frame->nb_samples, context->sample_fmt, 1); /* if uref has no attached ubuf (ie DR not supported) */ if (unlikely(!uref->ubuf)) { ubuf = ubuf_block_alloc(upipe_avcdec->ubuf_mgr, avbufsize); if (unlikely(!ubuf)) { upipe_throw_aerror(upipe); return; } ubuf_block_write(ubuf, 0, &bufsize, &buf); memcpy(buf, frame->data[0], bufsize); uref_attach_ubuf(uref, ubuf); } /* unmap, reduce block if needed */ uref_block_unmap(uref, 0); uref_block_size(uref, &size); if (unlikely(size != avbufsize)) { uref_block_resize(uref, 0, avbufsize); } /* TODO: set attributes/need a real ubuf_audio structure (?) */ if (!upipe_avcdec->output_flow) { #if 0 struct uref *outflow = uref_sound_flow_alloc_def(upipe_avcdec->uref_mgr, context->channels, av_get_bytes_per_sample(context->sample_fmt)); #else struct uref *outflow = uref_block_flow_alloc_def(upipe_avcdec->uref_mgr, "sound."); #endif uref_sound_flow_set_channels(outflow, context->channels); uref_sound_flow_set_sample_size(outflow, av_get_bytes_per_sample(context->sample_fmt)); uref_sound_flow_set_rate(outflow, context->sample_rate); upipe_avcdec_store_flow_def(upipe, outflow); } /* samples in uref */ uref_sound_flow_set_samples(uref, frame->nb_samples); /* index rap attribute */ upipe_avcdec_set_index_rap(upipe, uref); upipe_avcdec_output(upipe, uref, upump); }
/** @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_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 merges a PSI section. * * @param upipe description structure of the pipe * @param uref uref pointing to (part of) a PSI section * @param upump_p reference to pump that generated the buffer * @return false if the uref has been entirely consumed */ static bool upipe_ts_psim_merge(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_ts_psim *upipe_ts_psim = upipe_ts_psim_from_upipe(upipe); if (upipe_ts_psim->next_uref != NULL) { struct ubuf *ubuf = ubuf_dup(uref->ubuf); if (unlikely(ubuf == NULL || !ubase_check(uref_block_append(upipe_ts_psim->next_uref, ubuf)))) { upipe_ts_psim_flush(upipe); if (ubuf != NULL) ubuf_free(ubuf); upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return false; } } else { /* Check for stuffing */ uint8_t table_id; if (unlikely(!ubase_check(uref_block_extract(uref, 0, 1, &table_id)) || table_id == 0xff)) { return false; } upipe_ts_psim->next_uref = uref_dup(uref); if (unlikely(upipe_ts_psim->next_uref == NULL)) { upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return false; } } size_t size = 0; UBASE_FATAL(upipe, uref_block_size(upipe_ts_psim->next_uref, &size)) if (size < PSI_HEADER_SIZE) return false; uint8_t buffer[PSI_HEADER_SIZE]; const uint8_t *psi_header = uref_block_peek(upipe_ts_psim->next_uref, 0, PSI_HEADER_SIZE, buffer); assert(psi_header != NULL); uint16_t length = psi_get_length(psi_header); UBASE_FATAL(upipe, uref_block_peek_unmap(upipe_ts_psim->next_uref, 0, buffer, psi_header)); if (unlikely(!psi_validate(psi_header) || length + PSI_HEADER_SIZE > PSI_PRIVATE_MAX_SIZE)) { upipe_warn(upipe, "wrong PSI header"); upipe_ts_psim_flush(upipe); return false; } if (length + PSI_HEADER_SIZE > size) return false; UBASE_FATAL(upipe, uref_block_resize(upipe_ts_psim->next_uref, 0, length + PSI_HEADER_SIZE)); upipe_ts_psim_output(upipe, upipe_ts_psim->next_uref, upump_p); upipe_ts_psim->next_uref = NULL; if (length + PSI_HEADER_SIZE == size) return false; size_t uref_size = 0; UBASE_FATAL(upipe, uref_block_size(uref, &uref_size)) UBASE_FATAL(upipe, uref_block_resize(uref, length + PSI_HEADER_SIZE - (size - uref_size), -1)); return true; }