/** @internal @This prints socket characteristics for debug purposes * * @param upipe description structure of the pipe * @param text descriptive text * @param bind bind sockaddr union * @param connect connect sockaddr union */ static void upipe_udp_print_socket(struct upipe *upipe, const char *text, union sockaddru *bind, union sockaddru *connect) { if (bind->ss.ss_family == AF_INET) { upipe_dbg_va(upipe, "%s bind:%s:%u", text, inet_ntoa(bind->sin.sin_addr), ntohs(bind->sin.sin_port)); } else if (bind->ss.ss_family == AF_INET6) { char buf[INET6_ADDRSTRLEN]; upipe_dbg_va(upipe, "%s bind:[%s]:%u", text, inet_ntop(AF_INET6, &bind->sin6.sin6_addr, buf, sizeof(buf)), ntohs(bind->sin6.sin6_port)); } if (connect->ss.ss_family == AF_INET) { upipe_dbg_va(upipe, "%s connect:%s:%u", text, inet_ntoa(connect->sin.sin_addr), ntohs(connect->sin.sin_port)); } else if (connect->ss.ss_family == AF_INET6) { char buf[INET6_ADDRSTRLEN]; upipe_dbg_va(upipe, "%s connect:[%s]:%u", text, inet_ntop(AF_INET6, &connect->sin6.sin6_addr, buf, sizeof(buf)), ntohs(connect->sin6.sin6_port)); } }
/** @internal @This handles urefs (data & flows). * * @param upipe description structure of the pipe * @param uref uref structure * @param upump pump that generated the buffer */ static void upipe_multicat_probe_input(struct upipe *upipe, struct uref *uref, struct upump *upump) { struct upipe_multicat_probe *upipe_multicat_probe = upipe_multicat_probe_from_upipe(upipe); const char *def; if (unlikely(uref_flow_get_def(uref, &def))) { upipe_dbg_va(upipe, "flow definition %s", def); upipe_multicat_probe_store_flow_def(upipe, uref); return; } if (unlikely(uref_flow_get_end(uref))) { uref_free(uref); upipe_throw_need_input(upipe); return; } if (unlikely(upipe_multicat_probe->flow_def == NULL)) { upipe_throw_flow_def_error(upipe, uref); uref_free(uref); return; } return _upipe_multicat_probe_input(upipe, uref, upump); }
/** @internal @This frees all resources allocated. * * @param upipe description structure of the pipe */ static void upipe_skip_free(struct upipe *upipe) { struct upipe_skip *upipe_skip = upipe_skip_from_upipe(upipe); upipe_dbg_va(upipe, "releasing pipe %p", upipe); upipe_throw_dead(upipe); upipe_skip_clean_output(upipe); upipe_clean(upipe); free(upipe_skip); }
/** @internal @This frees all resources allocated. * * @param upipe description structure of the pipe */ static void upipe_rtpd_free(struct upipe *upipe) { struct upipe_rtpd *upipe_rtpd = upipe_rtpd_from_upipe(upipe); upipe_dbg_va(upipe, "releasing pipe %p", upipe); upipe_throw_dead(upipe); uref_free(upipe_rtpd->flow_def_input); upipe_rtpd_clean_output(upipe); upipe_rtpd_clean_urefcount(upipe); upipe_rtpd_free_void(upipe); }
/** @internal @This checks and parses a line of a m3u file. * * @param upipe description structure of the pipe * @param flow_def the current flow definition * @param uref pointer to uref carrying the line to parse * @return an error code */ static int upipe_m3u_reader_process_line(struct upipe *upipe, struct uref *flow_def, struct uref *uref) { static const struct { const char *pfx; int (*cb)(struct upipe *, struct uref *, const char *); } ext_cb[] = { { "#EXTM3U", upipe_m3u_reader_process_m3u }, { "#EXT-X-VERSION:", upipe_m3u_reader_process_version }, { "#EXT-X-TARGETDURATION:", upipe_m3u_reader_process_target_duration }, { "#EXT-X-PLAYLIST-TYPE:", upipe_m3u_reader_process_playlist_type }, { "#EXTINF:", upipe_m3u_reader_process_extinf }, { "#EXT-X-BYTERANGE:", upipe_m3u_reader_process_byte_range }, { "#EXT-X-MEDIA:", upipe_m3u_reader_process_media }, { "#EXT-X-STREAM-INF:", upipe_m3u_reader_ext_x_stream_inf }, { "#EXT-X-MEDIA-SEQUENCE:", upipe_m3u_reader_ext_x_media_sequence }, { "#EXT-X-ENDLIST", upipe_m3u_reader_ext_x_endlist }, { "#EXT-X-KEY:", upipe_m3u_reader_key }, }; size_t block_size; UBASE_RETURN(uref_block_size(uref, &block_size)); uint8_t buffer[block_size + 1]; memset(buffer, 0, sizeof (buffer)); UBASE_RETURN(uref_block_extract(uref, 0, block_size, buffer)); char *line = (char *)buffer; /* remove end of line */ if (strlen(line) && line[strlen(line) - 1] == '\n') { line[strlen(line) - 1] = '\0'; if (strlen(line) && line[strlen(line) - 1] == '\r') line[strlen(line) - 1] = '\0'; } if (!strlen(line)) return UBASE_ERR_NONE; if (*line == '#') { for (unsigned i = 0; i < UBASE_ARRAY_SIZE(ext_cb); i++) { if (strncmp(line, ext_cb[i].pfx, strlen(ext_cb[i].pfx))) continue; return ext_cb[i].cb(upipe, flow_def, line + strlen(ext_cb[i].pfx)); } upipe_dbg_va(upipe, "ignore `%s'", line); return UBASE_ERR_NONE; } return upipe_m3u_reader_process_uri(upipe, flow_def, line); }
/** @hidden */ static int upipe_burst_throw_update(struct upipe *upipe, bool empty) { struct upipe_burst *upipe_burst = upipe_burst_from_upipe(upipe); if (unlikely(upipe_burst->empty == empty)) return UBASE_ERR_NONE; upipe_burst->empty = empty; upipe_dbg_va(upipe, "throw update %s", empty ? "empty" : "not empty"); return upipe_throw(upipe, UPROBE_BURST_UPDATE, UPIPE_BURST_SIGNATURE, empty); }
static void keyhandler(struct upipe *upipe, unsigned long key) { switch (key) { case 27: case 'q': { upipe_notice_va(upipe, "exit key pressed (%d), exiting", key); exit(0); } default: upipe_dbg_va(upipe, "key pressed (%d)", key); break; } }
/** @internal @This catches events of the glx pipe. * * @param uprobe pointer to probe * @param upipe pointer to pipe throwing the event * @param event event thrown * @param args optional event-specific parameters * @return an error code */ static int upipe_glxplayer_catch_glx(struct uprobe *uprobe, struct upipe *upipe, int event, va_list args) { switch (event) { case UPROBE_GLX_SINK_KEYPRESS: { struct upipe_glxplayer *glxplayer = container_of(uprobe, struct upipe_glxplayer, uprobe_glx_s); unsigned int signature = va_arg(args, unsigned int); assert(signature == UPIPE_GLX_SINK_SIGNATURE); unsigned long key = va_arg(args, unsigned long); switch (key) { case 27: case 'q': { upipe_notice_va(upipe, "exit key pressed (%d), exiting", key); upipe_release(glxplayer->upipe_src_xfer); upipe_mgr_release(glxplayer->src_xfer); break; } case ' ': { if (glxplayer->trickp) { if ( (glxplayer->paused = !glxplayer->paused) ) { upipe_notice(upipe, "Playback paused"); struct urational rate = { .num = 0, .den = 0 }; upipe_trickp_set_rate(glxplayer->upipe_trickp, rate); } else { upipe_notice(upipe, "Playback resumed"); struct urational rate = { .num = 1, .den = 1 }; upipe_trickp_set_rate(glxplayer->upipe_trickp, rate); } } break; } default: upipe_dbg_va(upipe, "key pressed (%d)", key); break; } return UBASE_ERR_NONE; } case UPROBE_GLX_SINK_KEYRELEASE: return UBASE_ERR_NONE; default: break; } return uprobe_throw_next(uprobe, upipe, event, args); }
/** @internal @This checks and parses a "#EXT-X-VERSION" 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_version(struct upipe *upipe, struct uref *flow_def, const char *line) { UBASE_RETURN(uref_flow_match_def(flow_def, M3U_FLOW_DEF)); char *endptr; unsigned long int version = strtoul(line, &endptr, 10); if (line == endptr || *endptr != '\0' || version > UINT8_MAX) { upipe_warn_va(upipe, "invalid version %s", line); return UBASE_ERR_INVALID; } upipe_dbg_va(upipe, "version: %u", version); return uref_m3u_flow_set_version(flow_def, version); }
/** @internal @This checks and parses a "#EXT-X-MEDIA-SEQUENCE" 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_media_sequence(struct upipe *upipe, struct uref *flow_def, const char *line) { const char *def; 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)); char *endptr; uint64_t media_sequence = strtoull(line, &endptr, 10); if (endptr == line || strlen(endptr)) { upipe_warn_va(upipe, "invalid media sequence %s", line); return UBASE_ERR_INVALID; } upipe_dbg_va(upipe, "media sequence %"PRIu64, media_sequence); return uref_m3u_playlist_flow_set_media_sequence(flow_def, media_sequence); }
/** @internal @This handles input uref. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump upump structure */ static void upipe_avcdec_input(struct upipe *upipe, struct uref *uref, struct upump *upump) { struct upipe_avcdec *upipe_avcdec = upipe_avcdec_from_upipe(upipe); if (upipe_avcdec->uref_mgr == NULL) { upipe_throw_need_uref_mgr(upipe); if (unlikely(upipe_avcdec->uref_mgr == NULL)) { uref_free(uref); return; } } const char *def = NULL; if (unlikely(uref_flow_get_def(uref, &def))) { if (unlikely(ubase_ncmp(def, EXPECTED_FLOW))) { upipe_throw_flow_def_error(upipe, uref); uref_free(uref); _upipe_avcdec_set_codec(upipe, NULL, NULL, 0); return; } upipe_dbg_va(upipe, "flow definition %s", def); def += strlen(EXPECTED_FLOW); _upipe_avcdec_set_codec(upipe, def, NULL, 0); uref_free(uref); return; } if (unlikely(uref_flow_get_end(uref))) { uref_free(uref); upipe_throw_need_input(upipe); return; } if (unlikely(!uref->ubuf)) { upipe_warn(upipe, "uref has no ubuf, dropping"); uref_free(uref); return; } upipe_avcdec_input_packet(upipe, uref, upump); }
/** @internal @This checks and parses a "#EXT-X-TARGETDURATION" 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_target_duration(struct upipe *upipe, struct uref *flow_def, const char *line) { const char *def; 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)); const char *endptr; uint64_t duration; UBASE_RETURN(duration_to_uclock(line, &endptr, &duration)); if (line == endptr || strlen(endptr)) { upipe_warn_va(upipe, "invalid target duration %s", line); return UBASE_ERR_INVALID; } upipe_dbg_va(upipe, "target duration: %"PRIu64, duration); return uref_m3u_playlist_flow_set_target_duration(flow_def, duration); }
/** helper phony pipe */ static void test_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct x264_test *x264_test = x264_test_from_upipe(upipe); uint64_t pts = 0, dts = 0; if (uref->udict != NULL) { udict_dump(uref->udict, upipe->uprobe); } if (!ubase_check(uref_clock_get_pts_prog(uref, &pts))) { upipe_warn(upipe, "received packet with no pts"); } if (!ubase_check(uref_clock_get_dts_prog(uref, &dts))) { upipe_warn(upipe, "received packet with no dts"); } upipe_dbg_va(upipe, "received pic %d, pts: %"PRIu64" , dts: %"PRIu64, x264_test->counter, pts, dts); x264_test->counter++; uref_free(uref); }
/** @This sets the low resolution parameter, if supported by codec. * If some codec is already used, it is re-opened. * * @param upipe description structure of the pipe * @param lowres lowres parameter (0=disabled) * @return false in case of error */ static bool _upipe_avcdec_set_lowres(struct upipe *upipe, int lowres) { struct upipe_avcdec *upipe_avcdec = upipe_avcdec_from_upipe(upipe); const char *codec_def; bool ret = true; if (lowres < 0) { upipe_warn_va(upipe, "Invalid lowres parameter (%d)", lowres); return false; } upipe_avcdec->lowres = lowres; upipe_dbg_va(upipe, "Requesting lowres %d", lowres); if (upipe_avcdec->context && upipe_avcdec->context->codec) { codec_def = upipe_av_to_flow_def(upipe_avcdec->context->codec->id); ret = _upipe_avcdec_set_codec(upipe, codec_def, upipe_avcdec->context->extradata, upipe_avcdec->context->extradata_size); } return ret; }
/** @internal @This copies extradata * * @param upipe description structure of the pipe * @param extradata pointer to extradata buffer * @param size extradata size * @return false if the buffer couldn't be accepted */ static uint8_t *upipe_avcdec_copy_extradata(struct upipe *upipe, const uint8_t *extradata, int size) { uint8_t *buf; if (!extradata || size <= 0) { return NULL; } buf = malloc(size + FF_INPUT_BUFFER_PADDING_SIZE); if (!buf) { upipe_throw_aerror(upipe); return NULL; } memset(buf+size, 0, FF_INPUT_BUFFER_PADDING_SIZE); memcpy(buf, extradata, size); upipe_dbg_va(upipe, "Received extradata (%d bytes)", size); return buf; }
/** @internal @This handles input. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump pump that generated the buffer */ static void upipe_skip_input(struct upipe *upipe, struct uref *uref, struct upump *upump) { struct upipe_skip *upipe_skip = upipe_skip_from_upipe(upipe); const char *def; if (unlikely(uref_flow_get_def(uref, &def))) { if (unlikely(ubase_ncmp(def, EXPECTED_FLOW))) { upipe_throw_flow_def_error(upipe, uref); uref_free(uref); return; } upipe_dbg_va(upipe, "flow definition %s", def); if (unlikely(!uref_flow_set_def(uref, "block."))) upipe_throw_aerror(upipe); upipe_skip_store_flow_def(upipe, uref); return; } if (unlikely(uref_flow_get_end(uref))) { uref_free(uref); upipe_throw_need_input(upipe); return; } if (unlikely(upipe_skip->flow_def == NULL)) { upipe_throw_flow_def_error(upipe, uref); uref_free(uref); return; } if (unlikely(uref->ubuf == NULL)) { uref_free(uref); return; } upipe_skip_input_block(upipe, uref, upump); }
/** @internal @This checks and parses a "#EXT-X-PLAYLIST-TYPE" 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_playlist_type(struct upipe *upipe, struct uref *flow_def, const char *line) { const char *def; 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)); static const char *types[] = { "VOD", "EVENT" }; const char *type = NULL; for (unsigned i = 0; !type && i < UBASE_ARRAY_SIZE(types); i++) if (!strcmp(line, types[i])) type = types[i]; if (unlikely(type == NULL)) { upipe_warn_va(upipe, "invalid playlist type `%s'", line); return UBASE_ERR_INVALID; } upipe_dbg_va(upipe, "playlist type %s", type); return uref_m3u_playlist_flow_set_type(flow_def, type); }
/** @internal @This is called by avcodec when releasing a frame * @param context current avcodec context * @param frame avframe handler released by avcodec black magic box */ static void upipe_avcdec_release_buffer(struct AVCodecContext *context, AVFrame *frame) { struct upipe *upipe = context->opaque; struct uref *uref = frame->opaque; const struct upipe_av_plane *planes = NULL; int i; uint64_t framenum = 0; uref_pic_get_number(uref, &framenum); upipe_dbg_va(upipe, "Releasing frame %u (%p)", (uint64_t) framenum, uref); if (likely(uref->ubuf)) { planes = upipe_avcdec_from_upipe(upipe)->pixfmt->planes; for (i=0; i < 4 && planes[i].chroma; i++) { ubuf_pic_plane_unmap(uref->ubuf, planes[i].chroma, 0, 0, -1, -1); frame->data[i] = NULL; } } else { avcodec_default_release_buffer(context, frame); } uref_free(uref); }
/** @internal @This is called by avcodec when allocating a new audio buffer * Used with audio decoders. * @param context current avcodec context * @param frame avframe handler entering avcodec black magic box */ static int upipe_avcdec_get_buffer_audio(struct AVCodecContext *context, AVFrame *frame) { struct upipe *upipe = context->opaque; struct upipe_avcdec *upipe_avcdec = upipe_avcdec_from_upipe(upipe); struct ubuf *ubuf_samples; uint8_t *buf; int size; frame->opaque = uref_dup(upipe_avcdec->uref); /* direct rendering - allocate ubuf for audio */ if (upipe_avcdec->context->codec->capabilities & CODEC_CAP_DR1) { ubuf_samples = ubuf_block_alloc(upipe_avcdec->ubuf_mgr, av_samples_get_buffer_size(NULL, context->channels, frame->nb_samples, context->sample_fmt, 1)); if (likely(ubuf_samples)) { ubuf_block_write(ubuf_samples, 0, &size, &buf); uref_attach_ubuf(frame->opaque, ubuf_samples); av_samples_fill_arrays(frame->data, frame->linesize, buf, context->channels, frame->nb_samples, context->sample_fmt, 1); frame->extended_data = frame->data; frame->type = FF_BUFFER_TYPE_USER; return 1; /* success */ } else { upipe_dbg_va(upipe, "ubuf allocation failed, fallback"); } } /* default : DR failed or not available */ return avcodec_default_get_buffer(context, frame); }
/** @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 handles packets. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump upump structure */ static void upipe_avcdec_input_packet(struct upipe *upipe, struct uref *uref, struct upump *upump) { uint8_t *inbuf; size_t insize = 0; struct upipe_avcdec *upipe_avcdec = upipe_avcdec_from_upipe(upipe); assert(upipe); assert(uref); if (upipe_avcdec->upump_av_deal) { /* pending open_codec callback */ upipe_dbg(upipe, "Received packet while open_codec pending"); if (upump) { upump_mgr_sink_block(upump->mgr); upump_mgr_use(upump->mgr); upipe_avcdec->saved_upump_mgr = upump->mgr; } if (upipe_avcdec->saved_uref) { upipe_warn(upipe, "Dropping previously saved packet !"); uref_free(upipe_avcdec->saved_uref); } upipe_avcdec->saved_uref = uref; return; } else if (upipe_avcdec->saved_uref) { upipe_dbg(upipe, "Processing previously saved packet"); struct uref *prev_uref = upipe_avcdec->saved_uref; upipe_avcdec->saved_uref = NULL; /* Not a typo, using the current upump here */ upipe_avcdec_input_packet(upipe, prev_uref, upump); } if (!upipe_avcdec->context) { uref_free(uref); upipe_warn(upipe, "Received packet but decoder is not initialized"); return; } /* avcodec input buffer needs to be at least 4-byte aligned and FF_INPUT_BUFFER_PADDING_SIZE larger than actual input size. Thus, extract ubuf content in a properly allocated buffer. Padding must be zeroed. */ uref_block_size(uref, &insize); if (unlikely(!insize)) { upipe_warn(upipe, "Received packet with size 0, dropping"); uref_free(uref); return; } upipe_dbg_va(upipe, "Received packet %u - size : %u", upipe_avcdec->counter, insize); inbuf = malloc(insize + FF_INPUT_BUFFER_PADDING_SIZE); if (unlikely(!inbuf)) { upipe_throw_aerror(upipe); return; } memset(inbuf, 0, insize + FF_INPUT_BUFFER_PADDING_SIZE); uref_block_extract(uref, 0, insize, inbuf); ubuf_free(uref_detach_ubuf(uref)); uref_pic_set_number(uref, upipe_avcdec->counter); /* Track current uref in pipe structure - required for buffer allocation * in upipe_avcdec_get_buffer */ upipe_avcdec->uref = uref; upipe_avcdec_process_buf(upipe, inbuf, insize, upump); free(inbuf); uref_free(uref); upipe_avcdec->counter++; }
/** @internal @This handles buffers once stripped from uref. * * @param upipe description structure of the pipe * @param buf buffer containing packet * @param size buffer size before padding * @param upump upump structure */ static bool upipe_avcdec_process_buf(struct upipe *upipe, uint8_t *buf, size_t size, struct upump *upump) { int gotframe = 0, len; AVPacket avpkt; AVFrame *frame; uint64_t framenum = 0; struct upipe_avcdec *upipe_avcdec = upipe_avcdec_from_upipe(upipe); assert(upipe); /* init avcodec packed and attach input buffer */ av_init_packet(&avpkt); avpkt.size = size; avpkt.data = buf; frame = upipe_avcdec->frame; switch (upipe_avcdec->context->codec->type) { case AVMEDIA_TYPE_VIDEO: { len = avcodec_decode_video2(upipe_avcdec->context, frame, &gotframe, &avpkt); if (len < 0) { upipe_warn(upipe, "Error while decoding frame"); } /* output frame if any has been decoded */ if (gotframe) { uref_pic_get_number(frame->opaque, &framenum); upipe_dbg_va(upipe, "%u\t - Picture decoded ! %dx%d - %u", upipe_avcdec->counter, frame->width, frame->height, (uint64_t) framenum); upipe_avcdec_output_frame(upipe, frame, upump); return true; } else { return false; } } case AVMEDIA_TYPE_AUDIO: { len = avcodec_decode_audio4(upipe_avcdec->context, frame, &gotframe, &avpkt); if (len < 0) { upipe_warn(upipe, "Error while decoding frame"); } /* output samples if any has been decoded */ if (gotframe) { upipe_avcdec_output_audio(upipe, frame, upump); return true; } else { return false; } } default: { /* should never be here */ upipe_err_va(upipe, "Unsupported media type (%d)", upipe_avcdec->context->codec->type); return false; } } }
/** @internal @This 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 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 catches events of the video output of the demux. * * @param uprobe pointer to probe * @param upipe pointer to pipe throwing the event * @param event event thrown * @param args optional event-specific parameters * @return an error code */ static int upipe_glxplayer_catch_demux_output(struct uprobe *uprobe, struct upipe *upipe, int event, va_list args) { struct upipe_glxplayer *glxplayer = container_of(uprobe, struct upipe_glxplayer, uprobe_demux_output_s); switch (event) { case UPROBE_NEED_OUTPUT: { struct uref *flow_def = va_arg(args, struct uref *); const char *def = "(none)"; if (!ubase_check(uref_flow_get_def(flow_def, &def)) || ubase_ncmp(def, "block.")) { upipe_warn_va(upipe, "flow def %s is not supported", def); return UBASE_ERR_UNHANDLED; } upipe_dbg_va(upipe, "add flow %s", def); /* prepare a queue to deport avcodec to a new thread */ uprobe_throw(glxplayer->uprobe_logger, NULL, UPROBE_FREEZE_UPUMP_MGR); struct upipe *upipe_dec_qsrc = upipe_qsrc_alloc(glxplayer->upipe_qsrc_mgr, uprobe_pfx_alloc_va(uprobe_use(&glxplayer->uprobe_dec_qsrc_s), glxplayer->loglevel, "dec qsrc"), DEC_QUEUE_LENGTH); if (unlikely(upipe_dec_qsrc == NULL)) { return UBASE_ERR_ALLOC; } uprobe_throw(glxplayer->uprobe_logger, NULL, UPROBE_THAW_UPUMP_MGR); glxplayer->upipe_dec_qsink = upipe_qsink_alloc(glxplayer->upipe_qsink_mgr, uprobe_pfx_alloc_va( uprobe_use(glxplayer->uprobe_logger), glxplayer->loglevel, "dec qsink"), upipe_dec_qsrc); if (unlikely(glxplayer->upipe_dec_qsink == NULL)) { upipe_release(upipe_dec_qsrc); return UBASE_ERR_ALLOC; } upipe_set_output(upipe, glxplayer->upipe_dec_qsink); /* prepare to transfer the queue source */ glxplayer->dec_xfer = upipe_xfer_mgr_alloc(XFER_QUEUE, XFER_POOL); if (unlikely(glxplayer->dec_xfer == NULL)) { upipe_release(upipe_dec_qsrc); return UBASE_ERR_ALLOC; } /* spawn a thread for the decoder */ if (pthread_create(&glxplayer->dec_thread_id, NULL, upipe_glxplayer_dec_thread, glxplayer)) { upipe_mgr_release(glxplayer->dec_xfer); upipe_release(upipe_dec_qsrc); return UBASE_ERR_ALLOC; } glxplayer->upipe_dec_qsrc_handle = upipe_xfer_alloc(glxplayer->dec_xfer, uprobe_pfx_alloc(uprobe_use(glxplayer->uprobe_logger), glxplayer->loglevel, "dec qsrc xfer"), upipe_dec_qsrc); if (unlikely(glxplayer->upipe_dec_qsrc_handle == NULL)) { upipe_mgr_release(glxplayer->dec_xfer); upipe_release(upipe_dec_qsrc); return UBASE_ERR_ALLOC; } upipe_attach_upump_mgr(glxplayer->upipe_dec_qsrc_handle); upipe_set_output(glxplayer->upipe_dec_qsink, glxplayer->upipe_dec_qsrc_handle); return UBASE_ERR_NONE; } case UPROBE_SOURCE_END: { upipe_flush(glxplayer->upipe_dec_qsink); upipe_release(glxplayer->upipe_dec_qsink); glxplayer->upipe_dec_qsink = NULL; /* set dec_qsrc output to null */ struct upipe *null = upipe_void_alloc(glxplayer->upipe_null_mgr, uprobe_pfx_alloc(uprobe_use(glxplayer->uprobe_logger), glxplayer->loglevel, "dec qsrc null")); if (likely(null != NULL)) { upipe_set_output(glxplayer->upipe_dec_qsrc_handle, null); upipe_release(null); } upipe_release(glxplayer->upipe_dec_qsrc_handle); return UBASE_ERR_NONE; } default: return uprobe_throw_next(uprobe, upipe, event, args); } }
/** @internal @This is called back to fill NaCl audio buffer. * Please note that this function runs in a different thread. * * @param sample_buffer buffer to fill * @param buffer_size size of the buffer in octets * @param latency how long before the audio data is to be presented * @param user_data opaque pointing to the pipe */ static void upipe_nacl_audio_worker(void *sample_buffer, uint32_t buffer_size, PP_TimeDelta latency, void *user_data) { struct upipe *upipe = (struct upipe *)user_data; struct upipe_nacl_audio *upipe_nacl_audio = upipe_nacl_audio_from_upipe(upipe); uint64_t next_pts = UINT64_MAX; if (likely(upipe_nacl_audio->uclock != NULL)) { /* This is slightly off. */ next_pts = uclock_now(upipe_nacl_audio->uclock) + (uint64_t)(latency * UCLOCK_FREQ); } uint32_t frames = buffer_size / 4; while (frames > 0) { if (upipe_nacl_audio->uref == NULL) upipe_nacl_audio->uref = uqueue_pop(&upipe_nacl_audio->uqueue, struct uref *); if (unlikely(upipe_nacl_audio->uref == NULL)) { upipe_dbg_va(upipe, "playing %u frames of silence (empty)", frames); memset(sample_buffer, 0, frames * 4); break; } struct uref *uref = upipe_nacl_audio->uref; if (next_pts != UINT64_MAX) { uint64_t uref_pts; if (unlikely(!ubase_check(uref_clock_get_pts_sys(uref, &uref_pts)))) { upipe_nacl_audio->uref = NULL; uref_free(uref); upipe_warn(upipe, "non-dated uref received"); continue; } int64_t tolerance = uref_pts - next_pts; if (tolerance > (int64_t)PTS_TOLERANCE) { uint32_t silence_frames = tolerance * SAMPLE_RATE / UCLOCK_FREQ; if (silence_frames > frames) silence_frames = frames; upipe_dbg_va(upipe, "playing %u frames of silence (wait)", silence_frames); memset(sample_buffer, 0, silence_frames * 4); sample_buffer += silence_frames * 4; frames -= silence_frames; continue; } else if (-tolerance > (int64_t)PTS_TOLERANCE) { uint32_t dropped_frames = (-tolerance) * SAMPLE_RATE / UCLOCK_FREQ; upipe_warn_va(upipe, "late buffer received, dropping %u frames", dropped_frames); upipe_nacl_audio_consume(upipe, dropped_frames); continue; } } size_t size; const void *uref_buffer; if (unlikely(!ubase_check(uref_sound_size(uref, &size, NULL)) || !ubase_check(uref_sound_plane_read_void(uref, "lr", 0, -1, &uref_buffer)))) { upipe_nacl_audio->uref = NULL; uref_free(uref); upipe_warn(upipe, "cannot read ubuf buffer"); continue; } uint32_t copied_frames = size < frames ? size : frames; memcpy(sample_buffer, uref_buffer, copied_frames * 4); uref_sound_plane_unmap(uref, "lr", 0, -1); sample_buffer += copied_frames * 4; upipe_nacl_audio_consume(upipe, copied_frames); frames -= copied_frames; } }