1 /**************************************************************************
2 *
3 * Copyright 2010 Thomas Balling Sørensen & Orasanu Lucian.
4 * Copyright 2014 Advanced Micro Devices, Inc.
5 * All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sub license, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
14 *
15 * The above copyright notice and this permission notice (including the
16 * next paragraph) shall be included in all copies or substantial portions
17 * of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
22 * IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR
23 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 **************************************************************************/
28
29 #include "pipe/p_screen.h"
30 #include "frontend/drm_driver.h"
31 #include "util/u_memory.h"
32 #include "util/u_handle_table.h"
33 #include "util/u_transfer.h"
34 #include "util/set.h"
35 #include "vl/vl_winsys.h"
36
37 #include "va_private.h"
38
39 #ifdef _WIN32
40 #include <va/va_win32.h>
41 #endif
42
43 #ifndef VA_MAPBUFFER_FLAG_DEFAULT
44 #define VA_MAPBUFFER_FLAG_DEFAULT 0
45 #define VA_MAPBUFFER_FLAG_READ 1
46 #define VA_MAPBUFFER_FLAG_WRITE 2
47 #endif
48
49 VAStatus
vlVaCreateBuffer(VADriverContextP ctx,VAContextID context,VABufferType type,unsigned int size,unsigned int num_elements,void * data,VABufferID * buf_id)50 vlVaCreateBuffer(VADriverContextP ctx, VAContextID context, VABufferType type,
51 unsigned int size, unsigned int num_elements, void *data,
52 VABufferID *buf_id)
53 {
54 vlVaDriver *drv;
55 vlVaBuffer *buf;
56
57 if (!ctx)
58 return VA_STATUS_ERROR_INVALID_CONTEXT;
59
60 buf = CALLOC(1, sizeof(vlVaBuffer));
61 if (!buf)
62 return VA_STATUS_ERROR_ALLOCATION_FAILED;
63
64 buf->type = type;
65 buf->size = size;
66 buf->num_elements = num_elements;
67
68 if (buf->type == VAEncCodedBufferType)
69 buf->data = CALLOC(1, sizeof(VACodedBufferSegment));
70 else
71 buf->data = MALLOC(size * num_elements);
72
73 if (!buf->data) {
74 FREE(buf);
75 return VA_STATUS_ERROR_ALLOCATION_FAILED;
76 }
77
78 if (data)
79 memcpy(buf->data, data, size * num_elements);
80
81 drv = VL_VA_DRIVER(ctx);
82 mtx_lock(&drv->mutex);
83 *buf_id = handle_table_add(drv->htab, buf);
84 mtx_unlock(&drv->mutex);
85
86 return VA_STATUS_SUCCESS;
87 }
88
89 VAStatus
vlVaBufferSetNumElements(VADriverContextP ctx,VABufferID buf_id,unsigned int num_elements)90 vlVaBufferSetNumElements(VADriverContextP ctx, VABufferID buf_id,
91 unsigned int num_elements)
92 {
93 vlVaDriver *drv;
94 vlVaBuffer *buf;
95
96 if (!ctx)
97 return VA_STATUS_ERROR_INVALID_CONTEXT;
98
99 drv = VL_VA_DRIVER(ctx);
100 mtx_lock(&drv->mutex);
101 buf = handle_table_get(drv->htab, buf_id);
102 mtx_unlock(&drv->mutex);
103 if (!buf)
104 return VA_STATUS_ERROR_INVALID_BUFFER;
105
106 if (buf->derived_surface.resource)
107 return VA_STATUS_ERROR_INVALID_BUFFER;
108
109 buf->data = REALLOC(buf->data, buf->size * buf->num_elements,
110 buf->size * num_elements);
111 buf->num_elements = num_elements;
112
113 if (!buf->data)
114 return VA_STATUS_ERROR_ALLOCATION_FAILED;
115
116 return VA_STATUS_SUCCESS;
117 }
118
119 VAStatus
vlVaMapBuffer(VADriverContextP ctx,VABufferID buf_id,void ** pbuff)120 vlVaMapBuffer(VADriverContextP ctx, VABufferID buf_id, void **pbuff)
121 {
122 return vlVaMapBuffer2(ctx, buf_id, pbuff, VA_MAPBUFFER_FLAG_DEFAULT);
123 }
124
vlVaMapBuffer2(VADriverContextP ctx,VABufferID buf_id,void ** pbuff,uint32_t flags)125 VAStatus vlVaMapBuffer2(VADriverContextP ctx, VABufferID buf_id,
126 void **pbuff, uint32_t flags)
127 {
128 vlVaDriver *drv;
129 vlVaBuffer *buf;
130
131 if (!ctx)
132 return VA_STATUS_ERROR_INVALID_CONTEXT;
133
134 drv = VL_VA_DRIVER(ctx);
135 if (!drv)
136 return VA_STATUS_ERROR_INVALID_CONTEXT;
137
138 if (!pbuff)
139 return VA_STATUS_ERROR_INVALID_PARAMETER;
140
141 mtx_lock(&drv->mutex);
142 buf = handle_table_get(drv->htab, buf_id);
143 if (!buf || buf->export_refcount > 0) {
144 mtx_unlock(&drv->mutex);
145 return VA_STATUS_ERROR_INVALID_BUFFER;
146 }
147
148 if (buf->type == VAEncCodedBufferType)
149 vlVaGetBufferFeedback(buf);
150
151 if (buf->derived_surface.resource) {
152 struct pipe_resource *resource;
153 struct pipe_box box;
154 unsigned usage = 0;
155 void *(*map_func)(struct pipe_context *,
156 struct pipe_resource *resource,
157 unsigned level,
158 unsigned usage, /* a combination of PIPE_MAP_x */
159 const struct pipe_box *,
160 struct pipe_transfer **out_transfer);
161
162 memset(&box, 0, sizeof(box));
163 resource = buf->derived_surface.resource;
164 box.width = resource->width0;
165 box.height = resource->height0;
166 box.depth = resource->depth0;
167
168 if (resource->target == PIPE_BUFFER)
169 map_func = drv->pipe->buffer_map;
170 else
171 map_func = drv->pipe->texture_map;
172
173 if (flags == VA_MAPBUFFER_FLAG_DEFAULT) {
174 /* For VAImageBufferType, use PIPE_MAP_WRITE for now,
175 * PIPE_MAP_READ_WRITE degradate perf with two copies when map/unmap. */
176 if (buf->type == VAEncCodedBufferType)
177 usage = PIPE_MAP_READ;
178 else
179 usage = PIPE_MAP_WRITE;
180
181 /* Map decoder and postproc surfaces also for reading. */
182 if (buf->derived_surface.entrypoint == PIPE_VIDEO_ENTRYPOINT_BITSTREAM ||
183 buf->derived_surface.entrypoint == PIPE_VIDEO_ENTRYPOINT_PROCESSING)
184 usage |= PIPE_MAP_READ;
185 }
186
187 if (flags & VA_MAPBUFFER_FLAG_READ)
188 usage |= PIPE_MAP_READ;
189 if (flags & VA_MAPBUFFER_FLAG_WRITE)
190 usage |= PIPE_MAP_WRITE;
191
192 assert(usage);
193
194 *pbuff = map_func(drv->pipe, resource, 0, usage,
195 &box, &buf->derived_surface.transfer);
196 mtx_unlock(&drv->mutex);
197
198 if (!buf->derived_surface.transfer || !*pbuff)
199 return VA_STATUS_ERROR_INVALID_BUFFER;
200
201 if (buf->type == VAEncCodedBufferType) {
202 VACodedBufferSegment* curr_buf_ptr = (VACodedBufferSegment*) buf->data;
203
204 if ((buf->extended_metadata.present_metadata & PIPE_VIDEO_FEEDBACK_METADATA_TYPE_ENCODE_RESULT) &&
205 (buf->extended_metadata.encode_result & PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_FAILED)) {
206 curr_buf_ptr->status = VA_CODED_BUF_STATUS_BAD_BITSTREAM;
207 return VA_STATUS_ERROR_OPERATION_FAILED;
208 }
209
210 curr_buf_ptr->status = (buf->extended_metadata.average_frame_qp & VA_CODED_BUF_STATUS_PICTURE_AVE_QP_MASK);
211 if (buf->extended_metadata.encode_result & PIPE_VIDEO_FEEDBACK_METADATA_ENCODE_FLAG_MAX_FRAME_SIZE_OVERFLOW)
212 curr_buf_ptr->status |= VA_CODED_BUF_STATUS_FRAME_SIZE_OVERFLOW;
213
214 if ((buf->extended_metadata.present_metadata & PIPE_VIDEO_FEEDBACK_METADATA_TYPE_CODEC_UNIT_LOCATION) == 0) {
215 curr_buf_ptr->buf = *pbuff;
216 curr_buf_ptr->size = buf->coded_size;
217 *pbuff = buf->data;
218 } else {
219 uint8_t* compressed_bitstream_data = *pbuff;
220 *pbuff = buf->data;
221
222 for (size_t i = 0; i < buf->extended_metadata.codec_unit_metadata_count - 1; i++) {
223 if (!curr_buf_ptr->next)
224 curr_buf_ptr->next = CALLOC(1, sizeof(VACodedBufferSegment));
225 if (!curr_buf_ptr->next)
226 return VA_STATUS_ERROR_ALLOCATION_FAILED;
227 curr_buf_ptr = curr_buf_ptr->next;
228 }
229 if (curr_buf_ptr->next) {
230 VACodedBufferSegment *node = curr_buf_ptr->next;
231 while (node) {
232 VACodedBufferSegment *next = node->next;
233 FREE(node);
234 node = next;
235 }
236 }
237 curr_buf_ptr->next = NULL;
238
239 curr_buf_ptr = buf->data;
240 for (size_t i = 0; i < buf->extended_metadata.codec_unit_metadata_count; i++) {
241 curr_buf_ptr->size = buf->extended_metadata.codec_unit_metadata[i].size;
242 curr_buf_ptr->buf = compressed_bitstream_data + buf->extended_metadata.codec_unit_metadata[i].offset;
243 if (buf->extended_metadata.codec_unit_metadata[i].flags & PIPE_VIDEO_CODEC_UNIT_LOCATION_FLAG_MAX_SLICE_SIZE_OVERFLOW)
244 curr_buf_ptr->status |= VA_CODED_BUF_STATUS_SLICE_OVERFLOW_MASK;
245 if (buf->extended_metadata.codec_unit_metadata[i].flags & PIPE_VIDEO_CODEC_UNIT_LOCATION_FLAG_SINGLE_NALU)
246 curr_buf_ptr->status |= VA_CODED_BUF_STATUS_SINGLE_NALU;
247
248 curr_buf_ptr = curr_buf_ptr->next;
249 }
250 }
251 }
252 } else {
253 mtx_unlock(&drv->mutex);
254 *pbuff = buf->data;
255 }
256
257 return VA_STATUS_SUCCESS;
258 }
259
260 VAStatus
vlVaUnmapBuffer(VADriverContextP ctx,VABufferID buf_id)261 vlVaUnmapBuffer(VADriverContextP ctx, VABufferID buf_id)
262 {
263 vlVaDriver *drv;
264 vlVaBuffer *buf;
265 struct pipe_resource *resource;
266
267 if (!ctx)
268 return VA_STATUS_ERROR_INVALID_CONTEXT;
269
270 drv = VL_VA_DRIVER(ctx);
271 if (!drv)
272 return VA_STATUS_ERROR_INVALID_CONTEXT;
273
274 mtx_lock(&drv->mutex);
275 buf = handle_table_get(drv->htab, buf_id);
276 if (!buf || buf->export_refcount > 0) {
277 mtx_unlock(&drv->mutex);
278 return VA_STATUS_ERROR_INVALID_BUFFER;
279 }
280
281 resource = buf->derived_surface.resource;
282 if (resource) {
283 void (*unmap_func)(struct pipe_context *pipe,
284 struct pipe_transfer *transfer);
285
286 if (!buf->derived_surface.transfer) {
287 mtx_unlock(&drv->mutex);
288 return VA_STATUS_ERROR_INVALID_BUFFER;
289 }
290
291 if (resource->target == PIPE_BUFFER)
292 unmap_func = pipe_buffer_unmap;
293 else
294 unmap_func = pipe_texture_unmap;
295
296 unmap_func(drv->pipe, buf->derived_surface.transfer);
297 buf->derived_surface.transfer = NULL;
298
299 if (buf->type == VAImageBufferType)
300 drv->pipe->flush(drv->pipe, NULL, 0);
301 }
302 mtx_unlock(&drv->mutex);
303
304 return VA_STATUS_SUCCESS;
305 }
306
307 VAStatus
vlVaDestroyBuffer(VADriverContextP ctx,VABufferID buf_id)308 vlVaDestroyBuffer(VADriverContextP ctx, VABufferID buf_id)
309 {
310 vlVaDriver *drv;
311 vlVaBuffer *buf;
312
313 if (!ctx)
314 return VA_STATUS_ERROR_INVALID_CONTEXT;
315
316 drv = VL_VA_DRIVER(ctx);
317 mtx_lock(&drv->mutex);
318 buf = handle_table_get(drv->htab, buf_id);
319 if (!buf) {
320 mtx_unlock(&drv->mutex);
321 return VA_STATUS_ERROR_INVALID_BUFFER;
322 }
323
324 if (buf->derived_surface.resource) {
325 pipe_resource_reference(&buf->derived_surface.resource, NULL);
326
327 if (buf->derived_image_buffer)
328 buf->derived_image_buffer->destroy(buf->derived_image_buffer);
329 }
330
331 if (buf->type == VAEncCodedBufferType) {
332 VACodedBufferSegment* node = buf->data;
333 while (node) {
334 VACodedBufferSegment* next = (VACodedBufferSegment*) node->next;
335 FREE(node);
336 node = next;
337 }
338 } else {
339 FREE(buf->data);
340 }
341
342 if (buf->ctx) {
343 assert(_mesa_set_search(buf->ctx->buffers, buf));
344 _mesa_set_remove_key(buf->ctx->buffers, buf);
345 vlVaGetBufferFeedback(buf);
346 if (buf->fence && buf->ctx->decoder && buf->ctx->decoder->destroy_fence)
347 buf->ctx->decoder->destroy_fence(buf->ctx->decoder, buf->fence);
348 }
349
350 if (buf->coded_surf)
351 buf->coded_surf->coded_buf = NULL;
352
353 FREE(buf);
354 handle_table_remove(VL_VA_DRIVER(ctx)->htab, buf_id);
355 mtx_unlock(&drv->mutex);
356
357 return VA_STATUS_SUCCESS;
358 }
359
360 VAStatus
vlVaBufferInfo(VADriverContextP ctx,VABufferID buf_id,VABufferType * type,unsigned int * size,unsigned int * num_elements)361 vlVaBufferInfo(VADriverContextP ctx, VABufferID buf_id, VABufferType *type,
362 unsigned int *size, unsigned int *num_elements)
363 {
364 vlVaDriver *drv;
365 vlVaBuffer *buf;
366
367 if (!ctx)
368 return VA_STATUS_ERROR_INVALID_CONTEXT;
369
370 drv = VL_VA_DRIVER(ctx);
371 mtx_lock(&drv->mutex);
372 buf = handle_table_get(drv->htab, buf_id);
373 mtx_unlock(&drv->mutex);
374 if (!buf)
375 return VA_STATUS_ERROR_INVALID_BUFFER;
376
377 *type = buf->type;
378 *size = buf->size;
379 *num_elements = buf->num_elements;
380
381 return VA_STATUS_SUCCESS;
382 }
383
384 VAStatus
vlVaAcquireBufferHandle(VADriverContextP ctx,VABufferID buf_id,VABufferInfo * out_buf_info)385 vlVaAcquireBufferHandle(VADriverContextP ctx, VABufferID buf_id,
386 VABufferInfo *out_buf_info)
387 {
388 vlVaDriver *drv;
389 uint32_t i;
390 uint32_t mem_type;
391 vlVaBuffer *buf ;
392 struct pipe_screen *screen;
393
394 /* List of supported memory types, in preferred order. */
395 static const uint32_t mem_types[] = {
396 #ifdef _WIN32
397 VA_SURFACE_ATTRIB_MEM_TYPE_NTHANDLE,
398 VA_SURFACE_ATTRIB_MEM_TYPE_D3D12_RESOURCE,
399 #else
400 VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME,
401 #endif
402 0
403 };
404
405 if (!ctx)
406 return VA_STATUS_ERROR_INVALID_CONTEXT;
407
408 drv = VL_VA_DRIVER(ctx);
409 screen = VL_VA_PSCREEN(ctx);
410 mtx_lock(&drv->mutex);
411 buf = handle_table_get(VL_VA_DRIVER(ctx)->htab, buf_id);
412 mtx_unlock(&drv->mutex);
413
414 if (!buf)
415 return VA_STATUS_ERROR_INVALID_BUFFER;
416
417 /* Only VA surface|image like buffers are supported for now .*/
418 if (buf->type != VAImageBufferType)
419 return VA_STATUS_ERROR_UNSUPPORTED_BUFFERTYPE;
420
421 if (!out_buf_info)
422 return VA_STATUS_ERROR_INVALID_PARAMETER;
423
424 if (!out_buf_info->mem_type)
425 mem_type = mem_types[0];
426 else {
427 mem_type = 0;
428 for (i = 0; mem_types[i] != 0; i++) {
429 if (out_buf_info->mem_type & mem_types[i]) {
430 mem_type = out_buf_info->mem_type;
431 break;
432 }
433 }
434 if (!mem_type)
435 return VA_STATUS_ERROR_UNSUPPORTED_MEMORY_TYPE;
436 }
437
438 if (!buf->derived_surface.resource)
439 return VA_STATUS_ERROR_INVALID_BUFFER;
440
441 if (buf->export_refcount > 0) {
442 if (buf->export_state.mem_type != mem_type)
443 return VA_STATUS_ERROR_INVALID_PARAMETER;
444 } else {
445 VABufferInfo * const buf_info = &buf->export_state;
446
447 switch (mem_type) {
448 #ifdef _WIN32
449 case VA_SURFACE_ATTRIB_MEM_TYPE_D3D12_RESOURCE:
450 case VA_SURFACE_ATTRIB_MEM_TYPE_NTHANDLE:
451 #else
452 case VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME:
453 #endif
454 {
455 struct winsys_handle whandle;
456
457 mtx_lock(&drv->mutex);
458 drv->pipe->flush(drv->pipe, NULL, 0);
459
460 memset(&whandle, 0, sizeof(whandle));
461 whandle.type = WINSYS_HANDLE_TYPE_FD;
462
463 #ifdef _WIN32
464 if (mem_type == VA_SURFACE_ATTRIB_MEM_TYPE_D3D12_RESOURCE)
465 whandle.type = WINSYS_HANDLE_TYPE_D3D12_RES;
466 #endif
467 if (!screen->resource_get_handle(screen, drv->pipe,
468 buf->derived_surface.resource,
469 &whandle, PIPE_HANDLE_USAGE_FRAMEBUFFER_WRITE)) {
470 mtx_unlock(&drv->mutex);
471 return VA_STATUS_ERROR_INVALID_BUFFER;
472 }
473
474 mtx_unlock(&drv->mutex);
475
476 buf_info->handle = (intptr_t)whandle.handle;
477
478 #ifdef _WIN32
479 if (mem_type == VA_SURFACE_ATTRIB_MEM_TYPE_D3D12_RESOURCE)
480 buf_info->handle = (intptr_t)whandle.com_obj;
481 #endif
482 break;
483 }
484 default:
485 return VA_STATUS_ERROR_UNSUPPORTED_MEMORY_TYPE;
486 }
487
488 buf_info->type = buf->type;
489 buf_info->mem_type = mem_type;
490 buf_info->mem_size = buf->num_elements * buf->size;
491 }
492
493 buf->export_refcount++;
494
495 *out_buf_info = buf->export_state;
496
497 return VA_STATUS_SUCCESS;
498 }
499
500 VAStatus
vlVaReleaseBufferHandle(VADriverContextP ctx,VABufferID buf_id)501 vlVaReleaseBufferHandle(VADriverContextP ctx, VABufferID buf_id)
502 {
503 vlVaDriver *drv;
504 vlVaBuffer *buf;
505
506 if (!ctx)
507 return VA_STATUS_ERROR_INVALID_CONTEXT;
508
509 drv = VL_VA_DRIVER(ctx);
510 mtx_lock(&drv->mutex);
511 buf = handle_table_get(drv->htab, buf_id);
512 mtx_unlock(&drv->mutex);
513
514 if (!buf)
515 return VA_STATUS_ERROR_INVALID_BUFFER;
516
517 if (buf->export_refcount == 0)
518 return VA_STATUS_ERROR_INVALID_BUFFER;
519
520 if (--buf->export_refcount == 0) {
521 VABufferInfo * const buf_info = &buf->export_state;
522
523 switch (buf_info->mem_type) {
524 #ifdef _WIN32
525 case VA_SURFACE_ATTRIB_MEM_TYPE_D3D12_RESOURCE:
526 // Do nothing for this case.
527 break;
528 case VA_SURFACE_ATTRIB_MEM_TYPE_NTHANDLE:
529 CloseHandle((HANDLE) buf_info->handle);
530 break;
531 #else
532 case VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME:
533 close((intptr_t)buf_info->handle);
534 break;
535 #endif
536 default:
537 return VA_STATUS_ERROR_INVALID_BUFFER;
538 }
539
540 buf_info->mem_type = 0;
541 }
542
543 return VA_STATUS_SUCCESS;
544 }
545
546 #if VA_CHECK_VERSION(1, 15, 0)
547 VAStatus
vlVaSyncBuffer(VADriverContextP ctx,VABufferID buf_id,uint64_t timeout_ns)548 vlVaSyncBuffer(VADriverContextP ctx, VABufferID buf_id, uint64_t timeout_ns)
549 {
550 vlVaDriver *drv;
551 vlVaContext *context;
552 vlVaBuffer *buf;
553
554 if (!ctx)
555 return VA_STATUS_ERROR_INVALID_CONTEXT;
556
557 drv = VL_VA_DRIVER(ctx);
558 if (!drv)
559 return VA_STATUS_ERROR_INVALID_CONTEXT;
560
561 mtx_lock(&drv->mutex);
562 buf = handle_table_get(drv->htab, buf_id);
563
564 if (!buf) {
565 mtx_unlock(&drv->mutex);
566 return VA_STATUS_ERROR_INVALID_BUFFER;
567 }
568
569 if (!buf->fence) {
570 /* No outstanding operation: nothing to do. */
571 mtx_unlock(&drv->mutex);
572 return VA_STATUS_SUCCESS;
573 }
574
575 context = buf->ctx;
576 if (!context) {
577 mtx_unlock(&drv->mutex);
578 return VA_STATUS_ERROR_INVALID_CONTEXT;
579 }
580
581 if (!context->decoder) {
582 mtx_unlock(&drv->mutex);
583 return VA_STATUS_ERROR_UNSUPPORTED_ENTRYPOINT;
584 }
585
586 int ret = context->decoder->fence_wait(context->decoder, buf->fence, timeout_ns);
587 mtx_unlock(&drv->mutex);
588 return ret ? VA_STATUS_SUCCESS : VA_STATUS_ERROR_TIMEDOUT;
589 }
590 #endif
591
vlVaGetBufferFeedback(vlVaBuffer * buf)592 void vlVaGetBufferFeedback(vlVaBuffer *buf)
593 {
594 if (!buf->ctx || !buf->ctx->decoder || !buf->feedback)
595 return;
596
597 buf->ctx->decoder->get_feedback(buf->ctx->decoder, buf->feedback,
598 &buf->coded_size, &buf->extended_metadata);
599 buf->feedback = NULL;
600 }
601