summaryrefslogtreecommitdiffstats
path: root/src/include/libplacebo/utils/frame_queue.h
blob: 2a9c90c36aae916cfdb437d86aaeb49b5e82c1bc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
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