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 input buffers. * * @param upipe description structure of the pipe * @param uref input buffer to handle * @param upump_p reference to pump that generated the buffer */ static void upipe_ablk_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_ablk *upipe_ablk = upipe_ablk_from_upipe(upipe); struct uref *input_flow_def = upipe_ablk->input_flow_def; struct uref *flow_def = upipe_ablk->flow_def; if (uref->ubuf) { upipe_ablk_output(upipe, uref, upump_p); return; } if (unlikely(!input_flow_def)) { upipe_warn(upipe, "no input flow definition"); uref_free(uref); return; } if (unlikely(!flow_def)) { upipe_warn(upipe, "no output flow definition set"); uref_free(uref); return; } if (unlikely(!upipe_ablk->ubuf_mgr)) { upipe_warn(upipe, "no ubuf manager set"); uref_free(uref); return; } if (unlikely(!upipe_ablk->ubuf)) { upipe_verbose(upipe, "allocate blank sound"); uint64_t samples = 0; uint8_t sample_size = 0; uref_sound_flow_get_samples(flow_def, &samples); uref_sound_flow_get_sample_size(flow_def, &sample_size); struct ubuf *ubuf = ubuf_sound_alloc(upipe_ablk->ubuf_mgr, samples); if (unlikely(!ubuf)) { upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); uref_free(uref); return; } const char *channel; uint8_t *buf = NULL; ubuf_sound_foreach_plane(ubuf, channel) { ubuf_sound_plane_write_uint8_t(ubuf, channel, 0, -1, &buf); memset(buf, 0, sample_size * samples); ubuf_sound_plane_unmap(ubuf, channel, 0, -1); }
/** @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 audio input. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump_p reference to upump structure */ static void upipe_osx_audioqueue_sink_input_audio(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_osx_audioqueue_sink *osx_audioqueue = upipe_osx_audioqueue_sink_from_upipe(upipe); struct AudioQueueBuffer *qbuf; size_t size = 0; if (unlikely(!ubase_check(uref_block_size(uref, &size)))) { upipe_warn(upipe, "could not get block size"); uref_free(uref); return; } /* TODO block ? */ #if 0 upump_mgr_use(upump->mgr); upump_mgr_sink_block(upump->mgr); #endif /* allocate queue buf, extract block, enqueue * Audioqueue has no support for "external" buffers */ AudioQueueAllocateBuffer(osx_audioqueue->queue, size, &qbuf); uref_block_extract(uref, 0, -1, qbuf->mAudioData); qbuf->mAudioDataByteSize = size; qbuf->mUserData = (*upump_p)->mgr; AudioQueueEnqueueBuffer(osx_audioqueue->queue, qbuf, 0, NULL); uref_free(uref); }
static void upipe_rtp_opus_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_rtp_opus *upipe_rtp_opus = upipe_rtp_opus_from_upipe(upipe); uint64_t timestamp = 0; size_t block_size = 0; if (!ubase_check(uref_block_size(uref, &block_size))) { upipe_warn(upipe, "fail to get uref block size"); return; } uref_rtp_get_timestamp(uref, ×tamp); uref_clock_set_pts_orig(uref, timestamp * TS_MULTIPLIER); uint64_t delta = (UINT_MAX + timestamp - (upipe_rtp_opus->last_rtp_timestamp % UINT_MAX)) % UINT_MAX; upipe_rtp_opus->last_rtp_timestamp += delta; uref_clock_set_pts_prog(uref, upipe_rtp_opus->last_rtp_timestamp * TS_MULTIPLIER); upipe_throw_clock_ref(upipe, uref, upipe_rtp_opus->last_rtp_timestamp * TS_MULTIPLIER, 0); upipe_throw_clock_ts(upipe, uref); upipe_rtp_opus_output(upipe, uref, upump_p); }
static bool upipe_dveo_asi_sink_add_header(struct upipe *upipe, struct uref *uref, uint64_t pts) { struct upipe_dveo_asi_sink *upipe_dveo_asi_sink = upipe_dveo_asi_sink_from_upipe(upipe); uint64_t hdr_size; if (!ubase_check(uref_block_get_header_size(uref, &hdr_size))) hdr_size = 0; if (hdr_size != 0) return false; /* alloc header */ struct ubuf *header = ubuf_block_alloc(uref->ubuf->mgr, 8); if (unlikely(!header)) { return true; } /* 63-bits timestamp */ union { uint8_t timestamp[8]; uint64_t pts; } timestamp; timestamp.pts = pts; if (upipe_dveo_asi_sink->first_timestamp) { upipe_dveo_asi_sink->first_timestamp = false; // FIXME: set the counter in an empty packet, and account for latency timestamp.pts |= 1LLU << 63; /* Set MSB = Set the counter */ } /* write header, 64 bits little-endian : * https://github.com/kierank/dveo-linux-master/blob/450e4b9e4292c2f71acd4d3d2e0a0cd0879d473a/doc/ASI/features.txt#L62 */ int size = 8; uint8_t *header_write_ptr; if (!ubase_check(ubuf_block_write(header, 0, &size, &header_write_ptr))) { upipe_err(upipe, "could not write header"); ubuf_free(header); return true; } memcpy(header_write_ptr, timestamp.timestamp, 8); uref_block_unmap(uref, 0); /* append payload (current ubuf) to header to form segmented ubuf */ struct ubuf *payload = uref_detach_ubuf(uref); if (unlikely(!ubase_check(ubuf_block_append(header, payload)))) { upipe_warn(upipe, "could not append payload to header"); ubuf_free(header); ubuf_free(payload); return true; } uref_attach_ubuf(uref, header); uref_block_set_header_size(uref, 8); return false; }
/** helper phony pipe */ static void test_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct x264_test *x264_test = x264_test_from_upipe(upipe); uint64_t pts = 0, dts = 0; if (uref->udict != NULL) { udict_dump(uref->udict, upipe->uprobe); } if (!ubase_check(uref_clock_get_pts_prog(uref, &pts))) { upipe_warn(upipe, "received packet with no pts"); } if (!ubase_check(uref_clock_get_dts_prog(uref, &dts))) { upipe_warn(upipe, "received packet with no dts"); } upipe_dbg_va(upipe, "received pic %d, pts: %"PRIu64" , dts: %"PRIu64, x264_test->counter, pts, dts); x264_test->counter++; uref_free(uref); }
/** @internal @This handles input. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump_p reference to upump structure */ static void upipe_filter_ebur128_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_filter_ebur128 *upipe_filter_ebur128 = upipe_filter_ebur128_from_upipe(upipe); double loud = 0, lra = 0, global = 0; size_t samples; if (unlikely(!ubase_check(uref_sound_size(uref, &samples, NULL)))) { upipe_warn(upipe, "invalid sound buffer"); uref_free(uref); return; } const char *channel = NULL; const int16_t *buf = NULL; if (ubase_check(uref_sound_plane_iterate(uref, &channel)) && channel) { if (unlikely(!ubase_check(uref_sound_plane_read_int16_t(uref, channel, 0, -1, &buf)))) { upipe_warn(upipe, "error mapping sound buffer"); uref_free(uref); return; } if (unlikely((uintptr_t)buf & 1)) { upipe_warn(upipe, "unaligned buffer"); } ebur128_add_frames_short(upipe_filter_ebur128->st, buf, samples); uref_sound_plane_unmap(uref, channel, 0, -1); } ebur128_loudness_momentary(upipe_filter_ebur128->st, &loud); ebur128_loudness_range(upipe_filter_ebur128->st, &lra); ebur128_loudness_global(upipe_filter_ebur128->st, &global); uref_ebur128_set_momentary(uref, loud); uref_ebur128_set_lra(uref, lra); uref_ebur128_set_global(uref, global); upipe_verbose_va(upipe, "loud %f lra %f global %f", loud, lra, global); upipe_filter_ebur128_output(upipe, uref, upump_p); }
/** @internal @This takes the payload of a TS packet, checks if it may * contain part of a PES header, and outputs 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_pesd_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_ts_pesd *upipe_ts_pesd = upipe_ts_pesd_from_upipe(upipe); size_t uref_size; if (unlikely(!ubase_check(uref_block_size(uref, &uref_size)))) { upipe_warn(upipe, "invalid PES chunk"); uref_free(uref); return; } if (ubase_check(uref_block_get_start(uref))) { if (unlikely(upipe_ts_pesd->next_uref != NULL)) { upipe_warn(upipe, "truncated PES header"); uref_free(upipe_ts_pesd->next_uref); } upipe_ts_pesd->next_uref = uref; upipe_ts_pesd->next_uref_size = uref_size; upipe_ts_pesd_decaps(upipe, upump_p); } else if (upipe_ts_pesd->next_uref != NULL) { struct ubuf *ubuf = uref_detach_ubuf(uref); uref_free(uref); if (unlikely(!ubase_check(uref_block_append(upipe_ts_pesd->next_uref, ubuf)))) { ubuf_free(ubuf); upipe_ts_pesd_flush(upipe); upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return; } upipe_ts_pesd->next_uref_size += uref_size; upipe_ts_pesd_decaps(upipe, upump_p); } else if (likely(upipe_ts_pesd->acquired)) { upipe_ts_pesd->next_uref = uref; upipe_ts_pesd->next_uref_size += uref_size; upipe_ts_pesd_check_output(upipe, upump_p); } else uref_free(uref); }
/** @internal @This handles input buffer of the blank source pipe. This buffer * is supposed to be a picture or a sound to set as source buffer of the blank * pipes (audio/video blank pipe generator). * * @param upipe description structure of the pipe * @param uref uref reference picture or sound * @param upump_p is ignored */ static void upipe_blksrc_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_blksrc *upipe_blksrc = upipe_blksrc_from_upipe(upipe); if (unlikely(!upipe_blksrc->flow_def)) { upipe_warn(upipe, "flow def is not set"); uref_free(uref); } else if (ubase_check(uref_flow_match_def(upipe_blksrc->flow_def, UREF_PIC_FLOW_DEF))) { upipe_dbg(upipe, "set picture buffer"); upipe_vblk_set_pic(upipe_blksrc->blk, uref); } else if (ubase_check(uref_flow_match_def(upipe_blksrc->flow_def, UREF_SOUND_FLOW_DEF))) upipe_ablk_set_sound(upipe_blksrc->blk, uref); else { upipe_warn(upipe, "unsupported flow definition"); uref_free(uref); } }
/** @internal @This tries to output frames from the queue of input buffers. * * @param upipe description structure of the pipe * @param upump_p reference to pump that generated the buffer */ static void upipe_a52f_work(struct upipe *upipe, struct upump **upump_p) { struct upipe_a52f *upipe_a52f = upipe_a52f_from_upipe(upipe); while (upipe_a52f->next_uref != NULL) { if (unlikely(!upipe_a52f->acquired)) { size_t dropped = 0; bool ret = upipe_a52f_scan(upipe, &dropped); upipe_a52f_consume_uref_stream(upipe, dropped); if (!ret) return; } if (upipe_a52f->next_frame_size == -1 && !upipe_a52f_parse_header(upipe)) { upipe_warn(upipe, "invalid header"); upipe_a52f_consume_uref_stream(upipe, 1); upipe_a52f_sync_lost(upipe); continue; } if (upipe_a52f->next_frame_size == -1) return; /* not enough data */ bool ready; if (unlikely(!upipe_a52f_check_frame(upipe, &ready))) { upipe_warn(upipe, "invalid frame"); upipe_a52f_consume_uref_stream(upipe, 1); upipe_a52f->next_frame_size = -1; upipe_a52f_sync_lost(upipe); continue; } if (!ready) return; /* not enough data */ upipe_a52f_sync_acquired(upipe); upipe_a52f_output_frame(upipe, upump_p); upipe_a52f->next_frame_size = -1; } }
/** @internal @This inputs data. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump_p reference to upump structure */ static void upipe_nacl_audio_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_nacl_audio *upipe_nacl_audio = upipe_nacl_audio_from_upipe(upipe); size_t uref_size; if (unlikely(!ubase_check(uref_sound_size(uref, &uref_size, NULL)))) { upipe_warn(upipe, "unable to read uref"); uref_free(uref); return; } if (unlikely(!upipe_nacl_audio->started && !upipe_nacl_audio_open(upipe))) { upipe_warn(upipe, "unable to open device"); uref_free(uref); return; } uint64_t uref_pts; if (likely(ubase_check(uref_clock_get_pts_sys(uref, &uref_pts)))) { uref_pts += upipe_nacl_audio->latency; uref_clock_set_pts_sys(uref, uref_pts); } if (!upipe_nacl_audio_check_input(upipe)) { upipe_nacl_audio_hold_input(upipe, uref); upipe_nacl_audio_block_input(upipe, upump_p); } else if (!upipe_nacl_audio_handle(upipe, uref, upump_p)) { if (!upipe_nacl_audio_check_watcher(upipe)) { upipe_warn(upipe, "unable to spool uref"); uref_free(uref); return; } upipe_nacl_audio_hold_input(upipe, uref); upipe_nacl_audio_block_input(upipe, upump_p); upump_start(upipe_nacl_audio->upump); } }
/** @internal @This creates a new audioqueue * @param upipe description structure of the pipe * @param flow description structure of the flow * @return an error code */ static int upipe_osx_audioqueue_sink_set_flow_def(struct upipe *upipe, struct uref *flow) { OSStatus status; uint64_t sample_rate = 0; /* hush gcc */ uint8_t channels = 0; uint8_t sample_size = 0; struct AudioStreamBasicDescription fmt; struct upipe_osx_audioqueue_sink *osx_audioqueue = upipe_osx_audioqueue_sink_from_upipe(upipe); if (unlikely(osx_audioqueue->queue)) { upipe_osx_audioqueue_sink_remove(upipe); } /* retrieve flow format information */ uref_sound_flow_get_rate(flow, &sample_rate); uref_sound_flow_get_sample_size(flow, &sample_size); uref_sound_flow_get_channels(flow, &channels); /* build format description */ memset(&fmt, 0, sizeof(struct AudioStreamBasicDescription)); fmt.mSampleRate = sample_rate; fmt.mFormatID = kAudioFormatLinearPCM; fmt.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; fmt.mFramesPerPacket = 1; fmt.mChannelsPerFrame = channels; fmt.mBytesPerPacket = fmt.mBytesPerFrame = sample_size * channels; fmt.mBitsPerChannel = sample_size * 8; /* create queue */ status = AudioQueueNewOutput(&fmt, upipe_osx_audioqueue_sink_cb, upipe, NULL, kCFRunLoopCommonModes, 0, &osx_audioqueue->queue); if (unlikely(status == kAudioFormatUnsupportedDataFormatError)) { upipe_warn(upipe, "unsupported data format"); return UBASE_ERR_EXTERNAL; } /* change volume */ AudioQueueSetParameter(osx_audioqueue->queue, kAudioQueueParam_Volume, osx_audioqueue->volume); /* start queue ! */ AudioQueueStart(osx_audioqueue->queue, NULL); upipe_notice_va(upipe, "audioqueue started (%uHz, %hhuch, %db)", sample_rate, channels, sample_size*8); return UBASE_ERR_NONE; }
/** @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 handles data. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump pump that generated the buffer */ static void _upipe_multicat_probe_input(struct upipe *upipe, struct uref *uref, struct upump *upump) { struct upipe_multicat_probe *upipe_multicat_probe = upipe_multicat_probe_from_upipe(upipe); uint64_t systime = 0; int64_t newidx; if (unlikely(!uref_clock_get_systime(uref, &systime))) { upipe_warn(upipe, "uref has no systime"); } newidx = (systime/upipe_multicat_probe->rotate); if (upipe_multicat_probe->idx != newidx) { upipe_throw(upipe, UPROBE_MULTICAT_PROBE_ROTATE, UPIPE_MULTICAT_PROBE_SIGNATURE, uref, newidx); upipe_multicat_probe->idx = newidx; } upipe_multicat_probe_output(upipe, uref, upump); }
/** @internal @This handles input uref. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump upump structure */ static void upipe_avcdec_input(struct upipe *upipe, struct uref *uref, struct upump *upump) { struct upipe_avcdec *upipe_avcdec = upipe_avcdec_from_upipe(upipe); if (upipe_avcdec->uref_mgr == NULL) { upipe_throw_need_uref_mgr(upipe); if (unlikely(upipe_avcdec->uref_mgr == NULL)) { uref_free(uref); return; } } const char *def = NULL; if (unlikely(uref_flow_get_def(uref, &def))) { if (unlikely(ubase_ncmp(def, EXPECTED_FLOW))) { upipe_throw_flow_def_error(upipe, uref); uref_free(uref); _upipe_avcdec_set_codec(upipe, NULL, NULL, 0); return; } upipe_dbg_va(upipe, "flow definition %s", def); def += strlen(EXPECTED_FLOW); _upipe_avcdec_set_codec(upipe, def, NULL, 0); uref_free(uref); return; } if (unlikely(uref_flow_get_end(uref))) { uref_free(uref); upipe_throw_need_input(upipe); return; } if (unlikely(!uref->ubuf)) { upipe_warn(upipe, "uref has no ubuf, dropping"); uref_free(uref); return; } upipe_avcdec_input_packet(upipe, uref, upump); }
/** @internal @This handles input. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump_p reference to upump structure */ static void upipe_osx_audioqueue_sink_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_osx_audioqueue_sink *osx_audioqueue = upipe_osx_audioqueue_sink_from_upipe(upipe); /* empty uref */ if (unlikely(!uref->ubuf)) { uref_free(uref); return; } /* check queue */ if (unlikely(!osx_audioqueue->queue)) { upipe_warn(upipe, "audioqueue not configured"); uref_free(uref); return; } upipe_osx_audioqueue_sink_input_audio(upipe, uref, upump_p); }
/** @internal @This parses A/52 Annex E header. * * @param upipe description structure of the pipe * @return false in case the header is inconsistent */ static bool upipe_a52f_parse_a52e(struct upipe *upipe) { struct upipe_a52f *upipe_a52f = upipe_a52f_from_upipe(upipe); uint8_t header[A52_SYNCINFO_SIZE]; if (unlikely(!ubase_check(uref_block_extract(upipe_a52f->next_uref, 0, sizeof(header), header)))) return true; /* not enough data */ if (likely(a52e_sync_compare_formats(header, upipe_a52f->sync_header))) { /* identical sync */ upipe_a52f->next_frame_size = a52e_get_frame_size(a52e_get_frmsiz(header)); return true; } /* sample rate */ uint64_t samplerate; switch (a52e_get_fscod(header)) { case A52_FSCOD_48KHZ: samplerate = 48000; break; case A52_FSCOD_441KHZ: samplerate = 44100; break; case A52_FSCOD_32KHZ: samplerate = 32000; break; case A52_FSCOD_RESERVED: switch (a52e_get_fscod2(header)) { case A52E_FSCOD2_24KHZ: samplerate = 24000; break; case A52E_FSCOD2_2205KHZ: samplerate = 22050; break; case A52E_FSCOD2_16KHZ: samplerate = 16000; break; default: upipe_warn(upipe, "reserved fscod2"); return false; } break; default: /* never reached */ return false; } /* frame size */ upipe_a52f->next_frame_size = a52e_get_frame_size(a52e_get_frmsiz(header)); /* channels */ int acmod = a52e_get_acmod(header); int channels = acmod_chans[acmod].nfchans + a52e_get_lfeon(header); uint64_t octetrate = (upipe_a52f->next_frame_size * samplerate + A52_FRAME_SAMPLES - 1) / A52_FRAME_SAMPLES; memcpy(upipe_a52f->sync_header, header, A52_SYNCINFO_SIZE); upipe_a52f->samplerate = samplerate; struct uref *flow_def = upipe_a52f_alloc_flow_def_attr(upipe); if (unlikely(!flow_def)) { upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return false; } UBASE_FATAL(upipe, uref_flow_set_complete(flow_def)) UBASE_FATAL(upipe, uref_flow_set_def(flow_def, "block.eac3.sound.")) UBASE_FATAL(upipe, uref_sound_flow_set_samples(flow_def, A52_FRAME_SAMPLES)) UBASE_FATAL(upipe, uref_sound_flow_set_rate(flow_def, samplerate)) UBASE_FATAL(upipe, uref_sound_flow_set_channels(flow_def, channels)) UBASE_FATAL(upipe, uref_clock_set_latency(flow_def, upipe_a52f->input_latency + UCLOCK_FREQ * A52_FRAME_SAMPLES / samplerate)) UBASE_FATAL(upipe, uref_block_flow_set_octetrate(flow_def, octetrate)) flow_def = upipe_a52f_store_flow_def_attr(upipe, flow_def); if (unlikely(!flow_def)) { upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return false; } upipe_a52f_store_flow_def(upipe, flow_def); return true; }
/** @internal @This handles input. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump_p reference to upump structure */ static void upipe_filter_ebur128_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_filter_ebur128 *upipe_filter_ebur128 = upipe_filter_ebur128_from_upipe(upipe); double loud = 0, lra = 0, global = 0; if (unlikely(upipe_filter_ebur128->output_flow == NULL)) { upipe_err_va(upipe, "invalid input"); uref_free(uref); return; } size_t samples; uint8_t sample_size; if (unlikely(!ubase_check(uref_sound_size(uref, &samples, &sample_size)))) { upipe_warn(upipe, "invalid sound buffer"); uref_free(uref); return; } void *buf = NULL; const char *channel = NULL; if (upipe_filter_ebur128->planes == 1) { if (ubase_check(uref_sound_plane_iterate(uref, &channel)) && channel) { if (unlikely(!ubase_check(uref_sound_plane_read_void(uref, channel, 0, -1, (const void **)&buf)))) { upipe_warn(upipe, "error mapping sound buffer"); uref_free(uref); return; } } } else { buf = malloc(sample_size * upipe_filter_ebur128->channels * samples); if (buf == NULL) { upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); uref_free(uref); return; } if (!ubase_check(uref_sound_interleave(uref, (uint8_t *)buf, 0, samples, sample_size, upipe_filter_ebur128->planes))) { upipe_warn(upipe, "error mapping sound buffer"); uref_free(uref); return; } } if (unlikely((uintptr_t)buf & 1)) upipe_warn(upipe, "unaligned buffer"); switch (upipe_filter_ebur128->fmt) { case UPIPE_FILTER_EBUR128_SHORT: ebur128_add_frames_short(upipe_filter_ebur128->st, (short *)buf, samples); break; case UPIPE_FILTER_EBUR128_INT: ebur128_add_frames_int(upipe_filter_ebur128->st, (int *)buf, samples); break; case UPIPE_FILTER_EBUR128_FLOAT: ebur128_add_frames_float(upipe_filter_ebur128->st, (float *)buf, samples); break; case UPIPE_FILTER_EBUR128_DOUBLE: ebur128_add_frames_double(upipe_filter_ebur128->st, (double *)buf, samples); break; default: upipe_warn_va(upipe, "unknown sample format %d", upipe_filter_ebur128->fmt); break; } if (upipe_filter_ebur128->planes == 1) uref_sound_plane_unmap(uref, channel, 0, -1); else free(buf); ebur128_loudness_momentary(upipe_filter_ebur128->st, &loud); ebur128_loudness_range(upipe_filter_ebur128->st, &lra); ebur128_loudness_global(upipe_filter_ebur128->st, &global); uref_ebur128_set_momentary(uref, loud); uref_ebur128_set_lra(uref, lra); uref_ebur128_set_global(uref, global); upipe_verbose_va(upipe, "loud %f lra %f global %f", loud, lra, global); upipe_filter_ebur128_output(upipe, uref, upump_p); }
/** @internal @This handles 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 handles packets. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump upump structure */ static void upipe_avcdec_input_packet(struct upipe *upipe, struct uref *uref, struct upump *upump) { uint8_t *inbuf; size_t insize = 0; struct upipe_avcdec *upipe_avcdec = upipe_avcdec_from_upipe(upipe); assert(upipe); assert(uref); if (upipe_avcdec->upump_av_deal) { /* pending open_codec callback */ upipe_dbg(upipe, "Received packet while open_codec pending"); if (upump) { upump_mgr_sink_block(upump->mgr); upump_mgr_use(upump->mgr); upipe_avcdec->saved_upump_mgr = upump->mgr; } if (upipe_avcdec->saved_uref) { upipe_warn(upipe, "Dropping previously saved packet !"); uref_free(upipe_avcdec->saved_uref); } upipe_avcdec->saved_uref = uref; return; } else if (upipe_avcdec->saved_uref) { upipe_dbg(upipe, "Processing previously saved packet"); struct uref *prev_uref = upipe_avcdec->saved_uref; upipe_avcdec->saved_uref = NULL; /* Not a typo, using the current upump here */ upipe_avcdec_input_packet(upipe, prev_uref, upump); } if (!upipe_avcdec->context) { uref_free(uref); upipe_warn(upipe, "Received packet but decoder is not initialized"); return; } /* avcodec input buffer needs to be at least 4-byte aligned and FF_INPUT_BUFFER_PADDING_SIZE larger than actual input size. Thus, extract ubuf content in a properly allocated buffer. Padding must be zeroed. */ uref_block_size(uref, &insize); if (unlikely(!insize)) { upipe_warn(upipe, "Received packet with size 0, dropping"); uref_free(uref); return; } upipe_dbg_va(upipe, "Received packet %u - size : %u", upipe_avcdec->counter, insize); inbuf = malloc(insize + FF_INPUT_BUFFER_PADDING_SIZE); if (unlikely(!inbuf)) { upipe_throw_aerror(upipe); return; } memset(inbuf, 0, insize + FF_INPUT_BUFFER_PADDING_SIZE); uref_block_extract(uref, 0, insize, inbuf); ubuf_free(uref_detach_ubuf(uref)); uref_pic_set_number(uref, upipe_avcdec->counter); /* Track current uref in pipe structure - required for buffer allocation * in upipe_avcdec_get_buffer */ upipe_avcdec->uref = uref; upipe_avcdec_process_buf(upipe, inbuf, insize, upump); free(inbuf); uref_free(uref); upipe_avcdec->counter++; }
/** @internal @This handles buffers once stripped from uref. * * @param upipe description structure of the pipe * @param buf buffer containing packet * @param size buffer size before padding * @param upump upump structure */ static bool upipe_avcdec_process_buf(struct upipe *upipe, uint8_t *buf, size_t size, struct upump *upump) { int gotframe = 0, len; AVPacket avpkt; AVFrame *frame; uint64_t framenum = 0; struct upipe_avcdec *upipe_avcdec = upipe_avcdec_from_upipe(upipe); assert(upipe); /* init avcodec packed and attach input buffer */ av_init_packet(&avpkt); avpkt.size = size; avpkt.data = buf; frame = upipe_avcdec->frame; switch (upipe_avcdec->context->codec->type) { case AVMEDIA_TYPE_VIDEO: { len = avcodec_decode_video2(upipe_avcdec->context, frame, &gotframe, &avpkt); if (len < 0) { upipe_warn(upipe, "Error while decoding frame"); } /* output frame if any has been decoded */ if (gotframe) { uref_pic_get_number(frame->opaque, &framenum); upipe_dbg_va(upipe, "%u\t - Picture decoded ! %dx%d - %u", upipe_avcdec->counter, frame->width, frame->height, (uint64_t) framenum); upipe_avcdec_output_frame(upipe, frame, upump); return true; } else { return false; } } case AVMEDIA_TYPE_AUDIO: { len = avcodec_decode_audio4(upipe_avcdec->context, frame, &gotframe, &avpkt); if (len < 0) { upipe_warn(upipe, "Error while decoding frame"); } /* output samples if any has been decoded */ if (gotframe) { upipe_avcdec_output_audio(upipe, frame, upump); return true; } else { return false; } } default: { /* should never be here */ upipe_err_va(upipe, "Unsupported media type (%d)", upipe_avcdec->context->codec->type); return false; } } }
/** @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 allocates a blank source pipe. * * @param mgr common management structure * @param uprobe structure used to raise events * @param signature signature of the pipe allocator * @param args optional arguments * @return pointer to upipe or NULL in case of allocation error */ static struct upipe *upipe_blksrc_alloc(struct upipe_mgr *mgr, struct uprobe *uprobe, uint32_t signature, va_list args) { struct uref *flow_def; struct upipe *upipe = upipe_blksrc_alloc_flow(mgr, uprobe, signature, args, &flow_def); if (unlikely(!upipe)) { return NULL; } struct upipe_blksrc *upipe_blksrc = upipe_blksrc_from_upipe(upipe); upipe_blksrc_init_urefcount(upipe); upipe_blksrc_init_urefcount_real(upipe); upipe_blksrc_init_src_probe(upipe); upipe_blksrc_init_blk_probe(upipe); upipe_blksrc_init_bin_input(upipe); upipe_blksrc_init_bin_output(upipe); upipe_blksrc->flow_def = flow_def; upipe_throw_ready(upipe); uint64_t duration = 0; if (ubase_check(uref_flow_match_def(flow_def, UREF_PIC_FLOW_DEF))) { struct urational fps; if (unlikely(!ubase_check(uref_pic_flow_get_fps(flow_def, &fps)))) { upipe_release(upipe); return NULL; } duration = (uint64_t)UCLOCK_FREQ * fps.den / fps.num; } else if (ubase_check(uref_flow_match_def(flow_def, UREF_SOUND_FLOW_DEF))) { uint64_t samples, rate; if (unlikely(!ubase_check(uref_sound_flow_get_samples(flow_def, &samples))) || unlikely(!ubase_check(uref_sound_flow_get_rate(flow_def, &rate)))) { upipe_release(upipe); return NULL; } duration = samples * UCLOCK_FREQ / rate; } else { upipe_warn(upipe, "unsupported flow def"); upipe_release(upipe); return NULL; } struct uref *src_flow_def = uref_void_flow_alloc_def(flow_def->mgr); if (unlikely(!src_flow_def)) { upipe_err(upipe, "fail to allocate source pipe flow def"); upipe_release(upipe); return NULL; } if (unlikely(!ubase_check(uref_clock_set_duration(src_flow_def, duration)))) { uref_free(src_flow_def); upipe_release(upipe); return NULL; } struct upipe_mgr *upipe_voidsrc_mgr = upipe_voidsrc_mgr_alloc(); if (unlikely(!upipe_voidsrc_mgr)) { upipe_err(upipe, "fail to get void source manager"); uref_free(src_flow_def); upipe_release(upipe); return NULL; } struct upipe *src = upipe_flow_alloc(upipe_voidsrc_mgr, uprobe_pfx_alloc( uprobe_use(&upipe_blksrc->src_probe), UPROBE_LOG_VERBOSE, "src"), src_flow_def); upipe_mgr_release(upipe_voidsrc_mgr); uref_free(src_flow_def); if (unlikely(!src)) { upipe_err(upipe, "fail to allocate source pipe"); upipe_release(upipe); return NULL; } upipe_blksrc_store_bin_input(upipe, src); struct upipe_mgr *upipe_blk_mgr = NULL; if (ubase_check(uref_flow_match_def(flow_def, UREF_PIC_FLOW_DEF))) { upipe_blk_mgr = upipe_vblk_mgr_alloc(); } else if (ubase_check(uref_flow_match_def(flow_def, UREF_SOUND_FLOW_DEF))) { upipe_blk_mgr = upipe_ablk_mgr_alloc(); } if (unlikely(!upipe_blk_mgr)) { upipe_err(upipe, "fail to get blank generator manager"); upipe_release(upipe); return NULL; } struct upipe *blk = upipe_flow_alloc(upipe_blk_mgr, uprobe_pfx_alloc( uprobe_use(&upipe_blksrc->blk_probe), UPROBE_LOG_VERBOSE, "blk"), flow_def); upipe_mgr_release(upipe_blk_mgr); if (unlikely(!blk)) { upipe_err(upipe, "fail to allocate blank generator pipe"); upipe_release(upipe); return NULL; } upipe_blksrc_store_bin_output(upipe, blk); if (unlikely(!ubase_check(upipe_set_output(src, blk)))) { upipe_release(upipe); return NULL; } return upipe; }
/** @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 the input uref. * * @param upipe description structure of the pipe * @param uref input uref * @param upump_p reference to pump that generated the buffer */ static void upipe_vblk_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_vblk *upipe_vblk = upipe_vblk_from_upipe(upipe); struct uref *flow_def = upipe_vblk->flow_def; struct uref *input_flow_def = upipe_vblk->input_flow_def; if (uref->ubuf) { upipe_vblk_output(upipe, uref, upump_p); return; } if (unlikely(!input_flow_def)) { upipe_warn(upipe, "no input flow definition"); uref_free(uref); return; } if (unlikely(!flow_def)) { upipe_warn(upipe, "no output flow definition"); uref_free(uref); return; } if (unlikely(!upipe_vblk->ubuf_mgr)) { upipe_warn(upipe, "no ubuf manager set"); uref_free(uref); return; } if (unlikely(!upipe_vblk->ubuf)) { upipe_verbose(upipe, "allocate blank picture"); uint64_t hsize, vsize; ubase_assert(uref_pic_flow_get_hsize(upipe_vblk->flow_def, &hsize)); ubase_assert(uref_pic_flow_get_vsize(upipe_vblk->flow_def, &vsize)); upipe_vblk->ubuf = ubuf_pic_alloc(upipe_vblk->ubuf_mgr, hsize, vsize); if (unlikely(!upipe_vblk->ubuf)) { upipe_err(upipe, "fail to allocate picture"); uref_free(uref); upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return; } ubuf_pic_clear(upipe_vblk->ubuf, 0, 0, -1, -1, 1); } struct ubuf *ubuf = ubuf_dup(upipe_vblk->ubuf); if (unlikely(!ubuf)) { upipe_err(upipe, "fail to duplicate blank picture"); uref_free(uref); upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return; } uref_attach_ubuf(uref, ubuf); if (ubase_check(uref_pic_get_progressive(flow_def))) uref_pic_set_progressive(uref); upipe_vblk_output(upipe, uref, upump_p); }
/** @internal @This parses A/52 header. * * @param upipe description structure of the pipe * @return false in case the header is inconsistent */ static bool upipe_a52f_parse_a52(struct upipe *upipe) { struct upipe_a52f *upipe_a52f = upipe_a52f_from_upipe(upipe); uint8_t header[A52_SYNCINFO_SIZE + 2]; if (unlikely(!ubase_check(uref_block_extract(upipe_a52f->next_uref, 0, sizeof(header), header)))) return true; /* not enough data */ ssize_t next_frame_size = a52_get_frame_size(a52_get_fscod(header), a52_get_frmsizecod(header)); if (!next_frame_size) return false; if (likely(a52_sync_compare_formats(header, upipe_a52f->sync_header) && header[5] == upipe_a52f->sync_header[5] && header[6] == upipe_a52f->sync_header[6])) { /* identical sync */ upipe_a52f->next_frame_size = next_frame_size; return true; } /* sample rate */ uint64_t samplerate; switch (a52_get_fscod(header)) { case A52_FSCOD_48KHZ: samplerate = 48000; break; case A52_FSCOD_441KHZ: samplerate = 44100; break; case A52_FSCOD_32KHZ: samplerate = 32000; break; default: upipe_warn(upipe, "reserved fscod"); return false; } /* frame size */ upipe_a52f->next_frame_size = next_frame_size; /* channels */ int acmod = a52_get_acmod(header); int channels = acmod_chans[acmod].nfchans + ((header[6] >> (4 - acmod_chans[acmod].lfe_offset)) & 1); uint64_t octetrate = a52_bitrate_tab[a52_get_frmsizecod(header)] * 1000 / 8; memcpy(upipe_a52f->sync_header, header, A52_SYNCINFO_SIZE + 2); upipe_a52f->samplerate = samplerate; struct uref *flow_def = upipe_a52f_alloc_flow_def_attr(upipe); if (unlikely(!flow_def)) { upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return false; } UBASE_FATAL(upipe, uref_flow_set_complete(flow_def)) UBASE_FATAL(upipe, uref_flow_set_def(flow_def, "block.ac3.sound.")) UBASE_FATAL(upipe, uref_sound_flow_set_samples(flow_def, A52_FRAME_SAMPLES)) UBASE_FATAL(upipe, uref_sound_flow_set_rate(flow_def, samplerate)) UBASE_FATAL(upipe, uref_sound_flow_set_channels(flow_def, channels)) UBASE_FATAL(upipe, uref_clock_set_latency(flow_def, upipe_a52f->input_latency + UCLOCK_FREQ * A52_FRAME_SAMPLES / samplerate)) UBASE_FATAL(upipe, uref_block_flow_set_octetrate(flow_def, octetrate)) flow_def = upipe_a52f_store_flow_def_attr(upipe, flow_def); if (unlikely(!flow_def)) { upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return false; } upipe_a52f_store_flow_def(upipe, flow_def); return true; }
/** @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; }
/** @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); }
static void upipe_rtp_h264_drop(struct upipe *upipe, struct uref *uref) { upipe_warn(upipe, "drop..."); uref_free(uref); }