TRACE_WRAPPER
void
trace_ppb_core_call_on_main_thread(int32_t delay_in_milliseconds,
                                   struct PP_CompletionCallback callback, int32_t result)
{
    trace_info("[PPB] {full} %s delay_in_milliseconds=%d, callback={.func=%p, .user_data=%p, "
               ".flags=%d}, result=%d\n", __func__+6, delay_in_milliseconds, callback.func,
               callback.user_data, callback.flags, result);
    ppb_core_call_on_main_thread(delay_in_milliseconds, callback, result);
}
int32_t
ppb_video_decoder_reset(PP_Resource video_decoder, struct PP_CompletionCallback callback)
{
    struct pp_video_decoder_s *vd = pp_resource_acquire(video_decoder, PP_RESOURCE_VIDEO_DECODER);
    if (!vd) {
        trace_error("%s, bad resource\n", __func__);
        return PP_ERROR_BADRESOURCE;
    }

    avcodec_flush_buffers(vd->avctx);

    pp_resource_release(video_decoder);
    ppb_core_call_on_main_thread(0, callback, PP_OK);
    return PP_OK_COMPLETIONPENDING;
}
int32_t
ppb_video_decoder_decode(PP_Resource video_decoder,
                         const struct PP_VideoBitstreamBuffer_Dev *bitstream_buffer,
                         struct PP_CompletionCallback callback)
{
    struct pp_video_decoder_s *vd = pp_resource_acquire(video_decoder, PP_RESOURCE_VIDEO_DECODER);
    if (!vd) {
        trace_error("%s, bad resource\n", __func__);
        return PP_ERROR_BADRESOURCE;
    }

    if (vd->failed_state) {
        trace_warning("%s, there were errors before, giving up\n", __func__);
        pp_resource_release(video_decoder);
        return PP_ERROR_FAILED;
    }

    void *rawdata = ppb_buffer_map(bitstream_buffer->data);
    if (!rawdata) {
        trace_error("%s, bad bitstream buffer\n", __func__);
        pp_resource_release(video_decoder);
        return PP_ERROR_FAILED;
    }

    uint8_t *inbuf = rawdata;
    size_t   inbuf_sz = bitstream_buffer->size;

    while (inbuf_sz > 0) {
        uint8_t *outbuf = NULL;
        int      outbuf_sz = 0;
        int len = av_parser_parse2(vd->avparser, vd->avctx, &outbuf, &outbuf_sz,
                                   inbuf, inbuf_sz, 0, 0, AV_NOPTS_VALUE);
        if (outbuf_sz > 0)
            decode_frame(vd, outbuf, outbuf_sz, vd->last_consumed_bitstream_buffer_id);
        inbuf += len;
        inbuf_sz -= len;
    }

    vd->last_consumed_bitstream_buffer_id = bitstream_buffer->id;
    ppb_buffer_unmap(bitstream_buffer->data);

    pp_resource_release(video_decoder);
    ppb_core_call_on_main_thread(0, callback, PP_OK);
    return PP_OK_COMPLETIONPENDING;
}
int32_t
ppb_url_loader_read_response_body(PP_Resource loader, void *buffer, int32_t bytes_to_read,
                                  struct PP_CompletionCallback callback)
{
    int32_t read_bytes = PP_ERROR_FAILED;
    struct pp_url_loader_s *ul = pp_resource_acquire(loader, PP_RESOURCE_URL_LOADER);
    if (!ul) {
        trace_error("%s, bad resource\n", __func__);
        return PP_ERROR_BADRESOURCE;
    }

    if (ul->fd >= 0) {
        read_bytes = -1;
        off_t ofs = lseek(ul->fd, ul->read_pos, SEEK_SET);
        if (ofs != (off_t)-1)
            read_bytes = RETRY_ON_EINTR(read(ul->fd, buffer, bytes_to_read));

        if (read_bytes < 0)
            read_bytes = PP_ERROR_FAILED;
        else
            ul->read_pos += read_bytes;
    }

    if (read_bytes == 0 && !ul->finished_loading) {
        // no data ready, schedule read task
        struct url_loader_read_task_s *rt = g_slice_alloc(sizeof(*rt));
        rt->buffer = buffer;
        rt->bytes_to_read = bytes_to_read;
        rt->ccb = callback;

        ul->read_tasks = g_list_append(ul->read_tasks, rt);
        pp_resource_release(loader);
        return PP_OK_COMPLETIONPENDING;
    }

    pp_resource_release(loader);
    if (callback.flags & PP_COMPLETIONCALLBACK_FLAG_OPTIONAL)
        return read_bytes;

    ppb_core_call_on_main_thread(0, callback, read_bytes);
    return PP_OK_COMPLETIONPENDING;
}