/** @internal @This allocates a hls pipe. * * @param mgr management structure for this pipe type * @param uprobe structure used to raise events * @param signature signature of the pipe allocator * @param args optional arguments * @return pointer to allocated pipe, or NULL in case of failure */ static struct upipe *upipe_hls_alloc(struct upipe_mgr *mgr, struct uprobe *uprobe, uint32_t signature, va_list args) { struct upipe *upipe = upipe_hls_alloc_void(mgr, uprobe, signature, args); if (unlikely(upipe == NULL)) return NULL; struct upipe_hls *upipe_hls = upipe_hls_from_upipe(upipe); upipe_hls_init_urefcount(upipe); upipe_hls_init_urefcount_real(upipe); upipe_hls_init_probe_reader(upipe); upipe_hls_init_probe_master(upipe); upipe_hls_init_probe_null(upipe); upipe_hls_init_first_inner(upipe); upipe_hls_init_last_inner(upipe); upipe_hls_init_bin_input(upipe); upipe_hls_init_bin_output(upipe); upipe_hls->flow_def = NULL; upipe_hls->item = NULL; upipe_hls->sub_pipe_mgr = NULL; upipe_throw_ready(upipe); struct upipe *inner = NULL; struct upipe_mgr *upipe_m3u_reader_mgr = upipe_m3u_reader_mgr_alloc(); if (unlikely(!upipe_m3u_reader_mgr)) { upipe_err(upipe, "fail to allocate m3u reader manager"); upipe_release(upipe); return NULL; } inner = upipe_void_alloc( upipe_m3u_reader_mgr, uprobe_pfx_alloc(uprobe_use(&upipe_hls->probe_reader), UPROBE_LOG_VERBOSE, "reader")); upipe_mgr_release(upipe_m3u_reader_mgr); if (unlikely(!inner)) { upipe_err(upipe, "fail to allocate m3u reader"); upipe_release(upipe); return NULL; } upipe_hls_store_bin_input(upipe, inner); return upipe; }
/** @internal @This parses and outputs a m3u file. * * @param upipe description structure of the pipe * @return an error code */ static void upipe_m3u_reader_process(struct upipe *upipe) { struct upipe_m3u_reader *upipe_m3u_reader = upipe_m3u_reader_from_upipe(upipe); if (unlikely(upipe_m3u_reader->current_flow_def == NULL)) { upipe_m3u_reader->current_flow_def = uref_dup(upipe_m3u_reader->flow_def); if (unlikely(upipe_m3u_reader->current_flow_def == NULL)) { upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return; } } /* parse m3u */ int ret = UBASE_ERR_NONE; struct uref *uref = upipe_m3u_reader->next_uref; for (size_t offset = 0; uref && ubase_check(ret) && ubase_check(uref_block_scan(uref, &offset, '\n')); offset = 0, uref = upipe_m3u_reader->next_uref) { struct uref *line = upipe_m3u_reader_extract_uref_stream(upipe, offset + 1); ret = upipe_m3u_reader_process_line( upipe, upipe_m3u_reader->current_flow_def, line); uref_free(line); } if (!ubase_check(ret)) upipe_err(upipe, "invalid m3u"); }
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; }
static void upipe_rtp_h264_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { size_t bz = 0; if (!ubase_check(uref_block_size(uref, &bz))) { upipe_err(upipe, "fail to get uref block size"); return upipe_rtp_h264_drop(upipe, uref); } uint8_t buf[bz]; int size = bz; if (unlikely(!ubase_check(uref_block_extract(uref, 0, size, buf)))) { upipe_err(upipe, "fail to read from uref"); return upipe_rtp_h264_drop(upipe, uref); } uint8_t s_len; const uint8_t *s = upipe_mpeg_scan(buf, buf + size, &s_len); if (s != buf) { upipe_err(upipe, "uref does not start with a mpeg start code"); return upipe_rtp_h264_drop(upipe, uref); } while (s < buf + size) { uint8_t e_len = 0; const uint8_t *e = upipe_mpeg_scan(s + s_len, buf + size, &e_len); if (!e) e = buf + size; struct uref *part = uref_block_splice(uref, s - buf + s_len + 1, e - (s + s_len + 1)); upipe_rtp_h264_output_nalu(upipe, *(s + s_len), part, upump_p); s = e; s_len = e_len; } uref_free(uref); }
/** @internal @This is the open_codec upump callback * It calls _open_codec_cb. * * @param upump description structure of the pump */ static void upipe_avcdec_open_codec_cb(struct upump *upump) { assert(upump); struct upipe *upipe = upump_get_opaque(upump, struct upipe*); struct upipe_avcdec *upipe_avcdec = upipe_avcdec_from_upipe(upipe); struct upipe_avcodec_open_params *params = &upipe_avcdec->open_params; struct upump *upump_av_deal = upipe_avcdec->upump_av_deal; /* check udeal */ if (upump_av_deal) { if (unlikely(!upipe_av_deal_grab())) { upipe_dbg(upipe, "could not grab resource, return"); return; } upipe_avcdec->upump_av_deal = NULL; } /* real open_codec function */ bool ret = upipe_avcdec_open_codec(upipe, params->codec, params->extradata, params->extradata_size); /* clean dealer */ if (unlikely(!upipe_av_deal_yield(upump_av_deal))) { upump_free(upump_av_deal); upump_av_deal = NULL; upipe_err(upipe, "can't stop dealer"); upipe_throw_upump_error(upipe); if (upipe_avcdec->context) { avcodec_close(upipe_avcdec->context); av_free(upipe_avcdec->context); upipe_avcdec->context = NULL; } return; } upump_free(upump_av_deal); upump_av_deal = NULL; if (!ret) { return; } /* unblock input pump*/ if (upipe_avcdec->saved_upump_mgr) { upipe_dbg(upipe, "unblocking saved upump_mgr"); upump_mgr_sink_unblock(upipe_avcdec->saved_upump_mgr); upump_mgr_release(upipe_avcdec->saved_upump_mgr); upipe_avcdec->saved_upump_mgr = NULL; } }
/** @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); } }
/** @internal @This checks a "#EXTM3U" 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_m3u(struct upipe *upipe, struct uref *flow_def, const char *line) { while (isspace(*line)) line++; if (strlen(line)) { upipe_err(upipe, "invalid EXTM3U tag"); return UBASE_ERR_INVALID; } upipe_verbose(upipe, "found EXTM3U tag"); const char *def; UBASE_RETURN(uref_flow_get_def(flow_def, &def)); if (strcmp(def, EXPECTED_FLOW_DEF)) return UBASE_ERR_INVALID; return uref_flow_set_def(flow_def, M3U_FLOW_DEF); }
/** @internal @This outputs the m3u. * * @param upipe description structure of the pipe * @param upump_p reference to source pump to block */ static void upipe_m3u_reader_output_all(struct upipe *upipe, struct upump **upump_p) { struct upipe_m3u_reader *upipe_m3u_reader = upipe_m3u_reader_from_upipe(upipe); struct uref *flow_def = upipe_m3u_reader->current_flow_def; upipe_m3u_reader->current_flow_def = NULL; if (unlikely(!ubase_check(uref_flow_match_def(flow_def, M3U_FLOW_DEF)))) { upipe_err(upipe, "invalid m3u"); uref_free(flow_def); return; } /* force new flow def */ upipe_m3u_reader_store_flow_def(upipe, NULL); /* set output flow def */ upipe_m3u_reader_store_flow_def(upipe, flow_def); /* output */ struct uchain *uchain; bool first = true; upipe_use(upipe); while ((uchain = ulist_pop(&upipe_m3u_reader->items)) != NULL) { struct uref *uref = uref_from_uchain(uchain); if (first) uref_block_set_start(uref); first = false; if (ulist_empty(&upipe_m3u_reader->items)) uref_block_set_end(uref); upipe_m3u_reader_output(upipe, uref, upump_p); } upipe_release(upipe); }
/** @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 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); }
static void upipe_rtp_h264_output_nalu(struct upipe *upipe, uint8_t nalu, struct uref *uref, struct upump **upump_p) { size_t size = 0; if (unlikely(!ubase_check(uref_block_size(uref, &size)))) { upipe_err(upipe, "fail to get block size"); return; } bool split = size > RTP_SPLIT_SIZE; uint32_t fragment = 0; while (size) { bool last_fragment = size <= RTP_SPLIT_SIZE; size_t split_size = last_fragment ? size : RTP_SPLIT_SIZE; uint8_t hdr[2] = { nalu, 0 }; size_t hdr_size = 1; if (split) { if (!fragment) hdr[1] = FU_START; else if (last_fragment) hdr[1] = FU_END; hdr[1] |= NALU_TYPE(hdr[0]); hdr[0] = NALU_F(hdr[0]) | NALU_NRI(hdr[0]) | FU_A; hdr_size++; } struct uref *next = NULL; if (!last_fragment) { next = uref_block_split(uref, split_size); if (unlikely(next == NULL)) { upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return; } } /* FIXME should require a ubuf_mgr */ struct ubuf *header = ubuf_block_alloc(uref->ubuf->mgr, hdr_size * sizeof (hdr[0])); if (unlikely(header == NULL)) { upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); uref_free(uref); uref_free(next); return; } uint8_t *buf = NULL; int buf_size = hdr_size; ubuf_block_write(header, 0, &buf_size, &buf); memcpy(buf, hdr, hdr_size * sizeof (hdr[0])); ubuf_block_unmap(header, 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); uref_free(uref); uref_free(next); return; } uref_attach_ubuf(uref, header); uref_clock_set_cr_dts_delay(uref, 0); upipe_rtp_h264_output(upipe, uref, upump_p); size -= split_size; fragment++; uref = next; } }
/** @internal @This converts block urefs to sound urefs. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump_p reference to pump that generated the buffer */ static void upipe_block_to_sound_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_block_to_sound *upipe_block_to_sound = upipe_block_to_sound_from_upipe(upipe); /* get block size */ size_t block_size = 0; uref_block_size(uref, &block_size); /* drop incomplete samples */ if ((block_size % upipe_block_to_sound->sample_size) != 0) { upipe_warn(upipe, "Incomplete samples detected"); } int samples = block_size / upipe_block_to_sound->sample_size; block_size = samples * upipe_block_to_sound->sample_size; /* alloc sound ubuf */ struct ubuf *ubuf_block_to_sound = ubuf_sound_alloc(upipe_block_to_sound->ubuf_mgr, samples); if (unlikely(ubuf_block_to_sound == NULL)) { uref_free(uref); upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return; } /* map sound ubuf for writing */ int32_t *w; if (unlikely(!ubase_check(ubuf_sound_write_int32_t(ubuf_block_to_sound, 0, -1, &w, upipe_block_to_sound->planes)))) { upipe_err(upipe, "could not write uref, dropping samples"); ubuf_sound_unmap(ubuf_block_to_sound, 0, -1, upipe_block_to_sound->planes); ubuf_free(ubuf_block_to_sound); uref_free(uref); upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return; } /* map block ubuf for reading */ const uint8_t *r; int end = -1; if (unlikely(!ubase_check(uref_block_read(uref, 0, &end, &r)))) { upipe_err(upipe, "could not read uref, dropping samples"); ubuf_sound_unmap(ubuf_block_to_sound, 0, -1, upipe_block_to_sound->planes); ubuf_block_unmap(uref->ubuf, 0); ubuf_free(ubuf_block_to_sound); uref_free(uref); upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); return; } /* copy block to sound */ memcpy(w, r, block_size); /* unmap ubufs */ ubuf_sound_unmap(ubuf_block_to_sound, 0, -1, upipe_block_to_sound->planes); ubuf_block_unmap(uref->ubuf, 0); /* attach sound ubuf to uref */ uref_attach_ubuf(uref, ubuf_block_to_sound); /* output pipe */ upipe_block_to_sound_output(upipe, uref, upump_p); }
/** @internal @This parses _uri and opens IPv4 & IPv6 sockets * * @param upipe description structure of the pipe * @param _uri socket URI * @param ttl packets time-to-live * @param bind_port bind port * @param connect_port connect port * @param weight weight (UNUSED) * @param use_tcp Set this to open a tcp socket (instead of udp) * @param use_raw open RAW socket (udp) * @param raw_header user-provided buffer for RAW header (ip+udp) * @return socket fd, or -1 in case of error */ int upipe_udp_open_socket(struct upipe *upipe, const char *_uri, int ttl, uint16_t bind_port, uint16_t connect_port, unsigned int *weight, bool *use_tcp, bool *use_raw, uint8_t *raw_header) { union sockaddru bind_addr, connect_addr; int fd, i; char *uri = strdup(_uri); char *token = uri; char *token2 = NULL; int bind_if_index = 0, connect_if_index = 0; in_addr_t if_addr = INADDR_ANY; in_addr_t src_addr = INADDR_ANY; uint16_t src_port = 4242; int tos = 0; bool b_tcp; bool b_raw; int family; socklen_t sockaddr_len; #if !defined(__APPLE__) && !defined(__native_client__) char *ifname = NULL; #endif if (!uri) return -1; memset(&bind_addr, 0, sizeof(union sockaddru)); memset(&connect_addr, 0, sizeof(union sockaddru)); bind_addr.ss.ss_family = AF_UNSPEC; connect_addr.ss.ss_family = AF_UNSPEC; if (use_tcp == NULL) { use_tcp = &b_tcp; } *use_tcp = false; if (use_raw == NULL) { use_raw = &b_raw; } *use_raw = false; token2 = strrchr(uri, ','); if (token2) { *token2++ = '\0'; if (weight) { *weight = strtoul(token2, NULL, 0); } } else if (weight) { *weight = 1; } token2 = strchr(uri, '/'); if (token2) { *token2 = '\0'; } if (*token == '\0') { free(uri); return -1; } /* Hosts */ if (token[0] != '@') { if (!upipe_udp_parse_node_service(upipe, token, &token, connect_port, &connect_if_index, &connect_addr.ss)) { free(uri); return -1; } /* required on some architectures */ memset(&connect_addr.sin.sin_zero, 0, sizeof(connect_addr.sin.sin_zero)); } if (token[0] == '@') { token++; if (!upipe_udp_parse_node_service(upipe, token, &token, bind_port, &bind_if_index, &bind_addr.ss)) { free(uri); return -1; } /* required on some architectures */ memset(&bind_addr.sin.sin_zero, 0, sizeof(bind_addr.sin.sin_zero)); } if (bind_addr.ss.ss_family == AF_UNSPEC && connect_addr.ss.ss_family == AF_UNSPEC) { free(uri); return -1; } upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); /* Weights and options */ if (token2) { do { *token2++ = '\0'; #define IS_OPTION(option) (!strncasecmp(token2, option, strlen(option))) #define ARG_OPTION(option) (token2 + strlen(option)) if (IS_OPTION("ifindex=")) { bind_if_index = connect_if_index = strtol(ARG_OPTION("ifindex="), NULL, 0); } else if (IS_OPTION("ifaddr=")) { char *option = config_stropt(ARG_OPTION("ifaddr=")); if_addr = inet_addr(option); free( option ); #if !defined(__APPLE__) && !defined(__native_client__) } else if ( IS_OPTION("ifname=") ) { ifname = config_stropt( ARG_OPTION("ifname=") ); if (strlen(ifname) >= IFNAMSIZ) { ifname[IFNAMSIZ-1] = '\0'; } #endif } else if (IS_OPTION("srcaddr=")) { char *option = config_stropt(ARG_OPTION("srcaddr=")); src_addr = inet_addr(option); free(option); *use_raw = true; } else if (IS_OPTION("srcport=")) { src_port = strtol(ARG_OPTION("srcport="), NULL, 0); } else if (IS_OPTION("ttl=")) { ttl = strtol(ARG_OPTION("ttl="), NULL, 0); } else if (IS_OPTION("tos=")) { tos = strtol(ARG_OPTION("tos="), NULL, 0); } else if (IS_OPTION("tcp")) { *use_tcp = true; } else { upipe_warn_va(upipe, "unrecognized option %s", token2); } #undef IS_OPTION #undef ARG_OPTION } while ((token2 = strchr(token2, '/')) != NULL); } if (unlikely(*use_tcp && *use_raw)) { upipe_warn(upipe, "RAW sockets not implemented for tcp"); free(uri); return -1; } free(uri); /* Sanity checks */ if (bind_addr.ss.ss_family != AF_UNSPEC && connect_addr.ss.ss_family != AF_UNSPEC && bind_addr.ss.ss_family != connect_addr.ss.ss_family) { upipe_err(upipe, "incompatible address types"); return -1; } if (bind_addr.ss.ss_family != AF_UNSPEC) { family = bind_addr.ss.ss_family; } else if (connect_addr.ss.ss_family != AF_UNSPEC) { family = connect_addr.ss.ss_family; } else { upipe_err(upipe, "ambiguous address declaration"); return -1; } sockaddr_len = (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); if (bind_if_index && connect_if_index && bind_if_index != connect_if_index) { upipe_err(upipe, "incompatible bind and connect interfaces"); return -1; } if (connect_if_index) bind_if_index = connect_if_index; else connect_if_index = bind_if_index; /* RAW header */ if (*use_raw && raw_header) { upipe_udp_raw_fill_headers(upipe, raw_header, src_addr, connect_addr.sin.sin_addr.s_addr, src_port, ntohs(connect_addr.sin.sin_port), ttl, tos, 0); } /* Socket configuration */ int sock_type = SOCK_DGRAM; if (*use_tcp) sock_type = SOCK_STREAM; if (*use_raw) sock_type = SOCK_RAW; int sock_proto = (*use_raw ? IPPROTO_RAW : 0); if ((fd = socket(family, sock_type, sock_proto)) < 0) { upipe_err_va(upipe, "unable to open socket (%m)"); return -1; } #if !defined(__APPLE__) && !defined(__native_client__) if (*use_raw) { int hincl = 1; if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &hincl, sizeof(hincl)) < 0) { upipe_err_va(upipe, "unable to set IP_HDRINCL"); close(fd); return -1; } } #endif i = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&i, sizeof(i)) == -1) { upipe_err_va(upipe, "unable to set socket (%m)"); close(fd); return -1; } if (family == AF_INET6) { if (bind_if_index && setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, (void *)&bind_if_index, sizeof(bind_if_index)) < 0) { upipe_err(upipe, "couldn't set interface index"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } if (bind_addr.ss.ss_family != AF_UNSPEC) { #if !defined(__APPLE__) && !defined(__native_client__) if (IN6_IS_ADDR_MULTICAST(&bind_addr.sin6.sin6_addr)) { struct ipv6_mreq imr; union sockaddru bind_addr_any = bind_addr; bind_addr_any.sin6.sin6_addr = in6addr_any; if (bind(fd, &bind_addr_any.so, sizeof(bind_addr_any)) < 0) { upipe_err(upipe, "couldn't bind"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } imr.ipv6mr_multiaddr = bind_addr.sin6.sin6_addr; imr.ipv6mr_interface = bind_if_index; /* Join Multicast group without source filter */ if (setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&imr, sizeof(struct ipv6_mreq)) < 0) { upipe_err(upipe, "couldn't join multicast group"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } else #endif goto normal_bind; } } else if (bind_addr.ss.ss_family != AF_UNSPEC) { normal_bind: if (bind(fd, &bind_addr.so, sockaddr_len) < 0) { upipe_err(upipe, "couldn't bind"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } if (!*use_tcp) { /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to * avoid packet loss caused by scheduling problems */ i = 0x80000; if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void *) &i, sizeof(i))) upipe_warn(upipe, "fail to increase receive buffer"); /* Join the multicast group if the socket is a multicast address */ if (bind_addr.ss.ss_family == AF_INET && IN_MULTICAST(ntohl(bind_addr.sin.sin_addr.s_addr))) { #ifndef __native_client__ if (connect_addr.ss.ss_family != AF_UNSPEC) { /* Source-specific multicast */ struct ip_mreq_source imr; imr.imr_multiaddr = bind_addr.sin.sin_addr; imr.imr_interface.s_addr = if_addr; imr.imr_sourceaddr = connect_addr.sin.sin_addr; if (bind_if_index) { upipe_warn(upipe, "ignoring ifindex option in SSM"); } if (setsockopt(fd, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char *)&imr, sizeof(struct ip_mreq_source)) < 0) { upipe_err_va(upipe, "couldn't join multicast group (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } else if (bind_if_index) { /* Linux-specific interface-bound multicast */ struct ip_mreqn imr; imr.imr_multiaddr = bind_addr.sin.sin_addr; imr.imr_address.s_addr = if_addr; imr.imr_ifindex = bind_if_index; if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&imr, sizeof(struct ip_mreqn)) < 0) { upipe_err_va(upipe, "couldn't join multicast group (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } else #endif { /* Regular multicast */ struct ip_mreq imr; imr.imr_multiaddr = bind_addr.sin.sin_addr; imr.imr_interface.s_addr = if_addr; if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&imr, sizeof(struct ip_mreq)) < 0) { upipe_err_va(upipe, "couldn't join multicast group (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } #ifdef SO_BINDTODEVICE if (ifname) { /* linux specific, needs root or CAP_NET_RAW */ if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname) + 1) < 0) { upipe_err_va(upipe, "couldn't bind to device %s (%m)", ifname); free(ifname); close(fd); return -1; } ubase_clean_str(&ifname); } #endif } } if (connect_addr.ss.ss_family != AF_UNSPEC) { if (connect(fd, &connect_addr.so, sockaddr_len) < 0) { upipe_err_va(upipe, "cannot connect socket (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } if (!*use_tcp) { if (ttl) { if (family == AF_INET && IN_MULTICAST(ntohl(connect_addr.sin.sin_addr.s_addr))) { if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&ttl, sizeof(ttl)) == -1) { upipe_err_va(upipe, "couldn't set TTL (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } if (family == AF_INET6 && IN6_IS_ADDR_MULTICAST(&connect_addr.sin6.sin6_addr)) { if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (void *)&ttl, sizeof(ttl)) == -1) { upipe_err_va(upipe, "couldn't set TTL (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } } if (tos) { if (setsockopt(fd, IPPROTO_IP, IP_TOS, (void *)&tos, sizeof(tos)) == -1) { upipe_err_va(upipe, "couldn't set TOS (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } } } else if (*use_tcp) { /* Open in listen mode - wait for an incoming connection */ int new_fd; if (listen(fd, 1) < 0) { upipe_err_va(upipe, "couldn't listen (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } while ((new_fd = accept(fd, NULL, NULL)) < 0) { if (errno != EINTR) { upipe_err_va(upipe, "couldn't accept (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } close(fd); return new_fd; } return fd; }
/** @internal @This handles input buffers. * * @param upipe description structure of the pipe * @param uref input buffer to handle * @param upump_p reference to the pump that generated the buffer */ static void upipe_dvbcsa_enc_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_dvbcsa_enc *upipe_dvbcsa_enc = upipe_dvbcsa_enc_from_upipe(upipe); struct upipe_dvbcsa_common *common = upipe_dvbcsa_enc_to_common(upipe_dvbcsa_enc); if (unlikely(!upipe_dvbcsa_enc->key)) /* no key set, forward */ return upipe_dvbcsa_enc_output(upipe, uref, upump_p); uint32_t ts_header_size = TS_HEADER_SIZE; uint8_t buf[TS_HEADER_SIZE]; const uint8_t *ts_header = uref_block_peek(uref, 0, sizeof (buf), buf); if (unlikely(!ts_header)) { upipe_err(upipe, "fail to read ts header"); uref_free(uref); return; } uint8_t scrambling = ts_get_scrambling(ts_header); bool has_payload = ts_has_payload(ts_header); bool has_adaptation = ts_has_adaptation(ts_header); uint16_t pid = ts_get_pid(ts_header); uref_block_peek_unmap(uref, 0, buf, ts_header); if (!has_payload || scrambling || !upipe_dvbcsa_common_check_pid(common, pid)) return upipe_dvbcsa_enc_output(upipe, uref, upump_p); if (unlikely(has_adaptation)) { uint8_t af_length; int ret = uref_block_extract(uref, ts_header_size, 1, &af_length); if (unlikely(!ubase_check(ret))) { upipe_err(upipe, "fail to extract adaptation field length"); uref_free(uref); return; } if (unlikely(af_length >= 183)) { upipe_warn(upipe, "invalid adaptation field received"); uref_free(uref); return; } ts_header_size += af_length + 1; } struct ubuf *ubuf = ubuf_block_copy(uref->ubuf->mgr, uref->ubuf, 0, -1); if (unlikely(!ubuf)) { upipe_err(upipe, "fail to allocate buffer"); uref_free(uref); return; } uref_attach_ubuf(uref, ubuf); int size = -1; uint8_t *ts; int ret = ubuf_block_write(ubuf, 0, &size, &ts); if (unlikely(!ubase_check(ret))) { upipe_err(upipe, "fail to write buffer"); uref_free(uref); return; } ts_set_scrambling(ts, 0x2); dvbcsa_encrypt(upipe_dvbcsa_enc->key, ts + ts_header_size, size - ts_header_size); return upipe_dvbcsa_enc_output(upipe, uref, upump_p); }
/** @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 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 configures a new codec context * * @param upipe description structure of the pipe * @param codec_def codec defintion string * @param extradata pointer to extradata buffer * @param extradata_size extradata size * @return false if the buffer couldn't be accepted */ static bool _upipe_avcdec_set_codec(struct upipe *upipe, const char *codec_def, uint8_t *extradata, int extradata_size) { AVCodec *codec = NULL; int codec_id = 0; struct upipe_avcdec *upipe_avcdec = upipe_avcdec_from_upipe(upipe); struct upipe_avcodec_open_params *params = &upipe_avcdec->open_params; uint8_t *extradata_padded = NULL; /* find codec */ if (codec_def) { codec_id = upipe_av_from_flow_def(codec_def); if (unlikely(!codec_id)) { upipe_warn_va(upipe, "codec %s not found", codec_def); } codec = avcodec_find_decoder(codec_id); if (unlikely(!codec)) { upipe_warn_va(upipe, "codec %s (%d) not found", codec_def, codec_id); } } /* copy extradata if present */ if (extradata && extradata_size > 0) { extradata_padded = upipe_avcdec_copy_extradata(upipe, extradata, extradata_size); if (unlikely(!extradata_padded)) { extradata_size = 0; } } /* use udeal/upump callback if available */ if (upipe_avcdec->upump_mgr) { upipe_dbg(upipe, "upump_mgr present, using udeal"); if (unlikely(upipe_avcdec->upump_av_deal)) { upipe_dbg(upipe, "previous upump_av_deal still running, cleaning first"); upipe_avcdec_abort_av_deal(upipe); } else { upipe_use(upipe); } struct upump *upump_av_deal = upipe_av_deal_upump_alloc(upipe_avcdec->upump_mgr, upipe_avcdec_open_codec_cb, upipe); if (unlikely(!upump_av_deal)) { upipe_err(upipe, "can't create dealer"); upipe_throw_upump_error(upipe); return false; } upipe_avcdec->upump_av_deal = upump_av_deal; memset(params, 0, sizeof(struct upipe_avcodec_open_params)); params->codec = codec; params->extradata = extradata_padded; params->extradata_size = extradata_size; /* fire */ upipe_av_deal_start(upump_av_deal); return true; } else { upipe_dbg(upipe, "no upump_mgr present, direct call to avcdec_open"); upipe_use(upipe); return upipe_avcdec_open_codec(upipe, codec, extradata_padded, extradata_size); } }
/** @internal @This 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; }