@desklink/host 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +26 -0
- package/README.md +134 -56
- package/bin/desklink-host.mjs +13 -5
- package/dist/bridge.d.ts +1 -0
- package/dist/bridge.d.ts.map +1 -1
- package/dist/bridge.js +12 -4
- package/dist/bridge.js.map +1 -1
- package/dist/engineProcess.d.ts +6 -1
- package/dist/engineProcess.d.ts.map +1 -1
- package/dist/engineProcess.js +35 -4
- package/dist/engineProcess.js.map +1 -1
- package/dist/protocol.d.ts +58 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js.map +1 -1
- package/dist/resolveEngine.d.ts +7 -1
- package/dist/resolveEngine.d.ts.map +1 -1
- package/dist/resolveEngine.js +66 -14
- package/dist/resolveEngine.js.map +1 -1
- package/docs/BOUNDARY.md +15 -7
- package/docs/PROTOCOL.md +267 -27
- package/engine/Cargo.lock +47 -2
- package/engine/Cargo.toml +18 -13
- package/engine/build.rs +86 -4
- package/engine/native/agent_overlay_mac.m +51 -4
- package/engine/native/agent_overlay_wayland.c +145 -12
- package/engine/native/agent_overlay_x11.c +72 -0
- package/engine/native/nvenc_shim.c +453 -0
- package/engine/native/vaapi_shim.c +727 -0
- package/engine/native/vpx_shim.c +54 -4
- package/engine/native/vt_shim.c +448 -0
- package/engine/src/capture.rs +167 -58
- package/engine/src/capture_mac.rs +24 -14
- package/engine/src/capture_win.rs +23 -0
- package/engine/src/clipboard_win.rs +8 -0
- package/engine/src/convert.rs +286 -61
- package/engine/src/cursor.rs +118 -0
- package/engine/src/encoder.rs +122 -13
- package/engine/src/h264.rs +188 -0
- package/engine/src/h264_nvenc.rs +308 -0
- package/engine/src/h264_openh264.rs +735 -0
- package/engine/src/h264_vaapi.rs +263 -0
- package/engine/src/h264_vt.rs +368 -0
- package/engine/src/indicator.rs +104 -3
- package/engine/src/input_win.rs +91 -0
- package/engine/src/keymap_win.rs +39 -0
- package/engine/src/mac.rs +34 -9
- package/engine/src/main.rs +186 -26
- package/engine/src/peer.rs +466 -28
- package/engine/src/portal.rs +42 -4
- package/engine/src/portal_win.rs +37 -0
- package/engine/src/protocol.rs +64 -2
- package/engine/src/session.rs +1252 -105
- package/engine/src/win.rs +110 -0
- package/engine/src/x11.rs +225 -9
- package/engine/src/x11_mac.rs +12 -0
- package/engine/vendor/libva/COPYING +19 -0
- package/engine/vendor/libva/va/va.h +5450 -0
- package/engine/vendor/libva/va/va_dec_av1.h +695 -0
- package/engine/vendor/libva/va/va_dec_hevc.h +616 -0
- package/engine/vendor/libva/va/va_dec_jpeg.h +157 -0
- package/engine/vendor/libva/va/va_dec_vp8.h +249 -0
- package/engine/vendor/libva/va/va_dec_vp9.h +312 -0
- package/engine/vendor/libva/va/va_dec_vvc.h +688 -0
- package/engine/vendor/libva/va/va_drm.h +61 -0
- package/engine/vendor/libva/va/va_enc_av1.h +1010 -0
- package/engine/vendor/libva/va/va_enc_h264.h +659 -0
- package/engine/vendor/libva/va/va_enc_hevc.h +958 -0
- package/engine/vendor/libva/va/va_enc_jpeg.h +162 -0
- package/engine/vendor/libva/va/va_enc_mpeg2.h +308 -0
- package/engine/vendor/libva/va/va_enc_vp8.h +350 -0
- package/engine/vendor/libva/va/va_enc_vp9.h +639 -0
- package/engine/vendor/libva/va/va_fei.h +170 -0
- package/engine/vendor/libva/va/va_fei_h264.h +505 -0
- package/engine/vendor/libva/va/va_fei_hevc.h +254 -0
- package/engine/vendor/libva/va/va_prot.h +525 -0
- package/engine/vendor/libva/va/va_version.h +87 -0
- package/engine/vendor/libva/va/va_vpp.h +1590 -0
- package/engine/vendor/nv-codec-headers/LICENSE +24 -0
- package/engine/vendor/nv-codec-headers/README +7 -0
- package/engine/vendor/nv-codec-headers/include/ffnvcodec/nvEncodeAPI.h +3825 -0
- package/examples/reference-client.html +1 -1
- package/package.json +4 -3
|
@@ -0,0 +1,727 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Thin C surface over VA-API's H.264 slice encoder.
|
|
3
|
+
*
|
|
4
|
+
* libva is loaded with dlopen, never linked: a machine without it, or without
|
|
5
|
+
* a GPU that encodes, must still start the engine. The vendored libva headers
|
|
6
|
+
* (MIT, vendor/libva) give the parameter structs their exact layout and the
|
|
7
|
+
* function pointers their exact types; the Rust side sees an opaque handle and
|
|
8
|
+
* a byte slice.
|
|
9
|
+
*
|
|
10
|
+
* The SPS, PPS and slice headers are written here and handed to the driver as
|
|
11
|
+
* packed headers wherever it takes them: some drivers (Mesa's among them)
|
|
12
|
+
* write no header of their own without them, and the SPS carries a VUI that
|
|
13
|
+
* tells the decoder nothing is reordered, so it shows each frame as soon as it
|
|
14
|
+
* is decoded. The stream is Constrained Baseline: CAVLC, no 8x8 transform, P frames against the one
|
|
15
|
+
* previous picture, picture order equal to decode order (POC type 2) so a
|
|
16
|
+
* decoder never holds a frame back.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
#include <dlfcn.h>
|
|
20
|
+
#include <fcntl.h>
|
|
21
|
+
#include <pthread.h>
|
|
22
|
+
#include <stdarg.h>
|
|
23
|
+
#include <stdint.h>
|
|
24
|
+
#include <stdio.h>
|
|
25
|
+
#include <stdlib.h>
|
|
26
|
+
#include <string.h>
|
|
27
|
+
#include <unistd.h>
|
|
28
|
+
|
|
29
|
+
#include <va/va.h>
|
|
30
|
+
#include <va/va_drm.h>
|
|
31
|
+
|
|
32
|
+
/* The quantizer ceiling while the desktop moves; refinement lowers it. */
|
|
33
|
+
#define DL_VA_MOTION_MAX_QP 51
|
|
34
|
+
|
|
35
|
+
#define DL_VA_FUNCTIONS(F) \
|
|
36
|
+
F(vaInitialize) F(vaTerminate) F(vaSetErrorCallback) F(vaSetInfoCallback) \
|
|
37
|
+
F(vaErrorStr) F(vaMaxNumEntrypoints) F(vaQueryConfigEntrypoints) F(vaGetConfigAttributes) \
|
|
38
|
+
F(vaCreateConfig) F(vaDestroyConfig) F(vaCreateSurfaces) F(vaDestroySurfaces) \
|
|
39
|
+
F(vaCreateContext) F(vaDestroyContext) F(vaCreateBuffer) F(vaDestroyBuffer) \
|
|
40
|
+
F(vaMapBuffer) F(vaUnmapBuffer) F(vaCreateImage) F(vaDestroyImage) \
|
|
41
|
+
F(vaPutImage) F(vaBeginPicture) F(vaRenderPicture) F(vaEndPicture) \
|
|
42
|
+
F(vaSyncSurface)
|
|
43
|
+
|
|
44
|
+
static struct {
|
|
45
|
+
#define DL_VA_POINTER(name) __typeof__(name) *name;
|
|
46
|
+
DL_VA_FUNCTIONS(DL_VA_POINTER)
|
|
47
|
+
__typeof__(vaGetDisplayDRM) *vaGetDisplayDRM;
|
|
48
|
+
const char *missing;
|
|
49
|
+
} va;
|
|
50
|
+
|
|
51
|
+
static pthread_once_t va_once = PTHREAD_ONCE_INIT;
|
|
52
|
+
|
|
53
|
+
/* Resolve libva once per process. The libraries stay loaded: a driver that
|
|
54
|
+
* registered exit handlers must not be unmapped under them. */
|
|
55
|
+
static void va_load(void) {
|
|
56
|
+
void *core = dlopen("libva.so.2", RTLD_NOW | RTLD_LOCAL);
|
|
57
|
+
void *drm = dlopen("libva-drm.so.2", RTLD_NOW | RTLD_LOCAL);
|
|
58
|
+
if (core == NULL || drm == NULL) {
|
|
59
|
+
va.missing = "libva.so.2 or libva-drm.so.2 is not installed";
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
#define DL_VA_RESOLVE(name) \
|
|
63
|
+
if ((va.name = (__typeof__(name) *)dlsym(core, #name)) == NULL) \
|
|
64
|
+
va.missing = "libva lacks " #name;
|
|
65
|
+
DL_VA_FUNCTIONS(DL_VA_RESOLVE)
|
|
66
|
+
va.vaGetDisplayDRM = (__typeof__(vaGetDisplayDRM) *)dlsym(drm, "vaGetDisplayDRM");
|
|
67
|
+
if (va.vaGetDisplayDRM == NULL) {
|
|
68
|
+
va.missing = "libva-drm lacks vaGetDisplayDRM";
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* libva's default info callback writes to stdout, the engine's protocol
|
|
73
|
+
* channel; failures are reported through the error string instead. */
|
|
74
|
+
static void va_quiet(void *context, const char *message) {
|
|
75
|
+
(void)context;
|
|
76
|
+
(void)message;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
typedef struct {
|
|
80
|
+
int fd;
|
|
81
|
+
VADisplay dpy;
|
|
82
|
+
VAConfigID config;
|
|
83
|
+
VAContextID context;
|
|
84
|
+
/* [0] is the input picture, [1] and [2] alternate as reconstruction and
|
|
85
|
+
* reference. */
|
|
86
|
+
VASurfaceID surfaces[3];
|
|
87
|
+
int surface_count;
|
|
88
|
+
VAImage image;
|
|
89
|
+
int has_image;
|
|
90
|
+
VABufferID coded;
|
|
91
|
+
int width;
|
|
92
|
+
int height;
|
|
93
|
+
int width_mbs;
|
|
94
|
+
int height_mbs;
|
|
95
|
+
int fps;
|
|
96
|
+
int bitrate_kbps;
|
|
97
|
+
unsigned int rc_mode;
|
|
98
|
+
int level_idc;
|
|
99
|
+
/* Frames since the last IDR; 0 means the next frame must be one. */
|
|
100
|
+
unsigned int since_idr;
|
|
101
|
+
unsigned int idr_id;
|
|
102
|
+
int recon;
|
|
103
|
+
/* The rate-control buffers go out again on the next frame. */
|
|
104
|
+
int rc_dirty;
|
|
105
|
+
int last_max_qp;
|
|
106
|
+
uint8_t *packet;
|
|
107
|
+
size_t packet_size;
|
|
108
|
+
size_t packet_capacity;
|
|
109
|
+
int packet_is_key;
|
|
110
|
+
/* The driver takes SPS, PPS and slice headers packed; otherwise it
|
|
111
|
+
* writes its own. */
|
|
112
|
+
int packed_headers;
|
|
113
|
+
} dl_vaapi_encoder;
|
|
114
|
+
|
|
115
|
+
/* An RBSP bit writer; fixed room for an SPS or PPS. */
|
|
116
|
+
typedef struct {
|
|
117
|
+
uint8_t data[64];
|
|
118
|
+
int bits;
|
|
119
|
+
} bits_t;
|
|
120
|
+
|
|
121
|
+
static void put(bits_t *b, uint32_t value, int count) {
|
|
122
|
+
for (int i = count - 1; i >= 0; i--, b->bits++) {
|
|
123
|
+
if (value >> i & 1) {
|
|
124
|
+
b->data[b->bits / 8] |= (uint8_t)(0x80 >> (b->bits % 8));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
static void put_ue(bits_t *b, uint32_t value) {
|
|
130
|
+
int length = 0;
|
|
131
|
+
while ((value + 1) >> (length + 1)) {
|
|
132
|
+
length++;
|
|
133
|
+
}
|
|
134
|
+
put(b, 0, length);
|
|
135
|
+
put(b, value + 1, length + 1);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
static void put_se(bits_t *b, int32_t value) {
|
|
139
|
+
put_ue(b, value > 0 ? 2 * (uint32_t)value - 1 : 2 * (uint32_t)-value);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
static void put_trailing(bits_t *b) {
|
|
143
|
+
put(b, 1, 1);
|
|
144
|
+
b->bits = (b->bits + 7) & ~7;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* Start code, then the RBSP with emulation prevention bytes; a slice header's
|
|
148
|
+
* last byte may be partial. */
|
|
149
|
+
static size_t to_annexb(const bits_t *b, uint8_t *out) {
|
|
150
|
+
size_t n = 0;
|
|
151
|
+
int zeros = 0;
|
|
152
|
+
out[n++] = 0, out[n++] = 0, out[n++] = 0, out[n++] = 1;
|
|
153
|
+
for (int i = 0; i < (b->bits + 7) / 8; i++) {
|
|
154
|
+
if (zeros >= 2 && b->data[i] <= 3) {
|
|
155
|
+
out[n++] = 3;
|
|
156
|
+
zeros = 0;
|
|
157
|
+
}
|
|
158
|
+
zeros = b->data[i] == 0 ? zeros + 1 : 0;
|
|
159
|
+
out[n++] = b->data[i];
|
|
160
|
+
}
|
|
161
|
+
return n;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
static void fail(char *error, size_t error_size, const char *format, ...) {
|
|
165
|
+
va_list args;
|
|
166
|
+
va_start(args, format);
|
|
167
|
+
vsnprintf(error, error_size, format, args);
|
|
168
|
+
va_end(args);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
void dl_vaapi_destroy(dl_vaapi_encoder *self);
|
|
172
|
+
|
|
173
|
+
/* The smallest level whose frame size and macroblock rate hold this stream,
|
|
174
|
+
* capped at 5.2 as the SDP offer is. */
|
|
175
|
+
static int level_for(int mbs, int fps) {
|
|
176
|
+
static const struct { int idc, frame_mbs, mbs_per_second; } levels[] = {
|
|
177
|
+
{30, 1620, 40500}, {31, 3600, 108000}, {32, 5120, 216000},
|
|
178
|
+
{40, 8192, 245760}, {42, 8704, 522240}, {50, 22080, 589824},
|
|
179
|
+
{51, 36864, 983040},
|
|
180
|
+
};
|
|
181
|
+
for (size_t i = 0; i < sizeof levels / sizeof levels[0]; i++) {
|
|
182
|
+
if (mbs <= levels[i].frame_mbs && mbs * fps <= levels[i].mbs_per_second) {
|
|
183
|
+
return levels[i].idc;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return 52;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/* Open an encoder on one DRM render node. Returns NULL with a reason in
|
|
190
|
+
* `error` when the node has no H.264 encoder or refuses this configuration. */
|
|
191
|
+
dl_vaapi_encoder *dl_vaapi_create(const char *device, int width, int height, int bitrate_kbps,
|
|
192
|
+
int fps, char *error, size_t error_size) {
|
|
193
|
+
pthread_once(&va_once, va_load);
|
|
194
|
+
if (va.missing != NULL) {
|
|
195
|
+
fail(error, error_size, "%s", va.missing);
|
|
196
|
+
return NULL;
|
|
197
|
+
}
|
|
198
|
+
if (width <= 0 || height <= 0 || (width | height) & 1 || bitrate_kbps <= 0 || fps <= 0) {
|
|
199
|
+
fail(error, error_size, "bad geometry %dx%d", width, height);
|
|
200
|
+
return NULL;
|
|
201
|
+
}
|
|
202
|
+
dl_vaapi_encoder *self = calloc(1, sizeof *self);
|
|
203
|
+
if (self == NULL) {
|
|
204
|
+
fail(error, error_size, "out of memory");
|
|
205
|
+
return NULL;
|
|
206
|
+
}
|
|
207
|
+
self->config = VA_INVALID_ID;
|
|
208
|
+
self->context = VA_INVALID_ID;
|
|
209
|
+
self->coded = VA_INVALID_ID;
|
|
210
|
+
self->fd = open(device, O_RDWR | O_CLOEXEC);
|
|
211
|
+
if (self->fd < 0) {
|
|
212
|
+
fail(error, error_size, "cannot open %s", device);
|
|
213
|
+
free(self);
|
|
214
|
+
return NULL;
|
|
215
|
+
}
|
|
216
|
+
self->dpy = va.vaGetDisplayDRM(self->fd);
|
|
217
|
+
if (self->dpy == NULL) {
|
|
218
|
+
fail(error, error_size, "no VA display on %s", device);
|
|
219
|
+
dl_vaapi_destroy(self);
|
|
220
|
+
return NULL;
|
|
221
|
+
}
|
|
222
|
+
va.vaSetInfoCallback(self->dpy, va_quiet, NULL);
|
|
223
|
+
va.vaSetErrorCallback(self->dpy, va_quiet, NULL);
|
|
224
|
+
int major, minor;
|
|
225
|
+
VAStatus status = va.vaInitialize(self->dpy, &major, &minor);
|
|
226
|
+
if (status != VA_STATUS_SUCCESS) {
|
|
227
|
+
fail(error, error_size, "%s: vaInitialize: %s", device, va.vaErrorStr(status));
|
|
228
|
+
va.vaTerminate(self->dpy); /* frees the display a failed init leaves */
|
|
229
|
+
self->dpy = NULL;
|
|
230
|
+
dl_vaapi_destroy(self);
|
|
231
|
+
return NULL;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/* libva writes up to vaMaxNumEntrypoints() entries, whatever it is given. */
|
|
235
|
+
int count = 0;
|
|
236
|
+
int capacity = va.vaMaxNumEntrypoints(self->dpy);
|
|
237
|
+
VAEntrypoint *entrypoints = capacity > 0 ? calloc((size_t)capacity, sizeof *entrypoints) : NULL;
|
|
238
|
+
VAEntrypoint entrypoint = 0;
|
|
239
|
+
const VAProfile profile = VAProfileH264ConstrainedBaseline;
|
|
240
|
+
if (entrypoints != NULL &&
|
|
241
|
+
va.vaQueryConfigEntrypoints(self->dpy, profile, entrypoints, &count) == VA_STATUS_SUCCESS) {
|
|
242
|
+
for (int i = 0; i < count && i < capacity; i++) {
|
|
243
|
+
if (entrypoints[i] == VAEntrypointEncSlice ||
|
|
244
|
+
(entrypoints[i] == VAEntrypointEncSliceLP && entrypoint == 0)) {
|
|
245
|
+
entrypoint = entrypoints[i];
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
free(entrypoints);
|
|
250
|
+
if (entrypoint == 0) {
|
|
251
|
+
fail(error, error_size, "%s: no H.264 Constrained Baseline encoder", device);
|
|
252
|
+
dl_vaapi_destroy(self);
|
|
253
|
+
return NULL;
|
|
254
|
+
}
|
|
255
|
+
VAConfigAttrib attributes[3] = {{VAConfigAttribRTFormat, 0},
|
|
256
|
+
{VAConfigAttribRateControl, 0},
|
|
257
|
+
{VAConfigAttribEncPackedHeaders, 0}};
|
|
258
|
+
va.vaGetConfigAttributes(self->dpy, profile, entrypoint, attributes, 3);
|
|
259
|
+
const unsigned int packed =
|
|
260
|
+
VA_ENC_PACKED_HEADER_SEQUENCE | VA_ENC_PACKED_HEADER_PICTURE | VA_ENC_PACKED_HEADER_SLICE;
|
|
261
|
+
self->packed_headers = attributes[2].value != VA_ATTRIB_NOT_SUPPORTED &&
|
|
262
|
+
(attributes[2].value & packed) == packed;
|
|
263
|
+
if (attributes[0].value == VA_ATTRIB_NOT_SUPPORTED || !(attributes[0].value & VA_RT_FORMAT_YUV420)) {
|
|
264
|
+
fail(error, error_size, "%s: the H.264 encoder takes no 4:2:0 input", device);
|
|
265
|
+
dl_vaapi_destroy(self);
|
|
266
|
+
return NULL;
|
|
267
|
+
}
|
|
268
|
+
unsigned int rc = attributes[1].value == VA_ATTRIB_NOT_SUPPORTED ? 0 : attributes[1].value;
|
|
269
|
+
/* VBR first: under CBR Mesa pads every frame with filler data up to the
|
|
270
|
+
* rate, so a still desktop would cost the full target. The rate is then
|
|
271
|
+
* a ceiling, as it is for the other encoders' still frames. */
|
|
272
|
+
self->rc_mode = rc & VA_RC_VBR ? VA_RC_VBR : rc & VA_RC_CBR ? VA_RC_CBR : 0;
|
|
273
|
+
if (self->rc_mode == 0) {
|
|
274
|
+
fail(error, error_size, "%s: the H.264 encoder has neither CBR nor VBR", device);
|
|
275
|
+
dl_vaapi_destroy(self);
|
|
276
|
+
return NULL;
|
|
277
|
+
}
|
|
278
|
+
VAConfigAttrib chosen[3] = {{VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420},
|
|
279
|
+
{VAConfigAttribRateControl, self->rc_mode},
|
|
280
|
+
{VAConfigAttribEncPackedHeaders, packed}};
|
|
281
|
+
status = va.vaCreateConfig(self->dpy, profile, entrypoint, chosen,
|
|
282
|
+
self->packed_headers ? 3 : 2, &self->config);
|
|
283
|
+
if (status != VA_STATUS_SUCCESS) {
|
|
284
|
+
fail(error, error_size, "%s: vaCreateConfig: %s", device, va.vaErrorStr(status));
|
|
285
|
+
dl_vaapi_destroy(self);
|
|
286
|
+
return NULL;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
self->width = width;
|
|
290
|
+
self->height = height;
|
|
291
|
+
self->width_mbs = (width + 15) / 16;
|
|
292
|
+
self->height_mbs = (height + 15) / 16;
|
|
293
|
+
/* The input is the picture's own size; the driver pads it. The
|
|
294
|
+
* reconstructions are macroblock-aligned, as the coded picture is. */
|
|
295
|
+
status = va.vaCreateSurfaces(self->dpy, VA_RT_FORMAT_YUV420, width, height, &self->surfaces[0], 1,
|
|
296
|
+
NULL, 0);
|
|
297
|
+
if (status == VA_STATUS_SUCCESS) {
|
|
298
|
+
self->surface_count = 1;
|
|
299
|
+
status = va.vaCreateSurfaces(self->dpy, VA_RT_FORMAT_YUV420, self->width_mbs * 16,
|
|
300
|
+
self->height_mbs * 16, &self->surfaces[1], 2, NULL, 0);
|
|
301
|
+
}
|
|
302
|
+
if (status != VA_STATUS_SUCCESS) {
|
|
303
|
+
fail(error, error_size, "%s: vaCreateSurfaces: %s", device, va.vaErrorStr(status));
|
|
304
|
+
dl_vaapi_destroy(self);
|
|
305
|
+
return NULL;
|
|
306
|
+
}
|
|
307
|
+
self->surface_count = 3;
|
|
308
|
+
status = va.vaCreateContext(self->dpy, self->config, self->width_mbs * 16, self->height_mbs * 16,
|
|
309
|
+
VA_PROGRESSIVE, self->surfaces, 3, &self->context);
|
|
310
|
+
if (status != VA_STATUS_SUCCESS) {
|
|
311
|
+
self->context = VA_INVALID_ID;
|
|
312
|
+
fail(error, error_size, "%s: vaCreateContext %dx%d: %s", device, width, height,
|
|
313
|
+
va.vaErrorStr(status));
|
|
314
|
+
dl_vaapi_destroy(self);
|
|
315
|
+
return NULL;
|
|
316
|
+
}
|
|
317
|
+
VAImageFormat nv12 = {.fourcc = VA_FOURCC_NV12, .byte_order = VA_LSB_FIRST, .bits_per_pixel = 12};
|
|
318
|
+
status = va.vaCreateImage(self->dpy, &nv12, width, height, &self->image);
|
|
319
|
+
if (status != VA_STATUS_SUCCESS) {
|
|
320
|
+
fail(error, error_size, "%s: vaCreateImage NV12: %s", device, va.vaErrorStr(status));
|
|
321
|
+
dl_vaapi_destroy(self);
|
|
322
|
+
return NULL;
|
|
323
|
+
}
|
|
324
|
+
self->has_image = 1;
|
|
325
|
+
/* Twice the raw picture: more than any frame, even an I_PCM one, needs. */
|
|
326
|
+
unsigned int coded_size = (unsigned int)self->width_mbs * self->height_mbs * 256 * 3;
|
|
327
|
+
status = va.vaCreateBuffer(self->dpy, self->context, VAEncCodedBufferType, coded_size, 1, NULL,
|
|
328
|
+
&self->coded);
|
|
329
|
+
if (status != VA_STATUS_SUCCESS) {
|
|
330
|
+
self->coded = VA_INVALID_ID;
|
|
331
|
+
fail(error, error_size, "%s: coded buffer: %s", device, va.vaErrorStr(status));
|
|
332
|
+
dl_vaapi_destroy(self);
|
|
333
|
+
return NULL;
|
|
334
|
+
}
|
|
335
|
+
self->fps = fps;
|
|
336
|
+
self->bitrate_kbps = bitrate_kbps;
|
|
337
|
+
self->level_idc = level_for(self->width_mbs * self->height_mbs, fps);
|
|
338
|
+
self->recon = 1;
|
|
339
|
+
self->rc_dirty = 1;
|
|
340
|
+
self->last_max_qp = DL_VA_MOTION_MAX_QP;
|
|
341
|
+
return self;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/* Copy tightly packed I420 into the NV12 image and onto the input surface. */
|
|
345
|
+
static int upload(dl_vaapi_encoder *self, const uint8_t *i420) {
|
|
346
|
+
uint8_t *base;
|
|
347
|
+
if (va.vaMapBuffer(self->dpy, self->image.buf, (void **)&base) != VA_STATUS_SUCCESS) {
|
|
348
|
+
return -1;
|
|
349
|
+
}
|
|
350
|
+
int w = self->width, h = self->height;
|
|
351
|
+
for (int y = 0; y < h; y++) {
|
|
352
|
+
memcpy(base + self->image.offsets[0] + (size_t)y * self->image.pitches[0],
|
|
353
|
+
i420 + (size_t)y * w, (size_t)w);
|
|
354
|
+
}
|
|
355
|
+
const uint8_t *u = i420 + (size_t)w * h;
|
|
356
|
+
const uint8_t *v = u + (size_t)(w / 2) * (h / 2);
|
|
357
|
+
for (int y = 0; y < h / 2; y++) {
|
|
358
|
+
uint8_t *row = base + self->image.offsets[1] + (size_t)y * self->image.pitches[1];
|
|
359
|
+
const uint8_t *ur = u + (size_t)y * (w / 2), *vr = v + (size_t)y * (w / 2);
|
|
360
|
+
for (int x = 0; x < w / 2; x++) {
|
|
361
|
+
row[2 * x] = ur[x];
|
|
362
|
+
row[2 * x + 1] = vr[x];
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
if (va.vaUnmapBuffer(self->dpy, self->image.buf) != VA_STATUS_SUCCESS) {
|
|
366
|
+
return -1;
|
|
367
|
+
}
|
|
368
|
+
return va.vaPutImage(self->dpy, self->surfaces[0], self->image.image_id, 0, 0, w, h, 0, 0, w, h) ==
|
|
369
|
+
VA_STATUS_SUCCESS
|
|
370
|
+
? 0
|
|
371
|
+
: -1;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
static int add_buffer(dl_vaapi_encoder *self, VABufferID *ids, int *n, VABufferType type,
|
|
375
|
+
const void *data, size_t size) {
|
|
376
|
+
if (va.vaCreateBuffer(self->dpy, self->context, type, (unsigned int)size, 1, (void *)data,
|
|
377
|
+
&ids[*n]) != VA_STATUS_SUCCESS) {
|
|
378
|
+
return -1;
|
|
379
|
+
}
|
|
380
|
+
(*n)++;
|
|
381
|
+
return 0;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
static int add_misc(dl_vaapi_encoder *self, VABufferID *ids, int *n, VAEncMiscParameterType type,
|
|
385
|
+
const void *payload, size_t size) {
|
|
386
|
+
_Alignas(8) unsigned char buffer[sizeof(VAEncMiscParameterBuffer) + 512];
|
|
387
|
+
VAEncMiscParameterBuffer *misc = (VAEncMiscParameterBuffer *)buffer;
|
|
388
|
+
memset(buffer, 0, sizeof buffer);
|
|
389
|
+
misc->type = type;
|
|
390
|
+
memcpy(misc->data, payload, size);
|
|
391
|
+
return add_buffer(self, ids, n, VAEncMiscParameterBufferType, buffer,
|
|
392
|
+
sizeof(VAEncMiscParameterBuffer) + size);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
static int add_packed(dl_vaapi_encoder *self, VABufferID *ids, int *n, uint32_t type,
|
|
396
|
+
const bits_t *unit) {
|
|
397
|
+
uint8_t data[4 + sizeof unit->data * 3 / 2];
|
|
398
|
+
size_t size = to_annexb(unit, data);
|
|
399
|
+
VAEncPackedHeaderParameterBuffer header = {
|
|
400
|
+
.type = type,
|
|
401
|
+
.bit_length = (uint32_t)(size * 8 - (8 - unit->bits % 8) % 8),
|
|
402
|
+
.has_emulation_bytes = 1};
|
|
403
|
+
return add_buffer(self, ids, n, VAEncPackedHeaderParameterBufferType, &header, sizeof header) ||
|
|
404
|
+
add_buffer(self, ids, n, VAEncPackedHeaderDataBufferType, data, size);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/* The slice header for the parameters dl_vaapi_encode gives the driver. */
|
|
408
|
+
static int add_slice_header(dl_vaapi_encoder *self, VABufferID *ids, int *n, int idr,
|
|
409
|
+
unsigned int frame_num, int qp_delta) {
|
|
410
|
+
bits_t slice = {{0}, 0};
|
|
411
|
+
put(&slice, idr ? 0x65 : 0x61, 8); /* nal_ref_idc 3, IDR or non-IDR slice */
|
|
412
|
+
put_ue(&slice, 0); /* first_mb_in_slice */
|
|
413
|
+
put_ue(&slice, idr ? 7 : 5); /* I or P, every slice of the picture */
|
|
414
|
+
put_ue(&slice, 0); /* pic_parameter_set_id */
|
|
415
|
+
put(&slice, frame_num, 8);
|
|
416
|
+
if (idr) {
|
|
417
|
+
put_ue(&slice, self->idr_id);
|
|
418
|
+
} else {
|
|
419
|
+
put(&slice, 0, 1); /* num_ref_idx_active_override_flag */
|
|
420
|
+
put(&slice, 0, 1); /* ref_pic_list_modification_flag_l0 */
|
|
421
|
+
}
|
|
422
|
+
put(&slice, 0, idr ? 2 : 1); /* no_output_of_prior_pics, long_term_reference
|
|
423
|
+
* or adaptive_ref_pic_marking_mode_flag */
|
|
424
|
+
put_se(&slice, qp_delta); /* slice_qp_delta */
|
|
425
|
+
put_ue(&slice, 0); /* disable_deblocking_filter_idc */
|
|
426
|
+
put_ue(&slice, 0); /* slice_alpha_c0_offset_div2 */
|
|
427
|
+
put_ue(&slice, 0); /* slice_beta_offset_div2 */
|
|
428
|
+
return add_packed(self, ids, n, VAEncPackedHeaderSlice, &slice);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/* SPS and PPS for the parameters dl_vaapi_encode gives the driver. */
|
|
432
|
+
static int add_headers(dl_vaapi_encoder *self, VABufferID *ids, int *n) {
|
|
433
|
+
bits_t sps = {{0}, 0}, pps = {{0}, 0};
|
|
434
|
+
put(&sps, 0x67, 8); /* nal_ref_idc 3, SPS */
|
|
435
|
+
put(&sps, 66, 8); /* Baseline ... */
|
|
436
|
+
put(&sps, 0xc0, 8); /* ... constrained (constraint_set0 and set1) */
|
|
437
|
+
put(&sps, self->level_idc, 8);
|
|
438
|
+
put_ue(&sps, 0); /* seq_parameter_set_id */
|
|
439
|
+
put_ue(&sps, 4); /* log2_max_frame_num_minus4 */
|
|
440
|
+
put_ue(&sps, 2); /* pic_order_cnt_type */
|
|
441
|
+
put_ue(&sps, 1); /* max_num_ref_frames */
|
|
442
|
+
put(&sps, 0, 1); /* gaps_in_frame_num_value_allowed_flag */
|
|
443
|
+
put_ue(&sps, self->width_mbs - 1);
|
|
444
|
+
put_ue(&sps, self->height_mbs - 1);
|
|
445
|
+
put(&sps, 1, 1); /* frame_mbs_only_flag */
|
|
446
|
+
put(&sps, 1, 1); /* direct_8x8_inference_flag */
|
|
447
|
+
int crop_right = (self->width_mbs * 16 - self->width) / 2;
|
|
448
|
+
int crop_bottom = (self->height_mbs * 16 - self->height) / 2;
|
|
449
|
+
put(&sps, crop_right || crop_bottom, 1);
|
|
450
|
+
if (crop_right || crop_bottom) {
|
|
451
|
+
put_ue(&sps, 0);
|
|
452
|
+
put_ue(&sps, crop_right);
|
|
453
|
+
put_ue(&sps, 0);
|
|
454
|
+
put_ue(&sps, crop_bottom);
|
|
455
|
+
}
|
|
456
|
+
put(&sps, 1, 1); /* vui_parameters_present_flag */
|
|
457
|
+
put(&sps, 0, 1); /* aspect_ratio_info_present_flag */
|
|
458
|
+
put(&sps, 0, 1); /* overscan_info_present_flag */
|
|
459
|
+
put(&sps, 1, 1); /* video_signal_type_present_flag */
|
|
460
|
+
put(&sps, 5, 3); /* video_format: unspecified */
|
|
461
|
+
put(&sps, 0, 1); /* limited range, as convert.rs produces */
|
|
462
|
+
put(&sps, 1, 1); /* colour_description_present_flag */
|
|
463
|
+
put(&sps, 6, 8); /* BT.601 primaries, transfer and matrix */
|
|
464
|
+
put(&sps, 6, 8);
|
|
465
|
+
put(&sps, 6, 8);
|
|
466
|
+
put(&sps, 0, 1); /* chroma_loc_info_present_flag */
|
|
467
|
+
put(&sps, 1, 1); /* timing_info_present_flag */
|
|
468
|
+
put(&sps, 1, 32); /* num_units_in_tick */
|
|
469
|
+
put(&sps, (uint32_t)self->fps * 2, 32); /* time_scale */
|
|
470
|
+
put(&sps, 0, 1); /* fixed_frame_rate_flag */
|
|
471
|
+
put(&sps, 0, 1); /* nal_hrd_parameters_present_flag */
|
|
472
|
+
put(&sps, 0, 1); /* vcl_hrd_parameters_present_flag */
|
|
473
|
+
put(&sps, 0, 1); /* pic_struct_present_flag */
|
|
474
|
+
put(&sps, 1, 1); /* bitstream_restriction_flag */
|
|
475
|
+
put(&sps, 1, 1); /* motion_vectors_over_pic_boundaries_flag */
|
|
476
|
+
put_ue(&sps, 0); /* max_bytes_per_pic_denom */
|
|
477
|
+
put_ue(&sps, 0); /* max_bits_per_mb_denom */
|
|
478
|
+
put_ue(&sps, 16); /* log2_max_mv_length_horizontal */
|
|
479
|
+
put_ue(&sps, 16); /* log2_max_mv_length_vertical */
|
|
480
|
+
put_ue(&sps, 0); /* max_num_reorder_frames: show on decode */
|
|
481
|
+
put_ue(&sps, 1); /* max_dec_frame_buffering */
|
|
482
|
+
put_trailing(&sps);
|
|
483
|
+
|
|
484
|
+
put(&pps, 0x68, 8); /* nal_ref_idc 3, PPS */
|
|
485
|
+
put_ue(&pps, 0); /* pic_parameter_set_id */
|
|
486
|
+
put_ue(&pps, 0); /* seq_parameter_set_id */
|
|
487
|
+
put(&pps, 0, 1); /* entropy_coding_mode_flag: CAVLC */
|
|
488
|
+
put(&pps, 0, 1); /* bottom_field_pic_order_in_frame_present_flag */
|
|
489
|
+
put_ue(&pps, 0); /* num_slice_groups_minus1 */
|
|
490
|
+
put_ue(&pps, 0); /* num_ref_idx_l0_default_active_minus1 */
|
|
491
|
+
put_ue(&pps, 0); /* num_ref_idx_l1_default_active_minus1 */
|
|
492
|
+
put(&pps, 0, 1); /* weighted_pred_flag */
|
|
493
|
+
put(&pps, 0, 2); /* weighted_bipred_idc */
|
|
494
|
+
put_ue(&pps, 0); /* pic_init_qp_minus26 (se 0 == ue 0) */
|
|
495
|
+
put_ue(&pps, 0); /* pic_init_qs_minus26 */
|
|
496
|
+
put_ue(&pps, 0); /* chroma_qp_index_offset */
|
|
497
|
+
put(&pps, 1, 1); /* deblocking_filter_control_present_flag */
|
|
498
|
+
put(&pps, 0, 1); /* constrained_intra_pred_flag */
|
|
499
|
+
put(&pps, 0, 1); /* redundant_pic_cnt_present_flag */
|
|
500
|
+
put_trailing(&pps);
|
|
501
|
+
|
|
502
|
+
return add_packed(self, ids, n, VAEncPackedHeaderSequence, &sps) ||
|
|
503
|
+
add_packed(self, ids, n, VAEncPackedHeaderPicture, &pps);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
static void invalidate(VAPictureH264 *picture) {
|
|
507
|
+
picture->picture_id = VA_INVALID_SURFACE;
|
|
508
|
+
picture->flags = VA_PICTURE_H264_INVALID;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/* Encode one tightly packed I420 frame. `max_qp` is the quantizer ceiling for
|
|
512
|
+
* this frame alone: the motion ceiling, or a refinement's lower one.
|
|
513
|
+
* Returns 1 when a packet is available through dl_vaapi_packet_*, -1 when the
|
|
514
|
+
* driver refused the frame. */
|
|
515
|
+
int dl_vaapi_encode(dl_vaapi_encoder *self, const uint8_t *i420, int force_keyframe, int max_qp) {
|
|
516
|
+
if (self == NULL || i420 == NULL || upload(self, i420) != 0) {
|
|
517
|
+
return -1;
|
|
518
|
+
}
|
|
519
|
+
int idr = force_keyframe || self->since_idr == 0;
|
|
520
|
+
if (idr) {
|
|
521
|
+
self->since_idr = 0;
|
|
522
|
+
self->rc_dirty = 1;
|
|
523
|
+
}
|
|
524
|
+
if (max_qp != self->last_max_qp) {
|
|
525
|
+
self->last_max_qp = max_qp;
|
|
526
|
+
self->rc_dirty = 1;
|
|
527
|
+
}
|
|
528
|
+
VASurfaceID recon = self->surfaces[self->recon];
|
|
529
|
+
VASurfaceID reference = self->surfaces[3 - self->recon];
|
|
530
|
+
VABufferID ids[16];
|
|
531
|
+
int n = 0;
|
|
532
|
+
int failed = 0;
|
|
533
|
+
|
|
534
|
+
if (idr) {
|
|
535
|
+
VAEncSequenceParameterBufferH264 seq = {0};
|
|
536
|
+
seq.level_idc = self->level_idc;
|
|
537
|
+
/* Informational for the driver: there is no periodic key frame. The
|
|
538
|
+
* frame types below are what decide. */
|
|
539
|
+
seq.intra_period = seq.intra_idr_period = 0x7fffffff;
|
|
540
|
+
seq.ip_period = 1;
|
|
541
|
+
seq.bits_per_second = (unsigned int)self->bitrate_kbps * 1000;
|
|
542
|
+
seq.max_num_ref_frames = 1;
|
|
543
|
+
seq.picture_width_in_mbs = self->width_mbs;
|
|
544
|
+
seq.picture_height_in_mbs = self->height_mbs;
|
|
545
|
+
seq.seq_fields.bits.chroma_format_idc = 1;
|
|
546
|
+
seq.seq_fields.bits.frame_mbs_only_flag = 1;
|
|
547
|
+
seq.seq_fields.bits.direct_8x8_inference_flag = 1;
|
|
548
|
+
seq.seq_fields.bits.log2_max_frame_num_minus4 = 4;
|
|
549
|
+
seq.seq_fields.bits.pic_order_cnt_type = 2;
|
|
550
|
+
int crop_right = self->width_mbs * 16 - self->width;
|
|
551
|
+
int crop_bottom = self->height_mbs * 16 - self->height;
|
|
552
|
+
if (crop_right || crop_bottom) {
|
|
553
|
+
/* In chroma samples: two luma samples per unit at 4:2:0. */
|
|
554
|
+
seq.frame_cropping_flag = 1;
|
|
555
|
+
seq.frame_crop_right_offset = crop_right / 2;
|
|
556
|
+
seq.frame_crop_bottom_offset = crop_bottom / 2;
|
|
557
|
+
}
|
|
558
|
+
seq.vui_parameters_present_flag = 1;
|
|
559
|
+
seq.vui_fields.bits.timing_info_present_flag = 1;
|
|
560
|
+
seq.vui_fields.bits.bitstream_restriction_flag = 1;
|
|
561
|
+
seq.vui_fields.bits.log2_max_mv_length_horizontal = 16;
|
|
562
|
+
seq.vui_fields.bits.log2_max_mv_length_vertical = 16;
|
|
563
|
+
seq.num_units_in_tick = 1;
|
|
564
|
+
seq.time_scale = (unsigned int)self->fps * 2;
|
|
565
|
+
failed |= add_buffer(self, ids, &n, VAEncSequenceParameterBufferType, &seq, sizeof seq);
|
|
566
|
+
if (self->packed_headers) {
|
|
567
|
+
failed |= add_headers(self, ids, &n);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
if (self->rc_dirty) {
|
|
571
|
+
VAEncMiscParameterRateControl rate = {0};
|
|
572
|
+
rate.bits_per_second = (unsigned int)self->bitrate_kbps * 1000;
|
|
573
|
+
rate.target_percentage = self->rc_mode == VA_RC_CBR ? 100 : 90;
|
|
574
|
+
rate.window_size = 1000;
|
|
575
|
+
rate.initial_qp = 26;
|
|
576
|
+
rate.min_qp = 1;
|
|
577
|
+
rate.max_qp = (unsigned int)max_qp;
|
|
578
|
+
/* A skipped frame is a frozen desktop until the next change. */
|
|
579
|
+
rate.rc_flags.bits.disable_frame_skip = 1;
|
|
580
|
+
failed |= add_misc(self, ids, &n, VAEncMiscParameterTypeRateControl, &rate, sizeof rate);
|
|
581
|
+
VAEncMiscParameterFrameRate rate_fps = {.framerate = (unsigned int)self->fps};
|
|
582
|
+
failed |= add_misc(self, ids, &n, VAEncMiscParameterTypeFrameRate, &rate_fps, sizeof rate_fps);
|
|
583
|
+
/* One second of buffer, half full: a key frame may borrow several
|
|
584
|
+
* frames' budget. */
|
|
585
|
+
VAEncMiscParameterHRD hrd = {.buffer_size = (unsigned int)self->bitrate_kbps * 1000,
|
|
586
|
+
.initial_buffer_fullness = (unsigned int)self->bitrate_kbps * 500};
|
|
587
|
+
failed |= add_misc(self, ids, &n, VAEncMiscParameterTypeHRD, &hrd, sizeof hrd);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
VAEncPictureParameterBufferH264 pic = {0};
|
|
591
|
+
unsigned int frame_num = self->since_idr & 0xff;
|
|
592
|
+
pic.CurrPic.picture_id = recon;
|
|
593
|
+
pic.CurrPic.frame_idx = frame_num;
|
|
594
|
+
pic.CurrPic.TopFieldOrderCnt = (int32_t)(self->since_idr * 2);
|
|
595
|
+
pic.CurrPic.BottomFieldOrderCnt = pic.CurrPic.TopFieldOrderCnt;
|
|
596
|
+
for (int i = 0; i < 16; i++) {
|
|
597
|
+
invalidate(&pic.ReferenceFrames[i]);
|
|
598
|
+
}
|
|
599
|
+
VAPictureH264 ref = {0};
|
|
600
|
+
if (!idr) {
|
|
601
|
+
ref.picture_id = reference;
|
|
602
|
+
ref.frame_idx = (self->since_idr - 1) & 0xff;
|
|
603
|
+
ref.flags = VA_PICTURE_H264_SHORT_TERM_REFERENCE;
|
|
604
|
+
ref.TopFieldOrderCnt = (int32_t)((self->since_idr - 1) * 2);
|
|
605
|
+
ref.BottomFieldOrderCnt = ref.TopFieldOrderCnt;
|
|
606
|
+
pic.ReferenceFrames[0] = ref;
|
|
607
|
+
}
|
|
608
|
+
pic.coded_buf = self->coded;
|
|
609
|
+
pic.frame_num = frame_num;
|
|
610
|
+
pic.pic_init_qp = 26;
|
|
611
|
+
pic.pic_fields.bits.idr_pic_flag = idr;
|
|
612
|
+
pic.pic_fields.bits.reference_pic_flag = 1;
|
|
613
|
+
pic.pic_fields.bits.deblocking_filter_control_present_flag = 1;
|
|
614
|
+
failed |= add_buffer(self, ids, &n, VAEncPictureParameterBufferType, &pic, sizeof pic);
|
|
615
|
+
|
|
616
|
+
VAEncSliceParameterBufferH264 slice = {0};
|
|
617
|
+
/* A lowered ceiling also starts the frame at it. Under rate control this
|
|
618
|
+
* is only a hint, but Mesa sends a frame's QP limits to the encoder only
|
|
619
|
+
* when its starting QP changes: without it the ceiling would not move. */
|
|
620
|
+
slice.slice_qp_delta = max_qp < DL_VA_MOTION_MAX_QP ? max_qp - 26 : 0;
|
|
621
|
+
slice.num_macroblocks = self->width_mbs * self->height_mbs;
|
|
622
|
+
slice.slice_type = idr ? 2 : 0; /* I : P */
|
|
623
|
+
slice.idr_pic_id = self->idr_id;
|
|
624
|
+
for (int i = 0; i < 32; i++) {
|
|
625
|
+
invalidate(&slice.RefPicList0[i]);
|
|
626
|
+
invalidate(&slice.RefPicList1[i]);
|
|
627
|
+
}
|
|
628
|
+
if (!idr) {
|
|
629
|
+
slice.RefPicList0[0] = ref;
|
|
630
|
+
}
|
|
631
|
+
failed |= add_buffer(self, ids, &n, VAEncSliceParameterBufferType, &slice, sizeof slice);
|
|
632
|
+
if (self->packed_headers) {
|
|
633
|
+
failed |= add_slice_header(self, ids, &n, idr, frame_num, slice.slice_qp_delta);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
if (!failed) {
|
|
637
|
+
failed = va.vaBeginPicture(self->dpy, self->context, self->surfaces[0]) != VA_STATUS_SUCCESS;
|
|
638
|
+
if (!failed) {
|
|
639
|
+
failed |= va.vaRenderPicture(self->dpy, self->context, ids, n) != VA_STATUS_SUCCESS;
|
|
640
|
+
/* EndPicture always, so a refused render leaves no picture open. */
|
|
641
|
+
failed |= va.vaEndPicture(self->dpy, self->context) != VA_STATUS_SUCCESS;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
for (int i = 0; i < n; i++) {
|
|
645
|
+
va.vaDestroyBuffer(self->dpy, ids[i]);
|
|
646
|
+
}
|
|
647
|
+
if (failed || va.vaSyncSurface(self->dpy, self->surfaces[0]) != VA_STATUS_SUCCESS) {
|
|
648
|
+
self->since_idr = 0; /* the reference is unknown now */
|
|
649
|
+
return -1;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
VACodedBufferSegment *segment;
|
|
653
|
+
if (va.vaMapBuffer(self->dpy, self->coded, (void **)&segment) != VA_STATUS_SUCCESS) {
|
|
654
|
+
self->since_idr = 0;
|
|
655
|
+
return -1;
|
|
656
|
+
}
|
|
657
|
+
self->packet_size = 0;
|
|
658
|
+
for (VACodedBufferSegment *s = segment; s != NULL; s = (VACodedBufferSegment *)s->next) {
|
|
659
|
+
if (self->packet_size + s->size > self->packet_capacity) {
|
|
660
|
+
size_t capacity = (self->packet_size + s->size) * 2;
|
|
661
|
+
uint8_t *grown = realloc(self->packet, capacity);
|
|
662
|
+
if (grown == NULL) {
|
|
663
|
+
va.vaUnmapBuffer(self->dpy, self->coded);
|
|
664
|
+
return -1;
|
|
665
|
+
}
|
|
666
|
+
self->packet = grown;
|
|
667
|
+
self->packet_capacity = capacity;
|
|
668
|
+
}
|
|
669
|
+
memcpy(self->packet + self->packet_size, s->buf, s->size);
|
|
670
|
+
self->packet_size += s->size;
|
|
671
|
+
}
|
|
672
|
+
va.vaUnmapBuffer(self->dpy, self->coded);
|
|
673
|
+
|
|
674
|
+
if (idr) {
|
|
675
|
+
self->idr_id = (self->idr_id + 1) & 0xffff;
|
|
676
|
+
}
|
|
677
|
+
self->packet_is_key = idr;
|
|
678
|
+
self->since_idr++;
|
|
679
|
+
self->recon = 3 - self->recon;
|
|
680
|
+
self->rc_dirty = 0;
|
|
681
|
+
return self->packet_size > 0 ? 1 : -1;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/* The target for the frames that follow; never forces an IDR. */
|
|
685
|
+
void dl_vaapi_set_bitrate(dl_vaapi_encoder *self, int bitrate_kbps) {
|
|
686
|
+
if (bitrate_kbps > 0 && bitrate_kbps != self->bitrate_kbps) {
|
|
687
|
+
self->bitrate_kbps = bitrate_kbps;
|
|
688
|
+
self->rc_dirty = 1;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
int dl_vaapi_motion_max_qp(void) { return DL_VA_MOTION_MAX_QP; }
|
|
693
|
+
|
|
694
|
+
const uint8_t *dl_vaapi_packet_data(const dl_vaapi_encoder *self) { return self->packet; }
|
|
695
|
+
|
|
696
|
+
size_t dl_vaapi_packet_size(const dl_vaapi_encoder *self) { return self->packet_size; }
|
|
697
|
+
|
|
698
|
+
int dl_vaapi_packet_is_key(const dl_vaapi_encoder *self) { return self->packet_is_key; }
|
|
699
|
+
|
|
700
|
+
void dl_vaapi_destroy(dl_vaapi_encoder *self) {
|
|
701
|
+
if (self == NULL) {
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
if (self->dpy != NULL) {
|
|
705
|
+
if (self->coded != VA_INVALID_ID) {
|
|
706
|
+
va.vaDestroyBuffer(self->dpy, self->coded);
|
|
707
|
+
}
|
|
708
|
+
if (self->has_image) {
|
|
709
|
+
va.vaDestroyImage(self->dpy, self->image.image_id);
|
|
710
|
+
}
|
|
711
|
+
if (self->context != VA_INVALID_ID) {
|
|
712
|
+
va.vaDestroyContext(self->dpy, self->context);
|
|
713
|
+
}
|
|
714
|
+
if (self->surface_count > 0) {
|
|
715
|
+
va.vaDestroySurfaces(self->dpy, self->surfaces, self->surface_count);
|
|
716
|
+
}
|
|
717
|
+
if (self->config != VA_INVALID_ID) {
|
|
718
|
+
va.vaDestroyConfig(self->dpy, self->config);
|
|
719
|
+
}
|
|
720
|
+
va.vaTerminate(self->dpy);
|
|
721
|
+
}
|
|
722
|
+
if (self->fd >= 0) {
|
|
723
|
+
close(self->fd);
|
|
724
|
+
}
|
|
725
|
+
free(self->packet);
|
|
726
|
+
free(self);
|
|
727
|
+
}
|