/** @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 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; }
/** @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 handles input buffers. * * @param upipe description structure of the pipe * @param uref input buffer to handle * @param upump_p reference to the pump that generated the buffer */ static void upipe_dvbcsa_enc_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_dvbcsa_enc *upipe_dvbcsa_enc = upipe_dvbcsa_enc_from_upipe(upipe); struct upipe_dvbcsa_common *common = upipe_dvbcsa_enc_to_common(upipe_dvbcsa_enc); if (unlikely(!upipe_dvbcsa_enc->key)) /* no key set, forward */ return upipe_dvbcsa_enc_output(upipe, uref, upump_p); uint32_t ts_header_size = TS_HEADER_SIZE; uint8_t buf[TS_HEADER_SIZE]; const uint8_t *ts_header = uref_block_peek(uref, 0, sizeof (buf), buf); if (unlikely(!ts_header)) { upipe_err(upipe, "fail to read ts header"); uref_free(uref); return; } uint8_t scrambling = ts_get_scrambling(ts_header); bool has_payload = ts_has_payload(ts_header); bool has_adaptation = ts_has_adaptation(ts_header); uint16_t pid = ts_get_pid(ts_header); uref_block_peek_unmap(uref, 0, buf, ts_header); if (!has_payload || scrambling || !upipe_dvbcsa_common_check_pid(common, pid)) return upipe_dvbcsa_enc_output(upipe, uref, upump_p); if (unlikely(has_adaptation)) { uint8_t af_length; int ret = uref_block_extract(uref, ts_header_size, 1, &af_length); if (unlikely(!ubase_check(ret))) { upipe_err(upipe, "fail to extract adaptation field length"); uref_free(uref); return; } if (unlikely(af_length >= 183)) { upipe_warn(upipe, "invalid adaptation field received"); uref_free(uref); return; } ts_header_size += af_length + 1; } struct ubuf *ubuf = ubuf_block_copy(uref->ubuf->mgr, uref->ubuf, 0, -1); if (unlikely(!ubuf)) { upipe_err(upipe, "fail to allocate buffer"); uref_free(uref); return; } uref_attach_ubuf(uref, ubuf); int size = -1; uint8_t *ts; int ret = ubuf_block_write(ubuf, 0, &size, &ts); if (unlikely(!ubase_check(ret))) { upipe_err(upipe, "fail to write buffer"); uref_free(uref); return; } ts_set_scrambling(ts, 0x2); dvbcsa_encrypt(upipe_dvbcsa_enc->key, ts + ts_header_size, size - ts_header_size); return upipe_dvbcsa_enc_output(upipe, uref, upump_p); }