vah264enc: Avoid mapping coded buffer twice.
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / va / gstvaencoder.c
1 /* GStreamer
2  *  Copyright (C) 2021 Intel Corporation
3  *     Author: He Junyan <junyan.he@intel.com>
4  *     Author: Víctor Jáquez <vjaquez@igalia.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include "gstvaencoder.h"
27
28 #include "vacompat.h"
29 #include "gstvaallocator.h"
30 #include "gstvapool.h"
31 #include "gstvacaps.h"
32 #include "gstvadisplay_priv.h"
33 #include "gstvavideoformat.h"
34 #include <gst/va/gstvadisplay_wrapped.h>
35
36 #define VA_ENTRYPOINT_FLAG(entry) (1U << G_PASTE(VAEntrypoint, entry))
37
38 typedef struct _GstVaProfileConfig GstVaProfileConfig;
39 typedef struct _GstVaPackedHeader GstVaPackedHeader;
40 typedef struct _GstVaEncSlice GstVaEncSlice;
41 typedef struct _GstVaEncReconstruct GstVaEncReconstruct;
42
43 struct _GstVaProfileConfig
44 {
45   VAProfile profile;
46   guint32 entrypoints;          /* bits map of GstVaapiEntrypoint */
47 };
48
49 struct _GstVaEncoder
50 {
51   GstObject parent;
52
53   GArray *available_profiles;
54   GstCaps *srcpad_caps;
55   GstCaps *sinkpad_caps;
56   GstVaDisplay *display;
57   VAConfigID config;
58   VAContextID context;
59   VAProfile profile;
60   VAEntrypoint entrypoint;
61   guint rt_format;
62   gint coded_width;
63   gint coded_height;
64   gint codedbuf_size;
65
66   GstBufferPool *recon_pool;
67 };
68
69 GST_DEBUG_CATEGORY_STATIC (gst_va_encoder_debug);
70 #define GST_CAT_DEFAULT gst_va_encoder_debug
71
72 #define gst_va_encoder_parent_class parent_class
73 G_DEFINE_TYPE_WITH_CODE (GstVaEncoder, gst_va_encoder, GST_TYPE_OBJECT,
74     GST_DEBUG_CATEGORY_INIT (gst_va_encoder_debug, "vaencoder", 0,
75         "VA Encoder"));
76
77 enum
78 {
79   PROP_DISPLAY = 1,
80   PROP_PROFILE,
81   PROP_ENTRYPOINT,
82   PROP_WIDTH,
83   PROP_HEIGHT,
84   PROP_CHROMA,
85   PROP_CODED_BUF_SIZE,
86   N_PROPERTIES
87 };
88
89 static GParamSpec *g_properties[N_PROPERTIES];
90
91 static gboolean
92 _destroy_buffer (GstVaDisplay * display, VABufferID buffer)
93 {
94   VAStatus status;
95   gboolean ret = TRUE;
96   VADisplay dpy = gst_va_display_get_va_dpy (display);
97
98   status = vaDestroyBuffer (dpy, buffer);
99   if (status != VA_STATUS_SUCCESS) {
100     ret = FALSE;
101     GST_WARNING ("Failed to destroy the buffer: %s", vaErrorStr (status));
102   }
103
104   return ret;
105 }
106
107 static VABufferID
108 _create_buffer (GstVaEncoder * self, gint type, gpointer data, gsize size)
109 {
110   VAStatus status;
111   VADisplay dpy = gst_va_display_get_va_dpy (self->display);
112   VABufferID buffer;
113   VAContextID context;
114
115   GST_OBJECT_LOCK (self);
116   context = self->context;
117   GST_OBJECT_UNLOCK (self);
118
119   dpy = gst_va_display_get_va_dpy (self->display);
120   status = vaCreateBuffer (dpy, context, type, size, 1, data, &buffer);
121   if (status != VA_STATUS_SUCCESS) {
122     GST_ERROR_OBJECT (self, "vaCreateBuffer: %s", vaErrorStr (status));
123     return VA_INVALID_ID;
124   }
125
126   return buffer;
127 }
128
129 static void
130 gst_va_encoder_set_property (GObject * object, guint prop_id,
131     const GValue * value, GParamSpec * pspec)
132 {
133   GstVaEncoder *self = GST_VA_ENCODER (object);
134
135   GST_OBJECT_LOCK (self);
136
137   switch (prop_id) {
138     case PROP_DISPLAY:{
139       g_assert (!self->display);
140       self->display = g_value_dup_object (value);
141       break;
142     }
143     default:
144       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
145       break;
146   }
147
148   GST_OBJECT_UNLOCK (self);
149 }
150
151 static void
152 gst_va_encoder_get_property (GObject * object, guint prop_id, GValue * value,
153     GParamSpec * pspec)
154 {
155   GstVaEncoder *self = GST_VA_ENCODER (object);
156
157   GST_OBJECT_LOCK (self);
158
159   switch (prop_id) {
160     case PROP_DISPLAY:
161       g_value_set_object (value, self->display);
162       break;
163     case PROP_PROFILE:
164       g_value_set_int (value, self->profile);
165       break;
166     case PROP_ENTRYPOINT:
167       g_value_set_int (value, self->entrypoint);
168       break;
169     case PROP_CHROMA:
170       g_value_set_uint (value, self->rt_format);
171       break;
172     case PROP_WIDTH:
173       g_value_set_int (value, self->coded_width);
174       break;
175     case PROP_HEIGHT:
176       g_value_set_int (value, self->coded_height);
177       break;
178     case PROP_CODED_BUF_SIZE:
179       g_value_set_int (value, self->codedbuf_size);
180       break;
181     default:
182       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
183       break;
184   }
185
186   GST_OBJECT_UNLOCK (self);
187 }
188
189 static void
190 gst_va_encoder_init (GstVaEncoder * self)
191 {
192   self->profile = VAProfileNone;
193   self->entrypoint = 0;
194   self->config = VA_INVALID_ID;
195   self->context = VA_INVALID_ID;
196   self->rt_format = 0;
197   self->coded_width = 0;
198   self->coded_height = 0;
199   self->codedbuf_size = 0;
200   g_clear_pointer (&self->recon_pool, gst_object_unref);
201 }
202
203 static inline gboolean
204 _is_open_unlocked (GstVaEncoder * self)
205 {
206   return (self->config != VA_INVALID_ID && self->profile != VAProfileNone);
207 }
208
209 gboolean
210 gst_va_encoder_is_open (GstVaEncoder * self)
211 {
212   gboolean ret;
213
214   g_return_val_if_fail (GST_IS_VA_ENCODER (self), FALSE);
215
216   GST_OBJECT_LOCK (self);
217   ret = _is_open_unlocked (self);
218   GST_OBJECT_UNLOCK (self);
219   return ret;
220 }
221
222 gboolean
223 gst_va_encoder_close (GstVaEncoder * self)
224 {
225   VADisplay dpy;
226   VAStatus status;
227   VAConfigID config = VA_INVALID_ID;
228   VAContextID context = VA_INVALID_ID;
229   GstBufferPool *recon_pool = NULL;
230
231   g_return_val_if_fail (GST_IS_VA_ENCODER (self), FALSE);
232
233   GST_OBJECT_LOCK (self);
234   if (!_is_open_unlocked (self)) {
235     GST_OBJECT_UNLOCK (self);
236     return TRUE;
237   }
238
239   config = self->config;
240   context = self->context;
241
242   recon_pool = self->recon_pool;
243   self->recon_pool = NULL;
244
245   gst_va_encoder_init (self);
246   GST_OBJECT_UNLOCK (self);
247
248   gst_buffer_pool_set_active (recon_pool, FALSE);
249   g_clear_pointer (&recon_pool, gst_object_unref);
250
251   dpy = gst_va_display_get_va_dpy (self->display);
252
253   if (context != VA_INVALID_ID) {
254     status = vaDestroyContext (dpy, context);
255     if (status != VA_STATUS_SUCCESS)
256       GST_ERROR_OBJECT (self, "vaDestroyContext: %s", vaErrorStr (status));
257   }
258
259   status = vaDestroyConfig (dpy, config);
260   if (status != VA_STATUS_SUCCESS)
261     GST_ERROR_OBJECT (self, "vaDestroyConfig: %s", vaErrorStr (status));
262
263   gst_caps_replace (&self->srcpad_caps, NULL);
264   gst_caps_replace (&self->sinkpad_caps, NULL);
265
266   return TRUE;
267 }
268
269 static GArray *
270 _get_surface_formats (GstVaDisplay * display, VAConfigID config)
271 {
272   GArray *formats;
273   GstVideoFormat format;
274   VASurfaceAttrib *attribs;
275   guint i, attrib_count;
276
277   attribs = gst_va_get_surface_attribs (display, config, &attrib_count);
278   if (!attribs)
279     return NULL;
280
281   formats = g_array_new (FALSE, FALSE, sizeof (GstVideoFormat));
282
283   for (i = 0; i < attrib_count; i++) {
284     if (attribs[i].value.type != VAGenericValueTypeInteger)
285       continue;
286     switch (attribs[i].type) {
287       case VASurfaceAttribPixelFormat:
288         format = gst_va_video_format_from_va_fourcc (attribs[i].value.value.i);
289         if (format != GST_VIDEO_FORMAT_UNKNOWN)
290           g_array_append_val (formats, format);
291         break;
292       default:
293         break;
294     }
295   }
296
297   g_free (attribs);
298
299   if (formats->len == 0) {
300     g_array_unref (formats);
301     return NULL;
302   }
303
304   return formats;
305 }
306
307 static GstBufferPool *
308 _create_reconstruct_pool (GstVaDisplay * display, GArray * surface_formats,
309     GstVideoFormat format, gint coded_width, gint coded_height, guint max_num)
310 {
311   GstAllocator *allocator = NULL;
312   guint usage_hint = VA_SURFACE_ATTRIB_USAGE_HINT_ENCODER;
313   GstVideoInfo info;
314   GstAllocationParams params = { 0, };
315   GstBufferPool *pool;
316   guint size;
317   GstCaps *caps = NULL;
318
319   gst_video_info_set_format (&info, format, coded_width, coded_height);
320
321   size = GST_VIDEO_INFO_SIZE (&info);
322
323   caps = gst_video_info_to_caps (&info);
324   gst_caps_set_features_simple (caps,
325       gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_VA));
326
327   allocator = gst_va_allocator_new (display, surface_formats);
328
329   if (!gst_va_allocator_set_format (allocator, &info, usage_hint)) {
330     gst_clear_object (&allocator);
331     gst_clear_caps (&caps);
332     return NULL;
333   }
334
335   gst_allocation_params_init (&params);
336
337   pool = gst_va_pool_new_with_config (caps, size, 1, max_num,
338       usage_hint, allocator, &params);
339
340   gst_clear_object (&allocator);
341   gst_clear_caps (&caps);
342
343   return pool;
344 }
345
346 gboolean
347 gst_va_encoder_open (GstVaEncoder * self, VAProfile profile,
348     VAEntrypoint entrypoint, GstVideoFormat video_format, guint rt_format,
349     gint coded_width, gint coded_height, gint codedbuf_size,
350     guint reconstruct_buffer_num, guint rc_ctrl, guint32 packed_headers)
351 {
352   /* *INDENT-OFF* */
353   VAConfigAttrib attribs[3] = {
354     { .type = VAConfigAttribRTFormat, .value = rt_format, },
355     { .type = VAConfigAttribRateControl, .value = rc_ctrl, },
356   };
357   /* *INDENT-ON* */
358   VAConfigID config = VA_INVALID_ID;
359   VAContextID context = VA_INVALID_ID;
360   VADisplay dpy;
361   GPtrArray *reconstruct_buffers = NULL;
362   GArray *surfaces = NULL;
363   GArray *surface_formats = NULL;
364   VAStatus status;
365   GstBufferPool *recon_pool = NULL;
366   guint i, attrib_idx = 2;
367
368   g_return_val_if_fail (GST_IS_VA_ENCODER (self), FALSE);
369   g_return_val_if_fail (reconstruct_buffer_num > 0, FALSE);
370   g_return_val_if_fail (codedbuf_size > 0, FALSE);
371
372   if (gst_va_encoder_is_open (self))
373     return TRUE;
374
375   if (!gst_va_encoder_has_profile_and_entrypoint (self, profile, entrypoint)) {
376     GST_ERROR_OBJECT (self, "Unsupported profile: %d, entrypoint: %d",
377         profile, entrypoint);
378     return FALSE;
379   }
380
381   if (packed_headers > 0) {
382     attribs[attrib_idx].type = VAConfigAttribEncPackedHeaders;
383     attribs[attrib_idx].value = packed_headers;
384     attrib_idx++;
385   }
386
387   dpy = gst_va_display_get_va_dpy (self->display);
388
389   status = vaCreateConfig (dpy, profile, entrypoint, attribs, attrib_idx,
390       &config);
391   if (status != VA_STATUS_SUCCESS) {
392     GST_ERROR_OBJECT (self, "vaCreateConfig: %s", vaErrorStr (status));
393     goto error;
394   }
395
396   surface_formats = _get_surface_formats (self->display, config);
397   if (!surface_formats) {
398     GST_ERROR_OBJECT (self, "Failed to get surface formats");
399     goto error;
400   }
401
402   recon_pool = _create_reconstruct_pool (self->display, surface_formats,
403       video_format, coded_width, coded_height, reconstruct_buffer_num);
404   if (!recon_pool) {
405     GST_ERROR_OBJECT (self, "Failed to create reconstruct pool");
406     goto error;
407   }
408   gst_buffer_pool_set_active (recon_pool, TRUE);
409
410   /* Create VA surfaces list for vaCreateContext() */
411   surfaces = g_array_sized_new (FALSE, FALSE, sizeof (VASurfaceID),
412       reconstruct_buffer_num);
413   if (!surfaces)
414     goto error;
415
416   reconstruct_buffers = g_ptr_array_sized_new (reconstruct_buffer_num);
417   if (!reconstruct_buffers)
418     goto error;
419
420   g_ptr_array_set_free_func (reconstruct_buffers,
421       (GDestroyNotify) gst_buffer_unref);
422
423   /* The encoder need to binding all reconstruct surface when create contex,
424      we have to allocate them all here. */
425   for (i = 0; i < reconstruct_buffer_num; i++) {
426     GstBuffer *buffer;
427     VASurfaceID surface_id;
428     GstFlowReturn ret;
429     GstBufferPoolAcquireParams buffer_pool_params = { 0, };
430
431     buffer_pool_params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
432     ret = gst_buffer_pool_acquire_buffer (recon_pool, &buffer,
433         &buffer_pool_params);
434     if (ret != GST_FLOW_OK) {
435       GST_ERROR_OBJECT (self, "Failed to create the reconstruct picture.");
436       goto error;
437     }
438
439     surface_id = gst_va_buffer_get_surface (buffer);
440     g_assert (surface_id != VA_INVALID_ID);
441
442     g_ptr_array_add (reconstruct_buffers, buffer);
443     g_array_append_val (surfaces, surface_id);
444   }
445
446   g_assert (surfaces->len == reconstruct_buffer_num);
447
448   status = vaCreateContext (dpy, config, coded_width, coded_height,
449       VA_PROGRESSIVE, (VASurfaceID *) surfaces->data, reconstruct_buffer_num,
450       &context);
451   if (status != VA_STATUS_SUCCESS) {
452     GST_ERROR_OBJECT (self, "vaCreateConfig: %s", vaErrorStr (status));
453     goto error;
454   }
455
456   g_clear_pointer (&surfaces, g_array_unref);
457   g_clear_pointer (&reconstruct_buffers, g_ptr_array_unref);
458
459   GST_OBJECT_LOCK (self);
460
461   self->config = config;
462   self->context = context;
463   self->profile = profile;
464   self->entrypoint = entrypoint;
465   self->rt_format = rt_format;
466   self->coded_width = coded_width;
467   self->coded_height = coded_height;
468   self->codedbuf_size = codedbuf_size;
469   gst_object_replace ((GstObject **) & self->recon_pool,
470       (GstObject *) recon_pool);
471
472   GST_OBJECT_UNLOCK (self);
473
474   g_clear_pointer (&recon_pool, gst_object_unref);
475   /* now we should return now only this profile's caps */
476   gst_caps_replace (&self->srcpad_caps, NULL);
477
478   return TRUE;
479
480 error:
481   g_clear_pointer (&surfaces, g_array_unref);
482   g_clear_pointer (&reconstruct_buffers, g_ptr_array_unref);
483   g_clear_pointer (&recon_pool, gst_object_unref);
484
485   if (config == VA_INVALID_ID)
486     vaDestroyConfig (dpy, config);
487
488   if (context == VA_INVALID_ID)
489     vaDestroyContext (dpy, context);
490
491   return FALSE;
492 }
493
494 static void
495 gst_va_encoder_dispose (GObject * object)
496 {
497   GstVaEncoder *self = GST_VA_ENCODER (object);
498
499   gst_va_encoder_close (self);
500
501   g_clear_pointer (&self->available_profiles, g_array_unref);
502   gst_clear_object (&self->display);
503
504   G_OBJECT_CLASS (parent_class)->dispose (object);
505 }
506
507 static void
508 gst_va_encoder_class_init (GstVaEncoderClass * klass)
509 {
510   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
511
512   gobject_class->set_property = gst_va_encoder_set_property;
513   gobject_class->get_property = gst_va_encoder_get_property;
514   gobject_class->dispose = gst_va_encoder_dispose;
515
516   g_properties[PROP_DISPLAY] =
517       g_param_spec_object ("display", "GstVaDisplay", "GstVADisplay object",
518       GST_TYPE_VA_DISPLAY,
519       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
520
521   g_properties[PROP_PROFILE] =
522       g_param_spec_int ("va-profile", "VAProfile", "VA Profile",
523       VAProfileNone, 50, VAProfileNone,
524       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
525
526   g_properties[PROP_ENTRYPOINT] =
527       g_param_spec_int ("va-entrypoint", "VAEntrypoint", "VA Entrypoint",
528       0, 14, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
529
530   g_properties[PROP_CHROMA] =
531       g_param_spec_uint ("va-rt-format", "VARTFormat", "VA RT Fromat or chroma",
532       VA_RT_FORMAT_YUV420, VA_RT_FORMAT_PROTECTED, VA_RT_FORMAT_YUV420,
533       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
534
535   g_properties[PROP_WIDTH] =
536       g_param_spec_int ("coded-width", "coded-picture-width",
537       "coded picture width", 0, G_MAXINT, 0,
538       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
539
540   g_properties[PROP_HEIGHT] =
541       g_param_spec_int ("coded-height", "coded-picture-height",
542       "coded picture height", 0, G_MAXINT, 0,
543       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
544
545   g_properties[PROP_CODED_BUF_SIZE] =
546       g_param_spec_int ("coded-buf-size", "coded-buffer-size",
547       "coded buffer size", 0, G_MAXINT, 0,
548       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
549
550   g_object_class_install_properties (gobject_class, N_PROPERTIES, g_properties);
551 }
552
553 static gboolean
554 gst_va_encoder_initialize (GstVaEncoder * self, guint32 codec)
555 {
556   GArray *enc_profiles = NULL;
557   gint i;
558
559   if (self->available_profiles)
560     return FALSE;
561
562   enc_profiles = gst_va_display_get_profiles (self->display, codec,
563       VAEntrypointEncSlice);
564
565   if (!enc_profiles)
566     return FALSE;
567
568   self->available_profiles =
569       g_array_new (FALSE, FALSE, sizeof (GstVaProfileConfig));
570
571   if (enc_profiles) {
572     for (i = 0; i < enc_profiles->len; i++) {
573       GstVaProfileConfig config;
574
575       config.profile = g_array_index (enc_profiles, VAProfile, i);
576       config.entrypoints = VA_ENTRYPOINT_FLAG (EncSlice);
577       g_array_append_val (self->available_profiles, config);
578     }
579   }
580
581   g_clear_pointer (&enc_profiles, g_array_unref);
582
583   if (self->available_profiles->len == 0)
584     return FALSE;
585
586   return TRUE;
587 }
588
589 GstVaEncoder *
590 gst_va_encoder_new (GstVaDisplay * display, guint32 codec)
591 {
592   GstVaEncoder *self;
593
594   g_return_val_if_fail (GST_IS_VA_DISPLAY (display), NULL);
595
596   self = g_object_new (GST_TYPE_VA_ENCODER, "display", display, NULL);
597   if (!gst_va_encoder_initialize (self, codec))
598     gst_clear_object (&self);
599
600   return self;
601 }
602
603 gboolean
604 gst_va_encoder_has_profile_and_entrypoint (GstVaEncoder * self,
605     VAProfile profile, VAEntrypoint entrypoint)
606 {
607   GstVaProfileConfig *config;
608   gint i;
609
610   g_return_val_if_fail (GST_IS_VA_ENCODER (self), FALSE);
611
612   if (profile == VAProfileNone)
613     return FALSE;
614
615   for (i = 0; i < self->available_profiles->len; i++) {
616     config = &g_array_index (self->available_profiles, GstVaProfileConfig, i);
617     if (config->profile == profile) {
618       if (entrypoint == 0)
619         break;
620
621       if (config->entrypoints & (1U << entrypoint))
622         break;
623     }
624   }
625   if (i == self->available_profiles->len)
626     return FALSE;
627
628   return TRUE;
629 }
630
631 gint32
632 gst_va_encoder_get_max_slice_num (GstVaEncoder * self,
633     VAProfile profile, VAEntrypoint entrypoint)
634 {
635   VAStatus status;
636   VADisplay dpy;
637   VAConfigAttrib attrib = {.type = VAConfigAttribEncMaxSlices };
638
639   g_return_val_if_fail (GST_IS_VA_ENCODER (self), -1);
640
641   if (profile == VAProfileNone)
642     return -1;
643
644   if (entrypoint != VAEntrypointEncSlice)
645     return -1;
646
647   dpy = gst_va_display_get_va_dpy (self->display);
648   status = vaGetConfigAttributes (dpy, profile, entrypoint, &attrib, 1);
649   if (status != VA_STATUS_SUCCESS) {
650     GST_WARNING_OBJECT (self, "Failed to query encoding slices: %s",
651         vaErrorStr (status));
652     return -1;
653   }
654
655   if (attrib.value == VA_ATTRIB_NOT_SUPPORTED) {
656     GST_WARNING_OBJECT (self, "Driver does not support encoding picture as "
657         "multiple slices");
658     return -1;
659   }
660
661   return attrib.value;
662 }
663
664 gboolean
665 gst_va_encoder_get_max_num_reference (GstVaEncoder * self,
666     VAProfile profile, VAEntrypoint entrypoint,
667     guint32 * list0, guint32 * list1)
668 {
669   VAStatus status;
670   VADisplay dpy;
671   VAConfigAttrib attrib = {.type = VAConfigAttribEncMaxRefFrames };
672
673   g_return_val_if_fail (GST_IS_VA_ENCODER (self), FALSE);
674
675   if (profile == VAProfileNone)
676     return FALSE;
677
678   if (entrypoint != VAEntrypointEncSlice)
679     return FALSE;
680
681   dpy = gst_va_display_get_va_dpy (self->display);
682   status = vaGetConfigAttributes (dpy, profile, entrypoint, &attrib, 1);
683   if (status != VA_STATUS_SUCCESS) {
684     GST_WARNING_OBJECT (self, "Failed to query reference frames: %s",
685         vaErrorStr (status));
686     return FALSE;
687   }
688
689   if (attrib.value == VA_ATTRIB_NOT_SUPPORTED) {
690     if (list0)
691       *list0 = 0;
692     if (list1)
693       *list1 = 0;
694
695     return TRUE;
696   }
697
698   if (list0)
699     *list0 = attrib.value & 0xffff;
700   if (list1)
701     *list1 = (attrib.value >> 16) & 0xffff;
702
703   return TRUE;
704 }
705
706 guint32
707 gst_va_encoder_get_rate_control_mode (GstVaEncoder * self,
708     VAProfile profile, VAEntrypoint entrypoint)
709 {
710   VAStatus status;
711   VADisplay dpy;
712   VAConfigAttrib attrib = {.type = VAConfigAttribRateControl };
713
714   g_return_val_if_fail (GST_IS_VA_ENCODER (self), 0);
715
716   if (profile == VAProfileNone)
717     return 0;
718
719   if (entrypoint != VAEntrypointEncSlice)
720     return 0;
721
722   dpy = gst_va_display_get_va_dpy (self->display);
723   status = vaGetConfigAttributes (dpy, profile, entrypoint, &attrib, 1);
724   if (status != VA_STATUS_SUCCESS) {
725     GST_WARNING_OBJECT (self, "Failed to query rate control mode: %s",
726         vaErrorStr (status));
727     return 0;
728   }
729
730   if (attrib.value == VA_ATTRIB_NOT_SUPPORTED) {
731     GST_WARNING_OBJECT (self, "Driver does not support any rate control modes");
732     return 0;
733   }
734
735   return attrib.value;
736 }
737
738 guint32
739 gst_va_encoder_get_quality_level (GstVaEncoder * self,
740     VAProfile profile, VAEntrypoint entrypoint)
741 {
742   VAStatus status;
743   VADisplay dpy;
744   VAConfigAttrib attrib = {.type = VAConfigAttribEncQualityRange };
745
746   g_return_val_if_fail (GST_IS_VA_ENCODER (self), 0);
747
748   if (profile == VAProfileNone)
749     return 0;
750
751   if (entrypoint != VAEntrypointEncSlice)
752     return 0;
753
754   dpy = gst_va_display_get_va_dpy (self->display);
755   status = vaGetConfigAttributes (dpy, profile, entrypoint, &attrib, 1);
756   if (status != VA_STATUS_SUCCESS) {
757     GST_WARNING_OBJECT (self, "Failed to query the quality level: %s",
758         vaErrorStr (status));
759     return 0;
760   }
761
762   if (attrib.value == VA_ATTRIB_NOT_SUPPORTED) {
763     GST_WARNING_OBJECT (self, "Driver does not support quality attribute");
764     return 0;
765   }
766
767   return attrib.value;
768 }
769
770 gboolean
771 gst_va_encoder_has_trellis (GstVaEncoder * self,
772     VAProfile profile, VAEntrypoint entrypoint)
773 {
774   VAStatus status;
775   VADisplay dpy;
776   VAConfigAttrib attrib = {.type = VAConfigAttribEncQuantization };
777
778   g_return_val_if_fail (GST_IS_VA_ENCODER (self), FALSE);
779
780   if (profile == VAProfileNone)
781     return FALSE;
782
783   if (entrypoint != VAEntrypointEncSlice)
784     return FALSE;
785
786   dpy = gst_va_display_get_va_dpy (self->display);
787   status = vaGetConfigAttributes (dpy, profile, entrypoint, &attrib, 1);
788   if (status != VA_STATUS_SUCCESS) {
789     GST_WARNING_OBJECT (self, "Failed to query the trellis: %s",
790         vaErrorStr (status));
791     return FALSE;
792   }
793
794   if (attrib.value == VA_ATTRIB_NOT_SUPPORTED) {
795     GST_WARNING_OBJECT (self, "Driver does not support trellis");
796     return FALSE;
797   }
798
799   return attrib.value & VA_ENC_QUANTIZATION_TRELLIS_SUPPORTED;
800 }
801
802 guint32
803 gst_va_encoder_get_rtformat (GstVaEncoder * self,
804     VAProfile profile, VAEntrypoint entrypoint)
805 {
806   VAStatus status;
807   VADisplay dpy;
808   VAConfigAttrib attrib = {.type = VAConfigAttribRTFormat };
809
810   if (profile == VAProfileNone)
811     return 0;
812
813   if (entrypoint != VAEntrypointEncSlice)
814     return 0;
815
816   dpy = gst_va_display_get_va_dpy (self->display);
817   status = vaGetConfigAttributes (dpy, profile, entrypoint, &attrib, 1);
818   if (status != VA_STATUS_SUCCESS) {
819     GST_ERROR_OBJECT (self, "Failed to query rt format: %s",
820         vaErrorStr (status));
821     return 0;
822   }
823
824   if (attrib.value == VA_ATTRIB_NOT_SUPPORTED) {
825     GST_WARNING_OBJECT (self, "Driver does not support any rt format");
826     return 0;
827   }
828
829   return attrib.value;
830 }
831
832 guint32
833 gst_va_encoder_get_packed_headers (GstVaEncoder * self, VAProfile profile,
834     VAEntrypoint entrypoint)
835 {
836   VAStatus status;
837   VADisplay dpy;
838   VAConfigAttrib attrib = {.type = VAConfigAttribEncPackedHeaders };
839
840   if (profile == VAProfileNone)
841     return 0;
842
843   if (entrypoint != VAEntrypointEncSlice)
844     return 0;
845
846   dpy = gst_va_display_get_va_dpy (self->display);
847   status = vaGetConfigAttributes (dpy, profile, entrypoint, &attrib, 1);
848   if (status != VA_STATUS_SUCCESS) {
849     GST_ERROR_OBJECT (self, "Failed to query packed headers: %s",
850         vaErrorStr (status));
851     return 0;
852   }
853
854   if (attrib.value == VA_ATTRIB_NOT_SUPPORTED) {
855     GST_WARNING_OBJECT (self, "Driver does not support any packed headers");
856     return 0;
857   }
858
859   return attrib.value;
860 }
861
862 /* Add packed header such as SPS, PPS, SEI, etc. If adding slice header,
863    it is attached to the last slice parameter. */
864 gboolean
865 gst_va_encoder_add_packed_header (GstVaEncoder * self, GstVaEncodePicture * pic,
866     gint type, gpointer data, gsize size_in_bits, gboolean has_emulation_bytes)
867 {
868   VABufferID buffer;
869   VAEncPackedHeaderParameterBuffer param = {
870     .type = type,
871     .bit_length = size_in_bits,
872     .has_emulation_bytes = has_emulation_bytes,
873   };
874
875   g_return_val_if_fail (GST_IS_VA_ENCODER (self), FALSE);
876   g_return_val_if_fail (self->context != VA_INVALID_ID, FALSE);
877   g_return_val_if_fail (pic && data && size_in_bits > 0, FALSE);
878   g_return_val_if_fail (type >= VAEncPackedHeaderSequence
879       && type <= VAEncPackedHeaderRawData, FALSE);
880
881   if (!gst_va_encoder_is_open (self)) {
882     GST_ERROR_OBJECT (self, "encoder has not been opened yet");
883     return FALSE;
884   }
885
886   buffer = _create_buffer (self, VAEncPackedHeaderParameterBufferType, &param,
887       sizeof (param));
888   if (buffer == VA_INVALID_ID)
889     return FALSE;
890
891   g_array_append_val (pic->params, buffer);
892
893   buffer = _create_buffer (self, VAEncPackedHeaderDataBufferType, data,
894       (size_in_bits + 7) / 8);
895   if (buffer == VA_INVALID_ID)
896     return FALSE;
897
898   g_array_append_val (pic->params, buffer);
899
900   return TRUE;
901 }
902
903 gboolean
904 gst_va_encoder_add_param (GstVaEncoder * self, GstVaEncodePicture * pic,
905     VABufferType type, gpointer data, gsize size)
906 {
907   VABufferID buffer;
908
909   g_return_val_if_fail (GST_IS_VA_ENCODER (self), FALSE);
910   g_return_val_if_fail (self->context != VA_INVALID_ID, FALSE);
911   g_return_val_if_fail (pic && data && size > 0, FALSE);
912
913   if (!gst_va_encoder_is_open (self)) {
914     GST_ERROR_OBJECT (self, "encoder has not been opened yet");
915     return FALSE;
916   }
917
918   buffer = _create_buffer (self, type, data, size);
919   if (buffer == VA_INVALID_ID)
920     return FALSE;
921
922   g_array_append_val (pic->params, buffer);
923
924   return TRUE;
925 }
926
927 GArray *
928 gst_va_encoder_get_surface_formats (GstVaEncoder * self)
929 {
930   g_return_val_if_fail (GST_IS_VA_ENCODER (self), NULL);
931
932   if (!gst_va_encoder_is_open (self))
933     return NULL;
934
935   return _get_surface_formats (self->display, self->config);
936 }
937
938 static gboolean
939 _get_codec_caps (GstVaEncoder * self)
940 {
941   GstCaps *sinkpad_caps = NULL, *srcpad_caps = NULL;
942
943   if (!gst_va_encoder_is_open (self)
944       && GST_IS_VA_DISPLAY_WRAPPED (self->display)) {
945     if (gst_va_caps_from_profiles (self->display, self->available_profiles,
946             VAEntrypointEncSlice, &sinkpad_caps, &srcpad_caps)) {
947       gst_caps_replace (&self->sinkpad_caps, sinkpad_caps);
948       gst_caps_replace (&self->srcpad_caps, srcpad_caps);
949       gst_caps_unref (srcpad_caps);
950       gst_caps_unref (sinkpad_caps);
951
952       return TRUE;
953     }
954   }
955
956   return FALSE;
957 }
958
959 GstCaps *
960 gst_va_encoder_get_sinkpad_caps (GstVaEncoder * self)
961 {
962   GstCaps *sinkpad_caps = NULL;
963
964   g_return_val_if_fail (GST_IS_VA_ENCODER (self), FALSE);
965
966   if (g_atomic_pointer_get (&self->sinkpad_caps))
967     return gst_caps_ref (self->sinkpad_caps);
968
969   if (_get_codec_caps (self))
970     return gst_caps_ref (self->sinkpad_caps);
971
972   if (gst_va_encoder_is_open (self)) {
973     sinkpad_caps = gst_va_create_raw_caps_from_config (self->display,
974         self->config);
975     gst_caps_replace (&self->sinkpad_caps, sinkpad_caps);
976     gst_caps_unref (sinkpad_caps);
977
978     return gst_caps_ref (self->sinkpad_caps);
979   }
980
981   return NULL;
982 }
983
984 GstCaps *
985 gst_va_encoder_get_srcpad_caps (GstVaEncoder * self)
986 {
987   g_return_val_if_fail (GST_IS_VA_ENCODER (self), FALSE);
988
989   if (g_atomic_pointer_get (&self->srcpad_caps))
990     return gst_caps_ref (self->srcpad_caps);
991
992   if (_get_codec_caps (self))
993     return gst_caps_ref (self->srcpad_caps);
994
995   if (gst_va_encoder_is_open (self)) {
996     VAProfile profile;
997     VAEntrypoint entrypoint;
998     GstCaps *caps;
999
1000     GST_OBJECT_LOCK (self);
1001     profile = self->profile;
1002     entrypoint = self->entrypoint;
1003     GST_OBJECT_UNLOCK (self);
1004
1005     caps = gst_va_create_coded_caps (self->display, profile, entrypoint, NULL);
1006     if (caps) {
1007       gst_caps_replace (&self->srcpad_caps, caps);
1008       return gst_caps_ref (self->srcpad_caps);
1009     }
1010   }
1011
1012   return NULL;
1013 }
1014
1015 static gboolean
1016 _destroy_all_buffers (GstVaEncodePicture * pic)
1017 {
1018   VABufferID buffer;
1019   guint i;
1020   gboolean ret = TRUE;
1021
1022   g_return_val_if_fail (GST_IS_VA_DISPLAY (pic->display), FALSE);
1023
1024   for (i = 0; i < pic->params->len; i++) {
1025     buffer = g_array_index (pic->params, VABufferID, i);
1026     ret &= _destroy_buffer (pic->display, buffer);
1027   }
1028   pic->params = g_array_set_size (pic->params, 0);
1029
1030   return ret;
1031 }
1032
1033 gboolean
1034 gst_va_encoder_encode (GstVaEncoder * self, GstVaEncodePicture * pic)
1035 {
1036   VADisplay dpy;
1037   VAStatus status;
1038   VASurfaceID surface;
1039   VAContextID context;
1040   gboolean ret = FALSE;
1041
1042   g_return_val_if_fail (pic, FALSE);
1043
1044   GST_OBJECT_LOCK (self);
1045
1046   if (!_is_open_unlocked (self)) {
1047     GST_OBJECT_UNLOCK (self);
1048     GST_ERROR_OBJECT (self, "encoder has not been opened yet");
1049     return FALSE;
1050   }
1051
1052   context = self->context;
1053   GST_OBJECT_UNLOCK (self);
1054
1055   surface = gst_va_encode_picture_get_raw_surface (pic);
1056   if (surface == VA_INVALID_ID) {
1057     GST_ERROR_OBJECT (self, "Encode picture without valid raw surface");
1058     goto bail;
1059   }
1060
1061   GST_TRACE_OBJECT (self, "Encode the surface %#x", surface);
1062
1063   dpy = gst_va_display_get_va_dpy (self->display);
1064
1065   status = vaBeginPicture (dpy, context, surface);
1066   if (status != VA_STATUS_SUCCESS) {
1067     GST_WARNING_OBJECT (self, "vaBeginPicture: %s", vaErrorStr (status));
1068     goto bail;
1069   }
1070
1071   if (pic->params->len > 0) {
1072     status = vaRenderPicture (dpy, context, (VABufferID *) pic->params->data,
1073         pic->params->len);
1074     if (status != VA_STATUS_SUCCESS) {
1075       GST_WARNING_OBJECT (self, "vaRenderPicture: %s", vaErrorStr (status));
1076       goto fail_end_pic;
1077     }
1078   }
1079
1080   status = vaEndPicture (dpy, context);
1081   ret = (status == VA_STATUS_SUCCESS);
1082   if (!ret)
1083     GST_WARNING_OBJECT (self, "vaEndPicture: %s", vaErrorStr (status));
1084
1085 bail:
1086   _destroy_all_buffers (pic);
1087
1088   return ret;
1089
1090 fail_end_pic:
1091   {
1092     _destroy_all_buffers (pic);
1093     status = vaEndPicture (dpy, context);
1094     ret = FALSE;
1095     goto bail;
1096   }
1097 }
1098
1099 VASurfaceID
1100 gst_va_encode_picture_get_reconstruct_surface (GstVaEncodePicture * pic)
1101 {
1102   g_return_val_if_fail (pic, VA_INVALID_ID);
1103   g_return_val_if_fail (pic->reconstruct_buffer, VA_INVALID_ID);
1104
1105   return gst_va_buffer_get_surface (pic->reconstruct_buffer);
1106 }
1107
1108 VASurfaceID
1109 gst_va_encode_picture_get_raw_surface (GstVaEncodePicture * pic)
1110 {
1111   g_return_val_if_fail (pic, VA_INVALID_ID);
1112   g_return_val_if_fail (pic->raw_buffer, VA_INVALID_ID);
1113
1114   return gst_va_buffer_get_surface (pic->raw_buffer);
1115 }
1116
1117 GstVaEncodePicture *
1118 gst_va_encode_picture_new (GstVaEncoder * self, GstBuffer * raw_buffer)
1119 {
1120   GstVaEncodePicture *pic;
1121   VABufferID coded_buffer;
1122   VADisplay dpy;
1123   VAStatus status;
1124   gint codedbuf_size;
1125   GstBufferPool *recon_pool = NULL;
1126   GstBuffer *reconstruct_buffer = NULL;
1127   GstFlowReturn ret;
1128   GstBufferPoolAcquireParams buffer_pool_params = {
1129     .flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT,
1130   };
1131
1132   g_return_val_if_fail (self && GST_IS_VA_ENCODER (self), NULL);
1133   g_return_val_if_fail (raw_buffer && GST_IS_BUFFER (raw_buffer), NULL);
1134
1135   GST_OBJECT_LOCK (self);
1136
1137   if (!_is_open_unlocked (self)) {
1138     GST_OBJECT_UNLOCK (self);
1139     GST_ERROR_OBJECT (self, "encoder has not been opened yet");
1140     return NULL;
1141   }
1142
1143   if (self->codedbuf_size <= 0) {
1144     GST_ERROR_OBJECT (self, "codedbuf_size: %d, is invalid",
1145         self->codedbuf_size);
1146     GST_OBJECT_UNLOCK (self);
1147     return NULL;
1148   }
1149   codedbuf_size = self->codedbuf_size;
1150
1151   recon_pool = gst_object_ref (self->recon_pool);
1152
1153   GST_OBJECT_UNLOCK (self);
1154
1155   ret = gst_buffer_pool_acquire_buffer (self->recon_pool, &reconstruct_buffer,
1156       &buffer_pool_params);
1157   gst_clear_object (&recon_pool);
1158
1159   if (ret != GST_FLOW_OK) {
1160     GST_ERROR_OBJECT (self, "Failed to create the reconstruct picture");
1161     gst_clear_buffer (&reconstruct_buffer);
1162     return NULL;
1163   }
1164
1165   dpy = gst_va_display_get_va_dpy (self->display);
1166   status = vaCreateBuffer (dpy, self->context, VAEncCodedBufferType,
1167       codedbuf_size, 1, NULL, &coded_buffer);
1168   if (status != VA_STATUS_SUCCESS) {
1169     GST_ERROR_OBJECT (self, "vaCreateBuffer: %s", vaErrorStr (status));
1170     gst_clear_buffer (&reconstruct_buffer);
1171     return NULL;
1172   }
1173
1174   pic = g_slice_new (GstVaEncodePicture);
1175   pic->raw_buffer = gst_buffer_ref (raw_buffer);
1176   pic->reconstruct_buffer = reconstruct_buffer;
1177   pic->display = gst_object_ref (self->display);
1178   pic->coded_buffer = coded_buffer;
1179
1180   pic->params = g_array_sized_new (FALSE, FALSE, sizeof (VABufferID), 8);
1181
1182   return pic;
1183 }
1184
1185 void
1186 gst_va_encode_picture_free (GstVaEncodePicture * pic)
1187 {
1188   g_return_if_fail (pic);
1189
1190   _destroy_all_buffers (pic);
1191
1192   if (pic->coded_buffer != VA_INVALID_ID)
1193     _destroy_buffer (pic->display, pic->coded_buffer);
1194
1195   gst_buffer_unref (pic->raw_buffer);
1196   gst_buffer_unref (pic->reconstruct_buffer);
1197
1198   g_clear_pointer (&pic->params, g_array_unref);
1199   gst_clear_object (&pic->display);
1200
1201   g_slice_free (GstVaEncodePicture, pic);
1202 }