/** @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; } }
/** @internal @This handles input buffer of the blank source pipe. This buffer * is supposed to be a picture or a sound to set as source buffer of the blank * pipes (audio/video blank pipe generator). * * @param upipe description structure of the pipe * @param uref uref reference picture or sound * @param upump_p is ignored */ static void upipe_blksrc_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_blksrc *upipe_blksrc = upipe_blksrc_from_upipe(upipe); if (unlikely(!upipe_blksrc->flow_def)) { upipe_warn(upipe, "flow def is not set"); uref_free(uref); } else if (ubase_check(uref_flow_match_def(upipe_blksrc->flow_def, UREF_PIC_FLOW_DEF))) { upipe_dbg(upipe, "set picture buffer"); upipe_vblk_set_pic(upipe_blksrc->blk, uref); } else if (ubase_check(uref_flow_match_def(upipe_blksrc->flow_def, UREF_SOUND_FLOW_DEF))) upipe_ablk_set_sound(upipe_blksrc->blk, uref); else { upipe_warn(upipe, "unsupported flow definition"); uref_free(uref); } }
/** @internal @This 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 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 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 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; }