/** @internal @This returns the index of an interface * * @param upipe description structure of the pipe * @param name interface name * @return interface index */ static bool upipe_udp_get_ifindex(struct upipe *upipe, const char *name, int *ifrindex) { #if !defined(__APPLE__) && !defined(__native_client__) int fd; struct ifreq ifr; if (! (name && ifrindex && upipe) ) { return false; } if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { upipe_err_va(upipe, "unable to open socket (%m)"); return false; } strncpy(ifr.ifr_name, name, IFNAMSIZ); ifr.ifr_name[IFNAMSIZ-1] = '\0'; if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { upipe_err_va(upipe, "unable to get interface index (%m)"); close(fd); return false; } close(fd); *ifrindex = ifr.ifr_ifindex; return true; #else upipe_err_va(upipe, "unable to get interface index (%m)"); return false; #endif }
/** @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; }
/** @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; }
static void upipe_dveo_asi_sink_stats(struct upipe *upipe) { struct upipe_dveo_asi_sink *upipe_dveo_asi_sink = upipe_dveo_asi_sink_from_upipe(upipe); int fd = upipe_dveo_asi_sink->fd; static uint64_t sum; sum += 188; //upipe_notice_va(upipe, "Wrote %"PRIu64" bytes into the card", sum); int val; if (ioctl(fd, ASI_IOC_TXGETEVENTS, &val) < 0) upipe_err_va(upipe, "ioctl TXGETEVENTS failed (%m)"); else { if (val & ASI_EVENT_TX_BUFFER) upipe_notice(upipe, "driver transmit buffer queue underrun"); if (val & ASI_EVENT_TX_FIFO) upipe_notice(upipe, "onboard transmit FIFO underrun"); if (val & ASI_EVENT_TX_DATA) { upipe_notice(upipe, "transmit data status change"); if (ioctl(fd, ASI_IOC_TXGETTXD, &val) < 0) upipe_err_va(upipe, "ioctl TXGETTXDfailed (%m)"); else upipe_notice_va(upipe, "transmitting: %d", val); } } if (ioctl(fd, ASI_IOC_TXGETBUFLEVEL, &val) < 0) upipe_err_va(upipe, "ioctl TXGETBUFLEVEL failed (%m)"); else { static int old; #define MARGIN 2 if ((val - MARGIN) > old || (val + MARGIN) < old) { float secs = (float)val * 6 * 196 * 8 / 10500000; upipe_notice_va(upipe, "buf level %d -> %.2fs", val, secs); old = val; } } if (ioctl(fd, ASI_IOC_TXGETBYTECOUNT, &val) < 0) upipe_err_va(upipe, "ioctl TXGETBYTECOUNTfailed (%m)"); else { /*static uint64_t byte_sum; byte_sum += val; upipe_notice_va(upipe, "byte count %d -> %"PRIu64, val, byte_sum);*/ } }
/** @internal @This allocates a block_to_sound 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_block_to_sound_alloc(struct upipe_mgr *mgr, struct uprobe *uprobe, uint32_t signature, va_list args) { struct uref *flow_def; struct upipe *upipe = upipe_block_to_sound_alloc_flow(mgr, uprobe, signature, args, &flow_def); if (unlikely(upipe == NULL)) return NULL; struct upipe_block_to_sound *upipe_block_to_sound = upipe_block_to_sound_from_upipe(upipe); if (unlikely(!ubase_check(uref_sound_flow_get_planes(flow_def, &upipe_block_to_sound->planes)) || upipe_block_to_sound->planes != 1 )) { upipe_err_va(upipe, "wrong number of planes: %d", upipe_block_to_sound->planes); upipe_block_to_sound_free_flow(upipe); uref_free(flow_def); return NULL; } if (unlikely(!ubase_check(uref_sound_flow_get_sample_size(flow_def, &upipe_block_to_sound->sample_size)))) { upipe_err_va(upipe, "flow def needs sample_size"); upipe_block_to_sound_free_flow(upipe); uref_free(flow_def); return NULL; } upipe_block_to_sound->flow_def_config = flow_def; upipe_block_to_sound_init_urefcount(upipe); upipe_block_to_sound_init_output(upipe); upipe_block_to_sound_init_ubuf_mgr(upipe); upipe_throw_ready(upipe); return upipe; }
/** @This starts the watcher waiting for the sink to unblock. * * @param upipe description structure of the pipe */ static void upipe_dveo_asi_sink_poll(struct upipe *upipe) { struct upipe_dveo_asi_sink *upipe_dveo_asi_sink = upipe_dveo_asi_sink_from_upipe(upipe); if (unlikely(!ubase_check(upipe_dveo_asi_sink_check_upump_mgr(upipe)))) { upipe_err_va(upipe, "can't get upump_mgr"); upipe_throw_fatal(upipe, UBASE_ERR_UPUMP); return; } struct upump *watcher = upump_alloc_fd_write(upipe_dveo_asi_sink->upump_mgr, upipe_dveo_asi_sink_watcher, upipe, upipe->refcount, upipe_dveo_asi_sink->fd); if (unlikely(watcher == NULL)) { upipe_err(upipe, "can't create watcher"); upipe_throw_fatal(upipe, UBASE_ERR_UPUMP); } else { upipe_dveo_asi_sink_set_upump(upipe, watcher); upump_start(watcher); } }
/** split callback */ static int split_catch(struct uprobe *uprobe, struct upipe *upipe, int event, va_list args) { if (event != UPROBE_NEED_OUTPUT) return uprobe_throw_next(uprobe, upipe, event, args); upipe_release(upipe_split_output); upipe_split_output = upipe_use(upipe); struct upipe *avcdec = upipe_void_alloc_output(upipe, upipe_avcdec_mgr, uprobe_pfx_alloc_va(uprobe_use(&uprobe_avcdec), loglevel, "avcdec")); if (avcdec == NULL) { upipe_err_va(upipe, "incompatible flow def"); upipe_release(upipe_source); return UBASE_ERR_UNHANDLED; } upipe_release(avcdec); return UBASE_ERR_NONE; }
/** @internal @This checks and parses a "#EXTINF" tag. * * @param upipe description structure of the pipe * @param flow_def the current flow definition * @param line the trailing characters of the line * @return an error code */ static int upipe_m3u_reader_process_extinf(struct upipe *upipe, struct uref *flow_def, const char *line) { const char *def; struct uref *item; UBASE_RETURN(uref_flow_get_def(flow_def, &def)); if (strcmp(def, M3U_FLOW_DEF) && strcmp(def, PLAYLIST_FLOW_DEF)) return UBASE_ERR_INVALID; UBASE_RETURN(uref_flow_set_def(flow_def, PLAYLIST_FLOW_DEF)); UBASE_RETURN(upipe_m3u_reader_get_item(upipe, flow_def, &item)); const char *endptr; uint64_t duration; UBASE_RETURN(duration_to_uclock(line, &endptr, &duration)); if (line == endptr || *endptr != ',') { upipe_err_va(upipe, "invalid segment duration `%s'", line); return UBASE_ERR_INVALID; } upipe_verbose_va(upipe, "segment duration: %"PRIu64, duration); return uref_m3u_playlist_set_seq_duration(item, duration); }
static uint64_t upipe_dveo_asi_sink_now(struct uclock *uclock) { struct upipe_dveo_asi_sink *upipe_dveo_asi_sink = upipe_dveo_asi_sink_from_uclock(uclock); if (upipe_dveo_asi_sink->fd < 0) return 0; unsigned int val; struct upipe *upipe = &upipe_dveo_asi_sink->upipe; if (ioctl(upipe_dveo_asi_sink->fd, ASI_IOC_TXGET27COUNT, &val) < 0) { upipe_err_va(upipe, "can't get hardware clock (%m)"); return 0; } if (val < upipe_dveo_asi_sink->last_val) { upipe_notice(upipe, "clock wrapping"); upipe_dveo_asi_sink->wraparounds++; } upipe_dveo_asi_sink->last_val = val; return (upipe_dveo_asi_sink->wraparounds << 32) + val; }
/** @internal @This checks and creates the upump watcher to wait for the * availability of the queue. * * @param upipe description structure of the pipe * @return false in case of error */ static bool upipe_nacl_audio_check_watcher(struct upipe *upipe) { struct upipe_nacl_audio *upipe_nacl_audio = upipe_nacl_audio_from_upipe(upipe); if (likely(upipe_nacl_audio->upump != NULL)) return true; upipe_nacl_audio_check_upump_mgr(upipe); if (upipe_nacl_audio->upump_mgr == NULL) return false; struct upump *upump = uqueue_upump_alloc_push(&upipe_nacl_audio->uqueue, upipe_nacl_audio->upump_mgr, upipe_nacl_audio_watcher, upipe, upipe->refcount); if (unlikely(upump == NULL)) { upipe_err_va(upipe, "can't create watcher"); upipe_throw_fatal(upipe, UBASE_ERR_UPUMP); return false; } upipe_nacl_audio_set_upump(upipe, upump); 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; }
/** avcdec callback */ static int avcdec_catch(struct uprobe *uprobe, struct upipe *upipe, int event, va_list args) { if (event != UPROBE_NEED_OUTPUT) return uprobe_throw_next(uprobe, upipe, event, args); struct uref *flow_def = va_arg(args, struct uref *); uint64_t hsize, vsize, wanted_hsize; struct urational sar; bool progressive; if (unlikely(!ubase_check(uref_pic_flow_get_hsize(flow_def, &hsize)) || !ubase_check(uref_pic_flow_get_vsize(flow_def, &vsize)) || !ubase_check(uref_pic_flow_get_sar(flow_def, &sar)))) { upipe_err_va(upipe, "incompatible flow def"); upipe_release(upipe_source); return UBASE_ERR_UNHANDLED; } wanted_hsize = (hsize * sar.num / sar.den / 2) * 2; progressive = ubase_check(uref_pic_get_progressive(flow_def)); struct uref *flow_def2 = uref_dup(flow_def); upipe_use(upipe); if (!progressive) { uref_pic_set_progressive(flow_def2); struct upipe *deint = upipe_void_alloc_output(upipe, upipe_filter_blend_mgr, uprobe_pfx_alloc(uprobe_use(logger), loglevel, "deint")); assert(deint != NULL); upipe_release(upipe); upipe = deint; } if (wanted_hsize != hsize) { uref_pic_flow_set_hsize(flow_def2, wanted_hsize); struct upipe *sws = upipe_flow_alloc_output(upipe, upipe_sws_mgr, uprobe_pfx_alloc_va(uprobe_use(logger), loglevel, "sws"), flow_def2); assert(sws != NULL); upipe_release(upipe); upipe = sws; } uref_pic_flow_clear_format(flow_def2); uref_flow_set_def(flow_def2, "block.mjpeg.pic."); struct upipe *jpegenc = upipe_flow_alloc_output(upipe, upipe_avcenc_mgr, uprobe_pfx_alloc_va(uprobe_use(logger), loglevel, "jpeg"), flow_def2); assert(jpegenc != NULL); upipe_release(upipe); upipe_set_option(jpegenc, "qmax", "2"); upipe = jpegenc; struct upipe *urefprobe = upipe_void_alloc_output(upipe, upipe_probe_uref_mgr, uprobe_pfx_alloc_va(uprobe_use(&uprobe_uref), loglevel, "urefprobe")); assert(urefprobe != NULL); upipe_release(upipe); upipe = urefprobe; struct upipe *fsink = upipe_void_alloc_output(upipe, upipe_fsink_mgr, uprobe_pfx_alloc_va(uprobe_use(logger), ((loglevel > UPROBE_LOG_DEBUG) ? UPROBE_LOG_WARNING : loglevel), "jpegsink")); assert(fsink != NULL); upipe_release(upipe); upipe_fsink_set_path(fsink, dstpath, UPIPE_FSINK_OVERWRITE); upipe = fsink; uref_free(flow_def2); upipe_release(upipe); return UBASE_ERR_NONE; }
/** @internal @This asks to open the given file. * * @param upipe description structure of the pipe * @param path relative or absolute path of the file * @param mode mode of opening the file * @return an error code */ static int _upipe_fsink_set_path(struct upipe *upipe, const char *path, enum upipe_fsink_mode mode) { struct upipe_fsink *upipe_fsink = upipe_fsink_from_upipe(upipe); if (unlikely(upipe_fsink->fd != -1)) { if (likely(upipe_fsink->path != NULL)) upipe_notice_va(upipe, "closing file %s", upipe_fsink->path); close(upipe_fsink->fd); } free(upipe_fsink->path); upipe_fsink->path = NULL; upipe_fsink_set_upump(upipe, NULL); if (!upipe_fsink_check_input(upipe)) /* Release the pipe used in @ref upipe_fsink_input. */ upipe_release(upipe); if (unlikely(path == NULL)) return UBASE_ERR_NONE; upipe_fsink_check_upump_mgr(upipe); const char *mode_desc = NULL; /* hush gcc */ int flags; switch (mode) { case UPIPE_FSINK_NONE: flags = 0; break; case UPIPE_FSINK_APPEND: mode_desc = "append"; flags = O_CREAT; break; case UPIPE_FSINK_OVERWRITE: mode_desc = "overwrite"; flags = O_CREAT | O_TRUNC; break; case UPIPE_FSINK_CREATE: mode_desc = "create"; flags = O_CREAT | O_EXCL; break; default: upipe_err_va(upipe, "invalid mode %d", mode); return UBASE_ERR_INVALID; } upipe_fsink->fd = open(path, O_WRONLY | O_NONBLOCK | O_CLOEXEC | flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (unlikely(upipe_fsink->fd == -1)) { upipe_err_va(upipe, "can't open file %s (%s)", path, mode_desc); return UBASE_ERR_EXTERNAL; } switch (mode) { /* O_APPEND seeks on each write, so use this instead */ case UPIPE_FSINK_APPEND: if (unlikely(lseek(upipe_fsink->fd, 0, SEEK_END) == -1)) { upipe_err_va(upipe, "can't append to file %s (%s)", path, mode_desc); close(upipe_fsink->fd); upipe_fsink->fd = -1; return UBASE_ERR_EXTERNAL; } break; default: break; } upipe_fsink->path = strdup(path); if (unlikely(upipe_fsink->path == NULL)) { close(upipe_fsink->fd); upipe_fsink->fd = -1; upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return UBASE_ERR_ALLOC; } if (!upipe_fsink_check_input(upipe)) /* Use again the pipe that we previously released. */ upipe_use(upipe); upipe_notice_va(upipe, "opening file %s in %s mode", upipe_fsink->path, mode_desc); return UBASE_ERR_NONE; }
/** @internal @This handles input. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump_p reference to upump structure * @return always true */ static bool upipe_filter_blend_handle(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_filter_blend *upipe_filter_blend = upipe_filter_blend_from_upipe(upipe); const char *def; if (unlikely(ubase_check(uref_flow_get_def(uref, &def)))) { upipe_filter_blend_store_flow_def(upipe, NULL); upipe_filter_blend_require_ubuf_mgr(upipe, uref); return true; } if (upipe_filter_blend->flow_def == NULL) return false; const uint8_t *in; uint8_t *out; uint8_t hsub, vsub, macropixel_size; size_t stride_in = 0, stride_out = 0, width, height; const char *chroma = NULL; struct ubuf *ubuf_deint = NULL; // Now process frames uref_pic_size(uref, &width, &height, NULL); upipe_verbose_va(upipe, "received pic (%zux%zu)", width, height); assert(upipe_filter_blend->ubuf_mgr); ubuf_deint = ubuf_pic_alloc(upipe_filter_blend->ubuf_mgr, width, height); if (unlikely(!ubuf_deint)) { upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); goto error; } // Iterate planes while (ubase_check(uref_pic_plane_iterate(uref, &chroma)) && chroma) { // map all if (unlikely(!ubase_check(uref_pic_plane_size(uref, chroma, &stride_in, &hsub, &vsub, ¯opixel_size)))) { upipe_err_va(upipe, "Could not read origin chroma %s", chroma); goto error; } if (unlikely(!ubase_check(ubuf_pic_plane_size(ubuf_deint, chroma, &stride_out, NULL, NULL, NULL)))) { upipe_err_va(upipe, "Could not read dest chroma %s", chroma); goto error; } uref_pic_plane_read(uref, chroma, 0, 0, -1, -1, &in); ubuf_pic_plane_write(ubuf_deint, chroma, 0, 0, -1, -1, &out); // process plane upipe_filter_blend_plane(in, out, stride_in, stride_out, (size_t) height/vsub, macropixel_size); // unmap all uref_pic_plane_unmap(uref, chroma, 0, 0, -1, -1); ubuf_pic_plane_unmap(ubuf_deint, chroma, 0, 0, -1, -1); } // Attach new ubuf and output frame uref_attach_ubuf(uref, ubuf_deint); uref_pic_set_progressive(uref); uref_pic_delete_tff(uref); upipe_filter_blend_output(upipe, uref, upump_p); return true; error: uref_free(uref); if (ubuf_deint) { ubuf_free(ubuf_deint); } return true; }
/** @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 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 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 is called by avcodec when allocating a new frame * @param context current avcodec context * @param frame avframe handler entering avcodec black magic box */ static int upipe_avcdec_get_buffer(struct AVCodecContext *context, AVFrame *frame) { struct upipe *upipe = context->opaque; struct upipe_avcdec *upipe_avcdec = upipe_avcdec_from_upipe(upipe); struct ubuf *ubuf_pic; int width_aligned, height_aligned, i; const struct upipe_av_plane *planes = NULL; size_t stride = 0; frame->opaque = uref_dup(upipe_avcdec->uref); uint64_t framenum = 0; uref_pic_get_number(frame->opaque, &framenum); upipe_dbg_va(upipe, "Allocating frame for %u (%p) - %ux%u", framenum, frame->opaque, frame->width, frame->height); if (unlikely(!upipe_avcdec->pixfmt)) { upipe_avcdec->pixfmt = upipe_av_pixfmt_from_ubuf_mgr(upipe_avcdec->ubuf_mgr); if (unlikely(!upipe_avcdec->pixfmt)) { upipe_err_va(upipe, "frame format of ubuf manager not recognized"); return 0; } } if (context->pix_fmt != *upipe_avcdec->pixfmt->pixfmt) { upipe_err_va(upipe, "frame format not compatible (%s != %s", av_get_pix_fmt_name(context->pix_fmt), av_get_pix_fmt_name(*upipe_avcdec->pixfmt->pixfmt)); return 0; } planes = upipe_avcdec->pixfmt->planes; /* direct rendering - allocate ubuf pic */ if (upipe_avcdec->context->codec->capabilities & CODEC_CAP_DR1) { width_aligned = context->width; height_aligned = context->height; /* use avcodec width/height alignement, then resize pic */ avcodec_align_dimensions(context, &width_aligned, &height_aligned); ubuf_pic = ubuf_pic_alloc(upipe_avcdec->ubuf_mgr, width_aligned, height_aligned); if (likely(ubuf_pic)) { ubuf_pic_resize(ubuf_pic, 0, 0, context->width, context->height); uref_attach_ubuf(frame->opaque, ubuf_pic); for (i=0; i < 4 && planes[i].chroma; i++) { ubuf_pic_plane_write(ubuf_pic, planes[i].chroma, 0, 0, -1, -1, &frame->data[i]); ubuf_pic_plane_size(ubuf_pic, planes[i].chroma, &stride, NULL, NULL, NULL); frame->linesize[i] = stride; } frame->extended_data = frame->data; frame->type = FF_BUFFER_TYPE_USER; return 1; /* success */ } else { upipe_dbg_va(upipe, "ubuf_pic_alloc(%d, %d) failed, fallback", width_aligned, height_aligned); } } /* default : DR failed or not available */ return avcodec_default_get_buffer(context, frame); }
/** @internal @This asks to open the given device. * * @param upipe description structure of the pipe * @param path relative or absolute path of the file * @return an error code */ static int upipe_dveo_asi_sink_open(struct upipe *upipe) { #define BYPASS_MODE 1 struct upipe_dveo_asi_sink *upipe_dveo_asi_sink = upipe_dveo_asi_sink_from_upipe(upipe); char path[20], sys[50], buf[20]; memset(buf, 0, sizeof(buf)); static const char dev_fmt[] = "/dev/asitx%u"; static const char sys_fmt[] = "/sys/class/asi/asitx%u/%s"; static const char dvbm_sys_fmt[] = "/sys/class/dvbm/%u/%s"; snprintf(sys, sizeof(sys), sys_fmt, upipe_dveo_asi_sink->card_idx, "timestamps"); snprintf(buf, sizeof(buf), "%u\n", 2); if (util_write(sys, buf, sizeof(buf)) < 0) { upipe_err_va(upipe, "Couldn't set timestamp mode (%m)"); return UBASE_ERR_EXTERNAL; } snprintf(sys, sizeof(sys), sys_fmt, upipe_dveo_asi_sink->card_idx, "bufsize"); snprintf(buf, sizeof(buf), "%u\n", 6*(188+8)); /* minimum is 1024 */ if (util_write(sys, buf, sizeof(buf)) < 0) { upipe_err_va(upipe, "Couldn't set buffer size (%m)"); return UBASE_ERR_EXTERNAL; } snprintf(sys, sizeof(sys), sys_fmt, upipe_dveo_asi_sink->card_idx, "buffers"); snprintf(buf, sizeof(buf), "%u\n", 500); if (util_write(sys, buf, sizeof(buf)) < 0) { upipe_err_va(upipe, "Couldn't set # of buffers (%m)"); return UBASE_ERR_EXTERNAL; } snprintf(path, sizeof(path), dev_fmt, upipe_dveo_asi_sink->card_idx); int fd = open(path, O_WRONLY | O_NONBLOCK | O_CLOEXEC); if (unlikely(fd < 0)) { upipe_err_va(upipe, "can't open file %s (%m)", path); return UBASE_ERR_EXTERNAL; } snprintf(sys, sizeof(sys), dvbm_sys_fmt, upipe_dveo_asi_sink->card_idx, "bypass_mode"); snprintf(buf, sizeof(buf), "%u", BYPASS_MODE); util_write(sys, buf, sizeof(buf)); /* Not all cards have this so don't fail */ unsigned int cap; if (ioctl(fd, ASI_IOC_TXGETCAP, &cap) < 0) { upipe_err_va(upipe, "can't get tx caps (%m)"); goto error; } unsigned long int bufsize; snprintf(sys, sizeof(sys), sys_fmt, upipe_dveo_asi_sink->card_idx, "bufsize"); if (util_strtoul(sys, &bufsize) < 0) { upipe_err(upipe, "Couldn't read buffer size"); goto error; } unsigned long int mode; snprintf(sys, sizeof(sys), sys_fmt, upipe_dveo_asi_sink->card_idx, "mode"); if (util_strtoul(sys, &mode) < 0) { upipe_err(upipe, "Couldn't read buffer size"); goto error; } switch (mode) { case ASI_CTL_TX_MODE_MAKE204: upipe_dbg(upipe, "Appending 0x00 bytes to make 204 bytes packets"); case ASI_CTL_TX_MODE_188: case ASI_CTL_TX_MODE_204: break; default: upipe_err_va(upipe, "Unknown TX mode %lu", mode); goto error; } unsigned long int clock_source = 0; if (cap & ASI_CAP_TX_SETCLKSRC) { snprintf(sys, sizeof(sys), sys_fmt, upipe_dveo_asi_sink->card_idx, "clock_source"); if (util_strtoul(sys, &clock_source) < 0) { upipe_err(upipe, "Couldn't read clock source"); goto error; } } switch (clock_source) { case ASI_CTL_TX_CLKSRC_ONBOARD: upipe_dbg(upipe, "Using onboard oscillator"); break; case ASI_CTL_TX_CLKSRC_EXT: upipe_dbg(upipe, "Using external reference clock"); break; case ASI_CTL_TX_CLKSRC_RX: upipe_dbg(upipe, "Using recovered receive clock"); break; default: upipe_dbg(upipe, "Unknown clock source"); break; } if (!(cap & ASI_CAP_TX_TIMESTAMPS)) { upipe_err(upipe, "Device does not support timestamps"); goto error; } upipe_dveo_asi_sink->fd = fd; upipe_notice_va(upipe, "opening file %s", path); return UBASE_ERR_NONE; error: close(fd); return UBASE_ERR_EXTERNAL; }
/** @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 * @return false if the input must be blocked */ static bool upipe_speexdsp_handle(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_speexdsp *upipe_speexdsp = upipe_speexdsp_from_upipe(upipe); struct urational drift_rate; if (!ubase_check(uref_clock_get_rate(uref, &drift_rate))) drift_rate = (struct urational){ 1, 1 }; /* reinitialize resampler when drift rate changes */ if (urational_cmp(&drift_rate, &upipe_speexdsp->drift_rate)) { upipe_speexdsp->drift_rate = drift_rate; spx_uint32_t ratio_num = drift_rate.den; spx_uint32_t ratio_den = drift_rate.num; spx_uint32_t in_rate = upipe_speexdsp->rate * ratio_num / ratio_den; spx_uint32_t out_rate = upipe_speexdsp->rate; int err = speex_resampler_set_rate_frac(upipe_speexdsp->ctx, ratio_num, ratio_den, in_rate, out_rate); if (err) { upipe_err_va(upipe, "Couldn't resample from %u to %u: %s", in_rate, out_rate, speex_resampler_strerror(err)); } else { upipe_dbg_va(upipe, "Resampling from %u to %u", in_rate, out_rate); } } size_t size; if (!ubase_check(uref_sound_size(uref, &size, NULL /* sample_size */))) { uref_free(uref); return true; } struct ubuf *ubuf = ubuf_sound_alloc(upipe_speexdsp->ubuf_mgr, size + 10); if (!ubuf) return false; const void *in; uref_sound_read_void(uref, 0, -1, &in, 1); void *out; ubuf_sound_write_void(ubuf, 0, -1, &out, 1); spx_uint32_t in_len = size; /* input size */ spx_uint32_t out_len = size + 10; /* available output size */ int err; if (upipe_speexdsp->f32) err = speex_resampler_process_interleaved_float(upipe_speexdsp->ctx, in, &in_len, out, &out_len); else err = speex_resampler_process_interleaved_int(upipe_speexdsp->ctx, in, &in_len, out, &out_len); if (err) { upipe_err_va(upipe, "Could not resample: %s", speex_resampler_strerror(err)); } uref_sound_unmap(uref, 0, -1, 1); ubuf_sound_unmap(ubuf, 0, -1, 1); if (err) { ubuf_free(ubuf); } else { ubuf_sound_resize(ubuf, 0, out_len); uref_attach_ubuf(uref, ubuf); } upipe_speexdsp_output(upipe, uref, upump_p); return true; } /** @internal @This receives incoming uref. * * @param upipe description structure of the pipe * @param uref uref structure describing the picture * @param upump_p reference to pump that generated the buffer */ static void upipe_speexdsp_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { if (!upipe_speexdsp_check_input(upipe)) { upipe_speexdsp_hold_input(upipe, uref); upipe_speexdsp_block_input(upipe, upump_p); } else if (!upipe_speexdsp_handle(upipe, uref, upump_p)) { upipe_speexdsp_hold_input(upipe, uref); upipe_speexdsp_block_input(upipe, upump_p); /* Increment upipe refcount to avoid disappearing before all packets * have been sent. */ upipe_use(upipe); } } /** @internal @This receives a provided ubuf manager. * * @param upipe description structure of the pipe * @param flow_format amended flow format * @return an error code */ static int upipe_speexdsp_check(struct upipe *upipe, struct uref *flow_format) { struct upipe_speexdsp *upipe_speexdsp = upipe_speexdsp_from_upipe(upipe); if (flow_format != NULL) upipe_speexdsp_store_flow_def(upipe, flow_format); if (upipe_speexdsp->flow_def == NULL) return UBASE_ERR_NONE; bool was_buffered = !upipe_speexdsp_check_input(upipe); upipe_speexdsp_output_input(upipe); upipe_speexdsp_unblock_input(upipe); if (was_buffered && upipe_speexdsp_check_input(upipe)) { /* All packets have been output, release again the pipe that has been * used in @ref upipe_speexdsp_input. */ upipe_release(upipe); } return UBASE_ERR_NONE; } /** @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_speexdsp_set_flow_def(struct upipe *upipe, struct uref *flow_def) { struct upipe_speexdsp *upipe_speexdsp = upipe_speexdsp_from_upipe(upipe); if (flow_def == NULL) return UBASE_ERR_INVALID; const char *def; UBASE_RETURN(uref_flow_get_def(flow_def, &def)) if (unlikely(ubase_ncmp(def, "sound.f32.") && ubase_ncmp(def, "sound.s16."))) return UBASE_ERR_INVALID; uint8_t in_planes; if (unlikely(!ubase_check(uref_sound_flow_get_planes(flow_def, &in_planes)))) return UBASE_ERR_INVALID; if (in_planes != 1) { upipe_err(upipe, "only interleaved audio is supported"); return UBASE_ERR_INVALID; } if (!ubase_check(uref_sound_flow_get_rate(flow_def, &upipe_speexdsp->rate))) { upipe_err(upipe, "no sound rate defined"); uref_dump(flow_def, upipe->uprobe); return UBASE_ERR_INVALID; } uint8_t channels; if (unlikely(!ubase_check(uref_sound_flow_get_channels(flow_def, &channels)))) return UBASE_ERR_INVALID; flow_def = uref_dup(flow_def); if (unlikely(flow_def == NULL)) { upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return UBASE_ERR_ALLOC; } upipe_speexdsp_require_ubuf_mgr(upipe, flow_def); if (upipe_speexdsp->ctx) speex_resampler_destroy(upipe_speexdsp->ctx); upipe_speexdsp->f32 = !ubase_ncmp(def, "sound.f32."); int err; upipe_speexdsp->ctx = speex_resampler_init(channels, upipe_speexdsp->rate, upipe_speexdsp->rate, upipe_speexdsp->quality, &err); if (!upipe_speexdsp->ctx) { upipe_err_va(upipe, "Could not create resampler: %s", speex_resampler_strerror(err)); return UBASE_ERR_INVALID; } return UBASE_ERR_NONE; } /** @internal @This provides a flow format suggestion. * * @param upipe description structure of the pipe * @param request description structure of the request * @return an error code */ static int upipe_speexdsp_provide_flow_format(struct upipe *upipe, struct urequest *request) { const char *def; UBASE_RETURN(uref_flow_get_def(request->uref, &def)) uint8_t channels; UBASE_RETURN(uref_sound_flow_get_channels(request->uref, &channels)) uint8_t planes; UBASE_RETURN(uref_sound_flow_get_planes(request->uref, &planes)) uint8_t sample_size; UBASE_RETURN(uref_sound_flow_get_sample_size(request->uref, &sample_size)) struct uref *flow = uref_dup(request->uref); UBASE_ALLOC_RETURN(flow); uref_sound_flow_clear_format(flow); uref_sound_flow_set_planes(flow, 0); uref_sound_flow_set_channels(flow, channels); uref_sound_flow_add_plane(flow, "all"); if (ubase_ncmp(def, "sound.s16.")) { uref_flow_set_def(flow, "sound.f32."); /* prefer f32 over s16 */ uref_sound_flow_set_sample_size(flow, 4 * channels); } else { uref_flow_set_def(flow, def); uref_sound_flow_set_sample_size(flow, (planes > 1) ? sample_size : sample_size / channels); } return urequest_provide_flow_format(request, flow); } /** @internal @This processes control commands on a speexdsp pipe. * * @param upipe description structure of the pipe * @param command type of command to process * @param args arguments of the command * @return an error code */ static int upipe_speexdsp_control(struct upipe *upipe, int command, va_list args) { struct upipe_speexdsp *upipe_speexdsp = upipe_speexdsp_from_upipe(upipe); switch (command) { /* generic commands */ case UPIPE_REGISTER_REQUEST: { struct urequest *request = va_arg(args, struct urequest *); if (request->type == UREQUEST_FLOW_FORMAT) return upipe_speexdsp_provide_flow_format(upipe, request); if (request->type == UREQUEST_UBUF_MGR) return upipe_throw_provide_request(upipe, request); return upipe_speexdsp_alloc_output_proxy(upipe, request); } case UPIPE_UNREGISTER_REQUEST: { struct urequest *request = va_arg(args, struct urequest *); if (request->type == UREQUEST_FLOW_FORMAT || request->type == UREQUEST_UBUF_MGR) return UBASE_ERR_NONE; return upipe_speexdsp_free_output_proxy(upipe, request); } case UPIPE_GET_OUTPUT: { struct upipe **p = va_arg(args, struct upipe **); return upipe_speexdsp_get_output(upipe, p); } case UPIPE_SET_OUTPUT: { struct upipe *output = va_arg(args, struct upipe *); return upipe_speexdsp_set_output(upipe, output); } case UPIPE_GET_FLOW_DEF: { struct uref **p = va_arg(args, struct uref **); return upipe_speexdsp_get_flow_def(upipe, p); } case UPIPE_SET_FLOW_DEF: { struct uref *flow = va_arg(args, struct uref *); return upipe_speexdsp_set_flow_def(upipe, flow); } case UPIPE_SET_OPTION: { const char *option = va_arg(args, const char *); const char *value = va_arg(args, const char *); if (strcmp(option, "quality")) return UBASE_ERR_INVALID; if (upipe_speexdsp->ctx) return UBASE_ERR_BUSY; int quality = atoi(value); if (quality > SPEEX_RESAMPLER_QUALITY_MAX) { quality = SPEEX_RESAMPLER_QUALITY_MAX; upipe_err_va(upipe, "Clamping quality to %d", SPEEX_RESAMPLER_QUALITY_MAX); } else if (quality < SPEEX_RESAMPLER_QUALITY_MIN) { quality = SPEEX_RESAMPLER_QUALITY_MIN; upipe_err_va(upipe, "Clamping quality to %d", SPEEX_RESAMPLER_QUALITY_MIN); } upipe_speexdsp->quality = quality; return UBASE_ERR_NONE; } default: return UBASE_ERR_UNHANDLED; } } /** @internal @This allocates a speexdsp 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_speexdsp_alloc(struct upipe_mgr *mgr, struct uprobe *uprobe, uint32_t signature, va_list args) { struct upipe *upipe = upipe_speexdsp_alloc_void(mgr, uprobe, signature, args); if (unlikely(upipe == NULL)) return NULL; struct upipe_speexdsp *upipe_speexdsp = upipe_speexdsp_from_upipe(upipe); upipe_speexdsp->ctx = NULL; upipe_speexdsp->drift_rate = (struct urational){ 0, 0 }; upipe_speexdsp->quality = SPEEX_RESAMPLER_QUALITY_MAX; upipe_speexdsp_init_urefcount(upipe); upipe_speexdsp_init_ubuf_mgr(upipe); upipe_speexdsp_init_output(upipe); upipe_speexdsp_init_flow_def(upipe); upipe_speexdsp_init_input(upipe); upipe_throw_ready(upipe); return upipe; } /** @This frees a upipe. * * @param upipe description structure of the pipe */ static void upipe_speexdsp_free(struct upipe *upipe) { struct upipe_speexdsp *upipe_speexdsp = upipe_speexdsp_from_upipe(upipe); if (likely(upipe_speexdsp->ctx)) speex_resampler_destroy(upipe_speexdsp->ctx); upipe_throw_dead(upipe); upipe_speexdsp_clean_input(upipe); upipe_speexdsp_clean_output(upipe); upipe_speexdsp_clean_flow_def(upipe); upipe_speexdsp_clean_ubuf_mgr(upipe); upipe_speexdsp_clean_urefcount(upipe); upipe_speexdsp_free_void(upipe); } /** module manager static descriptor */ static struct upipe_mgr upipe_speexdsp_mgr = { .refcount = NULL, .signature = UPIPE_SPEEXDSP_SIGNATURE, .upipe_alloc = upipe_speexdsp_alloc, .upipe_input = upipe_speexdsp_input, .upipe_control = upipe_speexdsp_control, .upipe_mgr_control = NULL }; /** @This returns the management structure for speexdsp pipes * * @return pointer to manager */ struct upipe_mgr *upipe_speexdsp_mgr_alloc(void) { return &upipe_speexdsp_mgr; }
/** @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 parses a new s337 header. * * @param upipe description structure of the pipe */ static void upipe_s337d_parse_preamble(struct upipe *upipe) { struct upipe_s337d *upipe_s337d = upipe_s337d_from_upipe(upipe); uint8_t preamble[S337_PREAMBLE_SIZE]; if (!ubase_check(uref_block_extract(upipe_s337d->next_uref, 0, S337_PREAMBLE_SIZE, preamble))) return; /* not enough data */ uint8_t data_type = s337_get_data_type(preamble); uint8_t data_mode = s337_get_data_mode(preamble); bool error = s337_get_error(preamble); uint8_t data_stream = s337_get_data_stream(preamble); uint8_t data_type_dep = s337_get_data_type_dep(preamble); upipe_s337d->next_frame_size = s337_get_length(preamble) + 7; upipe_s337d->next_frame_size /= 8; if (data_type != S337_TYPE_A52 && data_type != S337_TYPE_A52E) { upipe_s337d->next_frame_discard = true; return; } if (data_mode != S337_MODE_16) { upipe_err_va(upipe, "unsupported data mode (%"PRIu8")", data_mode); upipe_s337d->next_frame_discard = true; return; } upipe_s337d->next_frame_discard = false; if (error) upipe_warn(upipe, "error flag set"); if (upipe_s337d->data_stream != data_stream) { upipe_dbg_va(upipe, "now following stream %"PRIu8, data_stream); upipe_s337d->data_stream = data_stream; } if (upipe_s337d->data_type == data_type) return; upipe_s337d->data_type = data_type; if (data_type_dep & S337_TYPE_A52_REP_RATE_FLAG) upipe_warn(upipe, "repetition rate flag set"); if (data_type_dep & S337_TYPE_A52_NOT_FULL_SVC) upipe_warn(upipe, "not full service flag set"); struct uref *flow_def = upipe_s337d_alloc_flow_def_attr(upipe); if (unlikely(flow_def == NULL)) { upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return; } if (data_type == S337_TYPE_A52) UBASE_FATAL(upipe, uref_flow_set_def(flow_def, "block.ac3.sound.")) else UBASE_FATAL(upipe, uref_flow_set_def(flow_def, "block.eac3.sound.")) flow_def = upipe_s337d_store_flow_def_attr(upipe, flow_def); if (unlikely(!flow_def)) { upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return; } upipe_s337d_store_flow_def(upipe, flow_def); }
/** @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 * @param upump_p reference to pump that generated the buffer */ static bool upipe_s337_encaps_handle(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_s337_encaps *upipe_s337_encaps = upipe_s337_encaps_from_upipe(upipe); if (!upipe_s337_encaps->ubuf_mgr) return false; size_t block_size; if (!ubase_check(uref_block_size(uref, &block_size))) { upipe_err(upipe, "Couldn't read block size"); uref_free(uref); return true; } struct ubuf *ubuf = ubuf_sound_alloc(upipe_s337_encaps->ubuf_mgr, A52_FRAME_SAMPLES); if (!ubuf) return false; if (block_size / 2 > A52_FRAME_SAMPLES - 4) { upipe_err_va(upipe, "AC-3 block size %zu too big", block_size); block_size = (A52_FRAME_SAMPLES - 4) * 2; } int32_t *out_data; ubuf_sound_write_int32_t(ubuf, 0, -1, &out_data, 1); /* Pa, Pb, Pc, Pd */ out_data[0] = (S337_PREAMBLE_A1 << 24) | (S337_PREAMBLE_A2 << 16); out_data[1] = (S337_PREAMBLE_B1 << 24) | (S337_PREAMBLE_B2 << 16); out_data[2] = (S337_TYPE_A52 << 16) | (S337_MODE_16 << 21) | (S337_TYPE_A52_REP_RATE_FLAG << 24); out_data[3] = ((block_size * 8) & 0xffff) << 16; int offset = 0; uint8_t tmp = 0; while (block_size) { const uint8_t *buf; int size = block_size; if (!ubase_check(uref_block_read(uref, offset, &size, &buf)) || size == 0) { ubuf_sound_unmap(ubuf, 0, -1, 1); ubuf_free(ubuf); uref_free(uref); return true; } if (offset & 1) { out_data[4 + offset/2] = (tmp << 24) | (buf[0] << 16); buf++; offset++; block_size--; size--; } for (int i = 0; i < size/2; i++) out_data[4 + offset/2 + i] = (buf[2*i + 0] << 24) | (buf[2*i + 1] << 16); if (size & 1) tmp = buf[size-1]; uref_block_unmap(uref, offset); block_size -= size; offset += size; } memset(&out_data[4 + offset/2], 0, 4 * (A52_FRAME_SAMPLES*2 - 4 - offset/2)); ubuf_sound_unmap(ubuf, 0, -1, 1); uref_attach_ubuf(uref, ubuf); upipe_s337_encaps_output(upipe, uref, upump_p); return true; }