/** @This frees a upipe.
 *
 * @param upipe description structure of the pipe
 */
static void upipe_avcdec_free(struct upipe *upipe)
{
    struct upipe_avcdec *upipe_avcdec = upipe_avcdec_from_upipe(upipe);
    if (upipe_avcdec->context) {
        _upipe_avcdec_set_codec(upipe, NULL, NULL, 0);
        return; /* _set_codec() calls _use()/_release() */
    }

    upipe_throw_dead(upipe);

    if (upipe_avcdec->frame) {
        av_free(upipe_avcdec->frame);
    }

    if (upipe_avcdec->saved_uref) {
        uref_free(upipe_avcdec->saved_uref);
    }
    if (upipe_avcdec->saved_upump_mgr) {
        upump_mgr_sink_unblock(upipe_avcdec->saved_upump_mgr);
        upump_mgr_release(upipe_avcdec->saved_upump_mgr);
    }

    upipe_avcdec_abort_av_deal(upipe);
    upipe_avcdec_clean_output(upipe);
    upipe_avcdec_clean_ubuf_mgr(upipe);
    upipe_avcdec_clean_uref_mgr(upipe);
    upipe_avcdec_clean_upump_mgr(upipe);

    upipe_clean(upipe);
    free(upipe_avcdec);
}
/** @internal @This is called by AudioQueue after reading a buffer
 * @param _upipe description structure of the pipe (void)
 * @param queue AudioQueue
 * @param qbuf AudioQueue buffer
 */
static void upipe_osx_audioqueue_sink_cb(void *_upipe, AudioQueueRef queue,
        struct AudioQueueBuffer *qbuf)
{
    /* TODO uncblock ? */
#if 0
    struct upump_mgr *upump_mgr = qbuf->mUserData;
    upump_mgr_sink_unblock(upump_mgr);
    upump_mgr_release(upump_mgr);
#endif
    AudioQueueFreeBuffer(queue, qbuf);
}
/** @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;
    }
}