diff options
Diffstat (limited to 'src/include/libplacebo/utils/frame_queue.h')
-rw-r--r-- | src/include/libplacebo/utils/frame_queue.h | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/src/include/libplacebo/utils/frame_queue.h b/src/include/libplacebo/utils/frame_queue.h new file mode 100644 index 0000000..2a9c90c --- /dev/null +++ b/src/include/libplacebo/utils/frame_queue.h @@ -0,0 +1,230 @@ +/* + * This file is part of libplacebo. + * + * libplacebo is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * libplacebo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LIBPLACEBO_FRAME_QUEUE_H +#define LIBPLACEBO_FRAME_QUEUE_H + +#include <libplacebo/renderer.h> +#include <libplacebo/shaders/deinterlacing.h> + +PL_API_BEGIN + +// An abstraction layer for automatically turning a conceptual stream of +// (frame, pts) pairs, as emitted by a decoder or filter graph, into a +// `pl_frame_mix` suitable for `pl_render_image_mix`. +// +// This API ensures that minimal work is performed (e.g. only mapping frames +// that are actually required), while also satisfying the requirements +// of any configured frame mixer. +// +// Thread-safety: Safe +typedef struct pl_queue_t *pl_queue; + +enum pl_queue_status { + PL_QUEUE_OK, // success + PL_QUEUE_EOF, // no more frames are available + PL_QUEUE_MORE, // more frames needed, but not (yet) available + PL_QUEUE_ERR = -1, // some unknown error occurred while retrieving frames +}; + +struct pl_source_frame { + // The frame's presentation timestamp, in seconds relative to the first + // frame. These must be monotonically increasing for subsequent frames. + // To implement a discontinuous jump, users must explicitly reset the + // frame queue with `pl_queue_reset` and restart from PTS 0.0. + double pts; + + // The frame's duration. This is not needed in normal scenarios, as the + // FPS can be inferred from the `pts` values themselves. Providing it + // only helps initialize the value for initial frames, which can smooth + // out the interpolation weights. Its use is also highly recommended + // when displaying interlaced frames. (Optional) + float duration; + + // If set to something other than PL_FIELD_NONE, this source frame is + // marked as interlaced. It will be split up into two separate frames + // internally, and exported to the resulting `pl_frame_mix` as a pair of + // fields, referencing the corresponding previous and next frames. The + // first field will have the same PTS as `pts`, and the second field will + // be inserted at the timestamp `pts + duration/2`. + // + // Note: As a result of FPS estimates being unreliable around streams with + // mixed FPS (or when mixing interlaced and progressive frames), it's + // highly recommended to always specify a valid `duration` for interlaced + // frames. + enum pl_field first_field; + + // Abstract frame data itself. To allow mapping frames only when they're + // actually needed, frames use a lazy representation. The provided + // callbacks will be invoked to interface with it. + void *frame_data; + + // This will be called to map the frame to the GPU, only if needed. + // + // `tex` is a pointer to an array of 4 texture objects (or NULL), which + // *may* serve as backing storage for the texture being mapped. These are + // intended to be recreated by `map`, e.g. using `pl_tex_recreate` or + // `pl_upload_plane` as appropriate. They will be managed internally by + // `pl_queue` and destroyed at some unspecified future point in time. + // + // Note: If `map` fails, it will not be retried, nor will `discard` be run. + // The user should clean up state in this case. + bool (*map)(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *src, + struct pl_frame *out_frame); + + // If present, this will be called on frames that are done being used by + // `pl_queue`. This may be useful to e.g. unmap textures backed by external + // APIs such as hardware decoders. (Optional) + void (*unmap)(pl_gpu gpu, struct pl_frame *frame, const struct pl_source_frame *src); + + // This function will be called for frames that are deemed unnecessary + // (e.g. never became visible) and should instead be cleanly freed. + // (Optional) + void (*discard)(const struct pl_source_frame *src); +}; + +// Create a new, empty frame queue. +// +// It's highly recommended to fully render a single frame with `pts == 0.0`, +// and flush the GPU pipeline with `pl_gpu_finish`, prior to starting the timed +// playback loop. +PL_API pl_queue pl_queue_create(pl_gpu gpu); +PL_API void pl_queue_destroy(pl_queue *queue); + +// Explicitly clear the queue. This is essentially equivalent to destroying +// and recreating the queue, but preserves any internal memory allocations. +// +// Note: Calling `pl_queue_reset` may block, if another thread is currently +// blocked on a different `pl_queue_*` call. +PL_API void pl_queue_reset(pl_queue queue); + +// Explicitly push a frame. This is an alternative way to feed the frame queue +// with incoming frames, the other method being the asynchronous callback +// specified as `pl_queue_params.get_frame`. Both methods may be used +// simultaneously, although providing `get_frame` is recommended since it +// avoids the risk of the queue underrunning. +// +// When no more frames are available, call this function with `frame == NULL` +// to indicate EOF and begin draining the frame queue. +PL_API void pl_queue_push(pl_queue queue, const struct pl_source_frame *frame); + +// Variant of `pl_queue_push` that blocks while the queue is judged +// (internally) to be "too full". This is useful for asynchronous decoder loops +// in order to prevent the queue from exhausting available RAM if frames are +// decoded significantly faster than they're displayed. +// +// The given `timeout` parameter specifies how long to wait before giving up, +// in nanoseconds. Returns false if this timeout was reached. +PL_API bool pl_queue_push_block(pl_queue queue, uint64_t timeout, + const struct pl_source_frame *frame); + +struct pl_queue_params { + // The PTS of the frame that will be rendered. This should be set to the + // timestamp (in seconds) of the next vsync, relative to the initial frame. + // + // These must be monotonically increasing. To implement a discontinuous + // jump, users must explicitly reset the frame queue with `pl_queue_reset` + // and restart from PTS 0.0. + double pts; + + // The radius of the configured mixer. This should be set to the value + // as returned by `pl_frame_mix_radius`. + float radius; + + // The estimated duration of a vsync, in seconds. This will only be used as + // a hint, the true value will be estimated by comparing `pts` timestamps + // between calls to `pl_queue_update`. (Optional) + float vsync_duration; + + // If the difference between the (estimated) vsync duration and the + // (measured) frame duration is smaller than this threshold, silently + // disable interpolation and switch to ZOH semantics instead. + // + // For example, a value of 0.01 allows the FPS to differ by up to 1% + // without being interpolated. Note that this will result in a continuous + // phase drift unless also compensated for by the user, which will + // eventually resulted in a dropped or duplicated frame. (Though this can + // be preferable to seeing that same phase drift result in a temporally + // smeared image) + float interpolation_threshold; + + // Specifies how long `pl_queue_update` will wait for frames to become + // available, in nanoseconds, before giving up and returning with + // QUEUE_MORE. + // + // If `get_frame` is provided, this value is ignored by `pl_queue` and + // should instead be interpreted by the provided callback. + uint64_t timeout; + + // This callback will be used to pull new frames from the decoder. It may + // block if needed. The user is responsible for setting appropriate time + // limits and/or returning and interpreting QUEUE_MORE as sensible. + // + // Providing this callback is entirely optional. Users can instead choose + // to manually feed the frame queue with new frames using `pl_queue_push`. + enum pl_queue_status (*get_frame)(struct pl_source_frame *out_frame, + const struct pl_queue_params *params); + void *priv; +}; + +#define pl_queue_params(...) (&(struct pl_queue_params) { __VA_ARGS__ }) + +// Advance the frame queue's internal state to the target timestamp. Any frames +// which are no longer needed (i.e. too far in the past) are automatically +// unmapped and evicted. Any future frames which are needed to fill the queue +// must either have been pushed in advance, or will be requested using the +// provided `get_frame` callback. If you call this on `out_mix == NULL`, the +// queue state will advance, but no frames will be mapped. +// +// This function may return with PL_QUEUE_MORE, in which case the user may wish +// to ensure more frames are available and then re-run this function with the +// same parameters. In this case, `out_mix` is still written to, but it may be +// incomplete (or even contain no frames at all). Additionally, when the source +// contains interlaced frames (see `pl_source_frame.first_field`), this +// function may return with PL_QUEUE_MORE if a frame is missing references to +// a future frame. +// +// The resulting mix of frames in `out_mix` will represent the neighbourhood of +// the target timestamp, and can be passed to `pl_render_image_mix` as-is. +// +// Note: `out_mix` will only remain valid until the next call to +// `pl_queue_update` or `pl_queue_reset`. +PL_API enum pl_queue_status pl_queue_update(pl_queue queue, struct pl_frame_mix *out_mix, + const struct pl_queue_params *params); + +// Returns a pl_queue's internal estimates for FPS and VPS (vsyncs per second). +// Returns 0.0 if no estimate is available. +PL_API float pl_queue_estimate_fps(pl_queue queue); +PL_API float pl_queue_estimate_vps(pl_queue queue); + +// Returns the number of frames currently contained in a pl_queue. +PL_API int pl_queue_num_frames(pl_queue queue); + +// Inspect the contents of the Nth queued frame. Returns false if `idx` is +// out of range. +// +// Warning: No guarantee is made to ensure validity of `out->frame_data` +// after this call. In particular, pl_queue_* calls made from another thread +// may call `discard()` on the frame in question. The user bears responsibility +// to avoid accessing `out->frame_data` in a multi-threaded scenario unless +// an external guarantee can be made that the frame won't be dequeued until +// it is done being used by the user. +PL_API bool pl_queue_peek(pl_queue queue, int idx, struct pl_source_frame *out); + +PL_API_END + +#endif // LIBPLACEBO_FRAME_QUEUE_H |