context: clean-ups. Strip down APIs.
[vaapi:gstreamer-vaapi.git] / gst-libs / gst / vaapi / gstvaapiencoder.c
1 /*
2  *  gstvaapiencoder.c - VA encoder abstraction
3  *
4  *  Copyright (C) 2013-2014 Intel Corporation
5  *    Author: Wind Yuan <feng.yuan@intel.com>
6  *    Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Lesser General Public License
10  *  as published by the Free Software Foundation; either version 2.1
11  *  of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  Lesser General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Lesser General Public
19  *  License along with this library; if not, write to the Free
20  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  *  Boston, MA 02110-1301 USA
22  */
23
24 #include "sysdeps.h"
25 #include "gstvaapicompat.h"
26 #include "gstvaapiencoder.h"
27 #include "gstvaapiencoder_priv.h"
28 #include "gstvaapicontext.h"
29 #include "gstvaapidisplay_priv.h"
30 #include "gstvaapiutils.h"
31 #include "gstvaapivalue.h"
32
33 #define DEBUG 1
34 #include "gstvaapidebug.h"
35
36 /* Helper function to create a new encoder property object */
37 static GstVaapiEncoderPropData *
38 prop_new (gint id, GParamSpec * pspec)
39 {
40   GstVaapiEncoderPropData *prop;
41
42   if (!id || !pspec)
43     return NULL;
44
45   prop = g_slice_new (GstVaapiEncoderPropData);
46   if (!prop)
47     return NULL;
48
49   prop->prop = id;
50   prop->pspec = g_param_spec_ref_sink (pspec);
51   return prop;
52 }
53
54 /* Helper function to release a property object and any memory held herein */
55 static void
56 prop_free (GstVaapiEncoderPropData * prop)
57 {
58   if (!prop)
59     return;
60
61   if (prop->pspec) {
62     g_param_spec_unref (prop->pspec);
63     prop->pspec = NULL;
64   }
65   g_slice_free (GstVaapiEncoderPropData, prop);
66 }
67
68 /* Helper function to lookup the supplied property specification */
69 static GParamSpec *
70 prop_find_pspec (GstVaapiEncoder * encoder, gint prop_id)
71 {
72   GPtrArray *const props = encoder->properties;
73   guint i;
74
75   if (props) {
76     for (i = 0; i < props->len; i++) {
77       GstVaapiEncoderPropInfo *const prop = g_ptr_array_index (props, i);
78       if (prop->prop == prop_id)
79         return prop->pspec;
80     }
81   }
82   return NULL;
83 }
84
85 /* Create a new array of properties, or NULL on error */
86 GPtrArray *
87 gst_vaapi_encoder_properties_append (GPtrArray * props, gint prop_id,
88     GParamSpec * pspec)
89 {
90   GstVaapiEncoderPropData *prop;
91
92   if (!props) {
93     props = g_ptr_array_new_with_free_func ((GDestroyNotify) prop_free);
94     if (!props)
95       return NULL;
96   }
97
98   prop = prop_new (prop_id, pspec);
99   if (!prop)
100     goto error_allocation_failed;
101   g_ptr_array_add (props, prop);
102   return props;
103
104   /* ERRORS */
105 error_allocation_failed:
106   {
107     GST_ERROR ("failed to allocate encoder property info structure");
108     g_ptr_array_unref (props);
109     return NULL;
110   }
111 }
112
113 /* Generate the common set of encoder properties */
114 GPtrArray *
115 gst_vaapi_encoder_properties_get_default (const GstVaapiEncoderClass * klass)
116 {
117   const GstVaapiEncoderClassData *const cdata = klass->class_data;
118   GPtrArray *props = NULL;
119
120   g_assert (cdata->rate_control_get_type != NULL);
121
122   /**
123    * GstVaapiEncoder:rate-control:
124    *
125    * The desired rate control mode, expressed as a #GstVaapiRateControl.
126    */
127   GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
128       GST_VAAPI_ENCODER_PROP_RATECONTROL,
129       g_param_spec_enum ("rate-control",
130           "Rate Control", "Rate control mode",
131           cdata->rate_control_get_type (), cdata->default_rate_control,
132           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
133
134   /**
135    * GstVaapiEncoder:bitrate:
136    *
137    * The desired bitrate, expressed in kbps.
138    */
139   GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
140       GST_VAAPI_ENCODER_PROP_BITRATE,
141       g_param_spec_uint ("bitrate",
142           "Bitrate (kbps)",
143           "The desired bitrate expressed in kbps (0: auto-calculate)",
144           0, 100 * 1024, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
145
146   /**
147    * GstVaapiEncoder:keyframe-period:
148    *
149    * The maximal distance between two keyframes.
150    */
151   GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
152       GST_VAAPI_ENCODER_PROP_KEYFRAME_PERIOD,
153       g_param_spec_uint ("keyframe-period",
154           "Keyframe Period",
155           "Maximal distance between two keyframes (0: auto-calculate)", 1, 300,
156           30, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
157
158   /**
159    * GstVaapiEncoder:tune:
160    *
161    * The desired encoder tuning option.
162    */
163   GST_VAAPI_ENCODER_PROPERTIES_APPEND (props,
164       GST_VAAPI_ENCODER_PROP_TUNE,
165       g_param_spec_enum ("tune",
166           "Encoder Tuning",
167           "Encoder tuning option",
168           cdata->encoder_tune_get_type (), cdata->default_encoder_tune,
169           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
170
171   return props;
172 }
173
174 /**
175  * gst_vaapi_encoder_ref:
176  * @encoder: a #GstVaapiEncoder
177  *
178  * Atomically increases the reference count of the given @encoder by one.
179  *
180  * Returns: The same @encoder argument
181  */
182 GstVaapiEncoder *
183 gst_vaapi_encoder_ref (GstVaapiEncoder * encoder)
184 {
185   return gst_vaapi_object_ref (encoder);
186 }
187
188 /**
189  * gst_vaapi_encoder_unref:
190  * @encoder: a #GstVaapiEncoder
191  *
192  * Atomically decreases the reference count of the @encoder by one. If
193  * the reference count reaches zero, the encoder will be free'd.
194  */
195 void
196 gst_vaapi_encoder_unref (GstVaapiEncoder * encoder)
197 {
198   gst_vaapi_object_unref (encoder);
199 }
200
201 /**
202  * gst_vaapi_encoder_replace:
203  * @old_encoder_ptr: a pointer to a #GstVaapiEncoder
204  * @new_encoder: a #GstVaapiEncoder
205  *
206  * Atomically replaces the encoder encoder held in @old_encoder_ptr
207  * with @new_encoder. This means that @old_encoder_ptr shall reference
208  * a valid encoder. However, @new_encoder can be NULL.
209  */
210 void
211 gst_vaapi_encoder_replace (GstVaapiEncoder ** old_encoder_ptr,
212     GstVaapiEncoder * new_encoder)
213 {
214   gst_vaapi_object_replace (old_encoder_ptr, new_encoder);
215 }
216
217 /* Notifies gst_vaapi_encoder_create_coded_buffer() that a new buffer is free */
218 static void
219 _coded_buffer_proxy_released_notify (GstVaapiEncoder * encoder)
220 {
221   g_mutex_lock (&encoder->mutex);
222   g_cond_signal (&encoder->codedbuf_free);
223   g_mutex_unlock (&encoder->mutex);
224 }
225
226 /* Creates a new VA coded buffer object proxy, backed from a pool */
227 static GstVaapiCodedBufferProxy *
228 gst_vaapi_encoder_create_coded_buffer (GstVaapiEncoder * encoder)
229 {
230   GstVaapiCodedBufferPool *const pool =
231       GST_VAAPI_CODED_BUFFER_POOL (encoder->codedbuf_pool);
232   GstVaapiCodedBufferProxy *codedbuf_proxy;
233
234   g_mutex_lock (&encoder->mutex);
235   do {
236     codedbuf_proxy = gst_vaapi_coded_buffer_proxy_new_from_pool (pool);
237     if (codedbuf_proxy)
238       break;
239
240     /* Wait for a free coded buffer to become available */
241     g_cond_wait (&encoder->codedbuf_free, &encoder->mutex);
242     codedbuf_proxy = gst_vaapi_coded_buffer_proxy_new_from_pool (pool);
243   } while (0);
244   g_mutex_unlock (&encoder->mutex);
245   if (!codedbuf_proxy)
246     return NULL;
247
248   gst_vaapi_coded_buffer_proxy_set_destroy_notify (codedbuf_proxy,
249       (GDestroyNotify) _coded_buffer_proxy_released_notify, encoder);
250   return codedbuf_proxy;
251 }
252
253 /* Notifies gst_vaapi_encoder_create_surface() that a new surface is free */
254 static void
255 _surface_proxy_released_notify (GstVaapiEncoder * encoder)
256 {
257   g_mutex_lock (&encoder->mutex);
258   g_cond_signal (&encoder->surface_free);
259   g_mutex_unlock (&encoder->mutex);
260 }
261
262 /* Creates a new VA surface object proxy, backed from a pool and
263    useful to allocate reconstructed surfaces */
264 GstVaapiSurfaceProxy *
265 gst_vaapi_encoder_create_surface (GstVaapiEncoder * encoder)
266 {
267   GstVaapiSurfaceProxy *proxy;
268
269   g_return_val_if_fail (encoder->context != NULL, NULL);
270
271   g_mutex_lock (&encoder->mutex);
272   for (;;) {
273     proxy = gst_vaapi_context_get_surface_proxy (encoder->context);
274     if (proxy)
275       break;
276
277     /* Wait for a free surface proxy to become available */
278     g_cond_wait (&encoder->surface_free, &encoder->mutex);
279   }
280   g_mutex_unlock (&encoder->mutex);
281
282   gst_vaapi_surface_proxy_set_destroy_notify (proxy,
283       (GDestroyNotify) _surface_proxy_released_notify, encoder);
284   return proxy;
285 }
286
287 /**
288  * gst_vaapi_encoder_put_frame:
289  * @encoder: a #GstVaapiEncoder
290  * @frame: a #GstVideoCodecFrame
291  *
292  * Queues a #GstVideoCodedFrame to the HW encoder. The encoder holds
293  * an extra reference to the @frame.
294  *
295  * Return value: a #GstVaapiEncoderStatus
296  */
297 GstVaapiEncoderStatus
298 gst_vaapi_encoder_put_frame (GstVaapiEncoder * encoder,
299     GstVideoCodecFrame * frame)
300 {
301   GstVaapiEncoderClass *const klass = GST_VAAPI_ENCODER_GET_CLASS (encoder);
302   GstVaapiEncoderStatus status;
303   GstVaapiEncPicture *picture;
304   GstVaapiCodedBufferProxy *codedbuf_proxy;
305
306   for (;;) {
307     picture = NULL;
308     status = klass->reordering (encoder, frame, &picture);
309     if (status == GST_VAAPI_ENCODER_STATUS_NO_SURFACE)
310       break;
311     if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
312       goto error_reorder_frame;
313
314     codedbuf_proxy = gst_vaapi_encoder_create_coded_buffer (encoder);
315     if (!codedbuf_proxy)
316       goto error_create_coded_buffer;
317
318     status = klass->encode (encoder, picture, codedbuf_proxy);
319     if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
320       goto error_encode;
321
322     gst_vaapi_coded_buffer_proxy_set_user_data (codedbuf_proxy,
323         picture, (GDestroyNotify) gst_vaapi_mini_object_unref);
324     g_async_queue_push (encoder->codedbuf_queue, codedbuf_proxy);
325     encoder->num_codedbuf_queued++;
326
327     /* Try again with any pending reordered frame now available for encoding */
328     frame = NULL;
329   }
330   return GST_VAAPI_ENCODER_STATUS_SUCCESS;
331
332   /* ERRORS */
333 error_reorder_frame:
334   {
335     GST_ERROR ("failed to process reordered frames");
336     return status;
337   }
338 error_create_coded_buffer:
339   {
340     GST_ERROR ("failed to allocate coded buffer");
341     gst_vaapi_enc_picture_unref (picture);
342     return GST_VAAPI_ENCODER_STATUS_ERROR_ALLOCATION_FAILED;
343   }
344 error_encode:
345   {
346     GST_ERROR ("failed to encode frame (status = %d)", status);
347     gst_vaapi_enc_picture_unref (picture);
348     gst_vaapi_coded_buffer_proxy_unref (codedbuf_proxy);
349     return status;
350   }
351 }
352
353 /**
354  * gst_vaapi_encoder_get_buffer_with_timeout:
355  * @encoder: a #GstVaapiEncoder
356  * @out_codedbuf_proxy_ptr: the next coded buffer as a #GstVaapiCodedBufferProxy
357  * @timeout: the number of microseconds to wait for the coded buffer, at most
358  *
359  * Upon successful return, *@out_codedbuf_proxy_ptr contains the next
360  * coded buffer as a #GstVaapiCodedBufferProxy. The caller owns this
361  * object, so gst_vaapi_coded_buffer_proxy_unref() shall be called
362  * after usage. Otherwise, @GST_VAAPI_DECODER_STATUS_ERROR_NO_BUFFER
363  * is returned if no coded buffer is available so far (timeout).
364  *
365  * The parent frame is available as a #GstVideoCodecFrame attached to
366  * the user-data anchor of the output coded buffer. Ownership of the
367  * frame is transferred to the coded buffer.
368  *
369  * Return value: a #GstVaapiEncoderStatus
370  */
371 GstVaapiEncoderStatus
372 gst_vaapi_encoder_get_buffer_with_timeout (GstVaapiEncoder * encoder,
373     GstVaapiCodedBufferProxy ** out_codedbuf_proxy_ptr, guint64 timeout)
374 {
375   GstVaapiEncPicture *picture;
376   GstVaapiCodedBufferProxy *codedbuf_proxy;
377
378   codedbuf_proxy = g_async_queue_timeout_pop (encoder->codedbuf_queue, timeout);
379   if (!codedbuf_proxy)
380     return GST_VAAPI_ENCODER_STATUS_NO_BUFFER;
381
382   /* Wait for completion of all operations and report any error that occurred */
383   picture = gst_vaapi_coded_buffer_proxy_get_user_data (codedbuf_proxy);
384   if (!gst_vaapi_surface_sync (picture->surface))
385     goto error_invalid_buffer;
386
387   gst_vaapi_coded_buffer_proxy_set_user_data (codedbuf_proxy,
388       gst_video_codec_frame_ref (picture->frame),
389       (GDestroyNotify) gst_video_codec_frame_unref);
390
391   if (out_codedbuf_proxy_ptr)
392     *out_codedbuf_proxy_ptr = gst_vaapi_coded_buffer_proxy_ref (codedbuf_proxy);
393   gst_vaapi_coded_buffer_proxy_unref (codedbuf_proxy);
394   return GST_VAAPI_ENCODER_STATUS_SUCCESS;
395
396   /* ERRORS */
397 error_invalid_buffer:
398   {
399     GST_ERROR ("failed to encode the frame");
400     gst_vaapi_coded_buffer_proxy_unref (codedbuf_proxy);
401     return GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_SURFACE;
402   }
403 }
404
405 /**
406  * gst_vaapi_encoder_flush:
407  * @encoder: a #GstVaapiEncoder
408  *
409  * Submits any pending (reordered) frame for encoding.
410  *
411  * Return value: a #GstVaapiEncoderStatus
412  */
413 GstVaapiEncoderStatus
414 gst_vaapi_encoder_flush (GstVaapiEncoder * encoder)
415 {
416   GstVaapiEncoderClass *const klass = GST_VAAPI_ENCODER_GET_CLASS (encoder);
417
418   return klass->flush (encoder);
419 }
420
421 /**
422  * gst_vaapi_encoder_get_codec_data:
423  * @encoder: a #GstVaapiEncoder
424  * @out_codec_data_ptr: the pointer to the resulting codec-data (#GstBuffer)
425  *
426  * Returns a codec-data buffer that best represents the encoded
427  * bitstream. Upon successful return, and if the @out_codec_data_ptr
428  * contents is not NULL, then the caller function shall deallocates
429  * that buffer with gst_buffer_unref().
430  *
431  * Return value: a #GstVaapiEncoderStatus
432  */
433 GstVaapiEncoderStatus
434 gst_vaapi_encoder_get_codec_data (GstVaapiEncoder * encoder,
435     GstBuffer ** out_codec_data_ptr)
436 {
437   GstVaapiEncoderStatus ret = GST_VAAPI_ENCODER_STATUS_SUCCESS;
438   GstVaapiEncoderClass *const klass = GST_VAAPI_ENCODER_GET_CLASS (encoder);
439
440   *out_codec_data_ptr = NULL;
441   if (!klass->get_codec_data)
442     return GST_VAAPI_ENCODER_STATUS_SUCCESS;
443
444   ret = klass->get_codec_data (encoder, out_codec_data_ptr);
445   return ret;
446 }
447
448 /* Checks video info */
449 static GstVaapiEncoderStatus
450 check_video_info (GstVaapiEncoder * encoder, const GstVideoInfo * vip)
451 {
452   if (!vip->width || !vip->height)
453     goto error_invalid_resolution;
454   if (!vip->fps_n || !vip->fps_d)
455     goto error_invalid_framerate;
456   return GST_VAAPI_ENCODER_STATUS_SUCCESS;
457
458   /* ERRORS */
459 error_invalid_resolution:
460   {
461     GST_ERROR ("invalid resolution (%dx%d)", vip->width, vip->height);
462     return GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_PARAMETER;
463   }
464 error_invalid_framerate:
465   {
466     GST_ERROR ("invalid framerate (%d/%d)", vip->fps_n, vip->fps_d);
467     return GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_PARAMETER;
468   }
469 }
470
471 /* Determines the set of required packed headers */
472 static void
473 ensure_packed_headers (GstVaapiEncoder * encoder)
474 {
475   if (!gst_vaapi_context_get_attribute (encoder->context,
476           VAConfigAttribEncPackedHeaders, &encoder->packed_headers))
477     encoder->packed_headers = 0;
478   GST_INFO ("packed headers mask: 0x%08x", encoder->packed_headers);
479 }
480
481 /* Updates video context */
482 static void
483 set_context_info (GstVaapiEncoder * encoder)
484 {
485   GstVaapiContextInfo *const cip = &encoder->context_info;
486
487   cip->profile = encoder->profile;
488   cip->entrypoint = GST_VAAPI_ENTRYPOINT_SLICE_ENCODE;
489   cip->rc_mode = GST_VAAPI_ENCODER_RATE_CONTROL (encoder);
490   cip->width = GST_VAAPI_ENCODER_WIDTH (encoder);
491   cip->height = GST_VAAPI_ENCODER_HEIGHT (encoder);
492   cip->ref_frames = encoder->num_ref_frames;
493 }
494
495 /* Ensures the underlying VA context for encoding is created */
496 static gboolean
497 gst_vaapi_encoder_ensure_context (GstVaapiEncoder * encoder)
498 {
499   GstVaapiContextInfo *const cip = &encoder->context_info;
500
501   set_context_info (encoder);
502
503   if (encoder->context) {
504     if (!gst_vaapi_context_reset (encoder->context, cip))
505       return FALSE;
506   } else {
507     encoder->context = gst_vaapi_context_new (encoder->display, cip);
508     if (!encoder->context)
509       return FALSE;
510   }
511   encoder->va_context = gst_vaapi_context_get_id (encoder->context);
512   return TRUE;
513 }
514
515 /* Reconfigures the encoder with the new properties */
516 static GstVaapiEncoderStatus
517 gst_vaapi_encoder_reconfigure_internal (GstVaapiEncoder * encoder)
518 {
519   GstVaapiEncoderClass *const klass = GST_VAAPI_ENCODER_GET_CLASS (encoder);
520   GstVideoInfo *const vip = GST_VAAPI_ENCODER_VIDEO_INFO (encoder);
521   GstVaapiEncoderStatus status;
522   GstVaapiVideoPool *pool;
523   guint codedbuf_size;
524
525   /* Generate a keyframe every second */
526   if (!encoder->keyframe_period)
527     encoder->keyframe_period = (vip->fps_n + vip->fps_d - 1) / vip->fps_d;
528
529   status = klass->reconfigure (encoder);
530   if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
531     return status;
532
533   if (!gst_vaapi_encoder_ensure_context (encoder))
534     goto error_reset_context;
535   ensure_packed_headers (encoder);
536
537   codedbuf_size = encoder->codedbuf_pool ?
538       gst_vaapi_coded_buffer_pool_get_buffer_size (GST_VAAPI_CODED_BUFFER_POOL
539       (encoder)) : 0;
540   if (codedbuf_size != encoder->codedbuf_size) {
541     pool = gst_vaapi_coded_buffer_pool_new (encoder, encoder->codedbuf_size);
542     if (!pool)
543       goto error_alloc_codedbuf_pool;
544     gst_vaapi_video_pool_set_capacity (pool, 5);
545     gst_vaapi_video_pool_replace (&encoder->codedbuf_pool, pool);
546     gst_vaapi_video_pool_unref (pool);
547   }
548   return GST_VAAPI_ENCODER_STATUS_SUCCESS;
549
550   /* ERRORS */
551 error_alloc_codedbuf_pool:
552   {
553     GST_ERROR ("failed to initialize coded buffer pool");
554     return GST_VAAPI_ENCODER_STATUS_ERROR_ALLOCATION_FAILED;
555   }
556 error_reset_context:
557   {
558     GST_ERROR ("failed to update VA context");
559     return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
560   }
561 }
562
563 /**
564  * gst_vaapi_encoder_set_codec_state:
565  * @encoder: a #GstVaapiEncoder
566  * @state : a #GstVideoCodecState
567  *
568  * Notifies the encoder about the source surface properties. The
569  * accepted set of properties is: video resolution, colorimetry,
570  * pixel-aspect-ratio and framerate.
571  *
572  * This function is a synchronization point for codec configuration.
573  * This means that, at this point, the encoder is reconfigured to
574  * match the new properties and any other change beyond this point has
575  * zero effect.
576  *
577  * Return value: a #GstVaapiEncoderStatus
578  */
579 GstVaapiEncoderStatus
580 gst_vaapi_encoder_set_codec_state (GstVaapiEncoder * encoder,
581     GstVideoCodecState * state)
582 {
583   GstVaapiEncoderStatus status;
584
585   g_return_val_if_fail (encoder != NULL,
586       GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_PARAMETER);
587   g_return_val_if_fail (state != NULL,
588       GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_PARAMETER);
589
590   if (encoder->num_codedbuf_queued > 0)
591     goto error_operation_failed;
592
593   if (!gst_video_info_is_equal (&state->info, &encoder->video_info)) {
594     status = check_video_info (encoder, &state->info);
595     if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
596       return status;
597     encoder->video_info = state->info;
598   }
599   return gst_vaapi_encoder_reconfigure_internal (encoder);
600
601   /* ERRORS */
602 error_operation_failed:
603   {
604     GST_ERROR ("could not change codec state after encoding started");
605     return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
606   }
607 }
608
609 /**
610  * gst_vaapi_encoder_set_property:
611  * @encoder: a #GstVaapiEncoder
612  * @prop_id: the id of the property to change
613  * @value: the new value to set
614  *
615  * Update the requested property, designed by @prop_id, with the
616  * supplied @value. A @NULL value argument resets the property to its
617  * default value.
618  *
619  * Return value: a #GstVaapiEncoderStatus
620  */
621 static GstVaapiEncoderStatus
622 set_property (GstVaapiEncoder * encoder, gint prop_id, const GValue * value)
623 {
624   GstVaapiEncoderStatus status =
625       GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_PARAMETER;
626
627   g_assert (value != NULL);
628
629   /* Handle codec-specific properties */
630   if (prop_id < 0) {
631     GstVaapiEncoderClass *const klass = GST_VAAPI_ENCODER_GET_CLASS (encoder);
632
633     if (klass->set_property) {
634       if (encoder->num_codedbuf_queued > 0)
635         goto error_operation_failed;
636       status = klass->set_property (encoder, prop_id, value);
637     }
638     return status;
639   }
640
641   /* Handle common properties */
642   switch (prop_id) {
643     case GST_VAAPI_ENCODER_PROP_RATECONTROL:
644       status = gst_vaapi_encoder_set_rate_control (encoder,
645           g_value_get_enum (value));
646       break;
647     case GST_VAAPI_ENCODER_PROP_BITRATE:
648       status = gst_vaapi_encoder_set_bitrate (encoder,
649           g_value_get_uint (value));
650       break;
651     case GST_VAAPI_ENCODER_PROP_KEYFRAME_PERIOD:
652       status = gst_vaapi_encoder_set_keyframe_period (encoder,
653           g_value_get_uint (value));
654       break;
655     case GST_VAAPI_ENCODER_PROP_TUNE:
656       status = gst_vaapi_encoder_set_tuning (encoder, g_value_get_enum (value));
657       break;
658   }
659   return status;
660
661   /* ERRORS */
662 error_operation_failed:
663   {
664     GST_ERROR ("could not change codec state after encoding started");
665     return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
666   }
667 }
668
669 GstVaapiEncoderStatus
670 gst_vaapi_encoder_set_property (GstVaapiEncoder * encoder, gint prop_id,
671     const GValue * value)
672 {
673   GstVaapiEncoderStatus status;
674   GValue default_value = G_VALUE_INIT;
675
676   g_return_val_if_fail (encoder != NULL,
677       GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_PARAMETER);
678
679   if (!value) {
680     GParamSpec *const pspec = prop_find_pspec (encoder, prop_id);
681     if (!pspec)
682       goto error_invalid_property;
683
684     g_value_init (&default_value, pspec->value_type);
685     g_param_value_set_default (pspec, &default_value);
686     value = &default_value;
687   }
688
689   status = set_property (encoder, prop_id, value);
690
691   if (default_value.g_type)
692     g_value_unset (&default_value);
693   return status;
694
695   /* ERRORS */
696 error_invalid_property:
697   {
698     GST_ERROR ("unsupported property (%d)", prop_id);
699     return GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_PARAMETER;
700   }
701 }
702
703 /* Determine the supported rate control modes */
704 static gboolean
705 get_rate_control_mask (GstVaapiEncoder * encoder)
706 {
707   const GstVaapiEncoderClassData *const cdata =
708       GST_VAAPI_ENCODER_GET_CLASS (encoder)->class_data;
709   GstVaapiProfile profile;
710   GArray *profiles;
711   guint i, rate_control_mask = 0;
712
713   if (encoder->rate_control_mask)
714     return encoder->rate_control_mask;
715
716   profiles = gst_vaapi_display_get_encode_profiles (encoder->display);
717   if (!profiles)
718     goto cleanup;
719
720   // Pick a profile matching the class codec
721   for (i = 0; i < profiles->len; i++) {
722     profile = g_array_index (profiles, GstVaapiProfile, i);
723     if (gst_vaapi_profile_get_codec (profile) == cdata->codec)
724       break;
725   }
726
727   if (i != profiles->len) {
728     VAConfigAttrib attrib;
729     VAStatus status;
730
731     attrib.type = VAConfigAttribRateControl;
732     GST_VAAPI_DISPLAY_LOCK (encoder->display);
733     status =
734         vaGetConfigAttributes (GST_VAAPI_DISPLAY_VADISPLAY (encoder->display),
735         gst_vaapi_profile_get_va_profile (profile), VAEntrypointEncSlice,
736         &attrib, 1);
737     GST_VAAPI_DISPLAY_UNLOCK (encoder->display);
738     if (vaapi_check_status (status, "vaGetConfigAttributes()")) {
739       for (i = 0; i < 32; i++) {
740         if (!(attrib.value & (1 << i)))
741           continue;
742         rate_control_mask |= 1 << to_GstVaapiRateControl (1 << i);
743       }
744     }
745   }
746   g_array_unref (profiles);
747   GST_INFO ("supported rate controls: 0x%08x", rate_control_mask);
748
749 cleanup:
750   encoder->rate_control_mask = cdata->rate_control_mask & rate_control_mask;
751   return encoder->rate_control_mask;
752 }
753
754 /**
755  * gst_vaapi_encoder_set_rate_control:
756  * @encoder: a #GstVaapiEncoder
757  * @rate_control: the requested rate control
758  *
759  * Notifies the @encoder to use the supplied @rate_control mode.
760  *
761  * If the underlying encoder does not support that rate control mode,
762  * then @GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_RATE_CONTROL is
763  * returned.
764  *
765  * The rate control mode can only be specified before the first frame
766  * is to be encoded. Afterwards, any change to this parameter is
767  * invalid and @GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED is
768  * returned.
769  *
770  * Return value: a #GstVaapiEncoderStatus
771  */
772 GstVaapiEncoderStatus
773 gst_vaapi_encoder_set_rate_control (GstVaapiEncoder * encoder,
774     GstVaapiRateControl rate_control)
775 {
776   guint32 rate_control_mask;
777
778   g_return_val_if_fail (encoder != NULL,
779       GST_VAAPI_ENCODER_STATUS_ERROR_INVALID_PARAMETER);
780
781   if (encoder->rate_control != rate_control && encoder->num_codedbuf_queued > 0)
782     goto error_operation_failed;
783
784   rate_control_mask = get_rate_control_mask (encoder);
785   if (!(rate_control_mask & (1U << rate_control)))
786     goto error_unsupported_rate_control;
787
788   encoder->rate_control = rate_control;
789   return GST_VAAPI_ENCODER_STATUS_SUCCESS;
790
791   /* ERRORS */
792 error_operation_failed:
793   {
794     GST_ERROR ("could not change rate control mode after encoding started");
795     return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
796   }
797 error_unsupported_rate_control:
798   {
799     GST_ERROR ("unsupported rate control mode (%d)", rate_control);
800     return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_RATE_CONTROL;
801   }
802 }
803
804 /**
805  * gst_vaapi_encoder_set_bitrate:
806  * @encoder: a #GstVaapiEncoder
807  * @bitrate: the requested bitrate (in kbps)
808  *
809  * Notifies the @encoder to use the supplied @bitrate value.
810  *
811  * Note: currently, the bitrate can only be specified before the first
812  * frame is encoded. Afterwards, any change to this parameter is
813  * invalid and @GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED is
814  * returned.
815  *
816  * Return value: a #GstVaapiEncoderStatus
817  */
818 GstVaapiEncoderStatus
819 gst_vaapi_encoder_set_bitrate (GstVaapiEncoder * encoder, guint bitrate)
820 {
821   g_return_val_if_fail (encoder != NULL, 0);
822
823   if (encoder->bitrate != bitrate && encoder->num_codedbuf_queued > 0)
824     goto error_operation_failed;
825
826   encoder->bitrate = bitrate;
827   return GST_VAAPI_ENCODER_STATUS_SUCCESS;
828
829   /* ERRORS */
830 error_operation_failed:
831   {
832     GST_ERROR ("could not change bitrate value after encoding started");
833     return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
834   }
835 }
836
837 /**
838  * gst_vaapi_encoder_set_keyframe_period:
839  * @encoder: a #GstVaapiEncoder
840  * @keyframe_period: the maximal distance between two keyframes
841  *
842  * Notifies the @encoder to use the supplied @keyframe_period value.
843  *
844  * Note: currently, the keyframe period can only be specified before
845  * the last call to gst_vaapi_encoder_set_codec_state(), which shall
846  * occur before the first frame is encoded. Afterwards, any change to
847  * this parameter causes gst_vaapi_encoder_set_keyframe_period() to
848  * return @GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED.
849  *
850  * Return value: a #GstVaapiEncoderStatus
851  */
852 GstVaapiEncoderStatus
853 gst_vaapi_encoder_set_keyframe_period (GstVaapiEncoder * encoder,
854     guint keyframe_period)
855 {
856   g_return_val_if_fail (encoder != NULL, 0);
857
858   if (encoder->keyframe_period != keyframe_period
859       && encoder->num_codedbuf_queued > 0)
860     goto error_operation_failed;
861
862   encoder->keyframe_period = keyframe_period;
863   return GST_VAAPI_ENCODER_STATUS_SUCCESS;
864
865   /* ERRORS */
866 error_operation_failed:
867   {
868     GST_ERROR ("could not change keyframe period after encoding started");
869     return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
870   }
871 }
872
873 /**
874  * gst_vaapi_encoder_set_tuning:
875  * @encoder: a #GstVaapiEncoder
876  * @tuning: the #GstVaapiEncoderTune option
877  *
878  * Notifies the @encoder to use the supplied @tuning option.
879  *
880  * Note: currently, the tuning option can only be specified before the
881  * last call to gst_vaapi_encoder_set_codec_state(), which shall occur
882  * before the first frame is encoded. Afterwards, any change to this
883  * parameter causes gst_vaapi_encoder_set_tuning() to return
884  * @GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED.
885  *
886  * Return value: a #GstVaapiEncoderStatus
887  */
888 GstVaapiEncoderStatus
889 gst_vaapi_encoder_set_tuning (GstVaapiEncoder * encoder,
890     GstVaapiEncoderTune tuning)
891 {
892   g_return_val_if_fail (encoder != NULL, 0);
893
894   if (encoder->tune != tuning && encoder->num_codedbuf_queued > 0)
895     goto error_operation_failed;
896
897   encoder->tune = tuning;
898   return GST_VAAPI_ENCODER_STATUS_SUCCESS;
899
900   /* ERRORS */
901 error_operation_failed:
902   {
903     GST_ERROR ("could not change tuning options after encoding started");
904     return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
905   }
906 }
907
908 /* Initialize default values for configurable properties */
909 static gboolean
910 gst_vaapi_encoder_init_properties (GstVaapiEncoder * encoder)
911 {
912   GstVaapiEncoderClass *const klass = GST_VAAPI_ENCODER_GET_CLASS (encoder);
913   GPtrArray *props;
914   guint i;
915
916   props = klass->get_default_properties ();
917   if (!props)
918     return FALSE;
919
920   encoder->properties = props;
921   for (i = 0; i < props->len; i++) {
922     GstVaapiEncoderPropInfo *const prop = g_ptr_array_index (props, i);
923
924     if (gst_vaapi_encoder_set_property (encoder, prop->prop,
925             NULL) != GST_VAAPI_ENCODER_STATUS_SUCCESS)
926       return FALSE;
927   }
928   return TRUE;
929 }
930
931 /* Base encoder initialization (internal) */
932 static gboolean
933 gst_vaapi_encoder_init (GstVaapiEncoder * encoder, GstVaapiDisplay * display)
934 {
935   GstVaapiEncoderClass *const klass = GST_VAAPI_ENCODER_GET_CLASS (encoder);
936
937   g_return_val_if_fail (display != NULL, FALSE);
938
939 #define CHECK_VTABLE_HOOK(FUNC) do {            \
940     if (!klass->FUNC)                           \
941       goto error_invalid_vtable;                \
942   } while (0)
943
944   CHECK_VTABLE_HOOK (init);
945   CHECK_VTABLE_HOOK (finalize);
946   CHECK_VTABLE_HOOK (get_default_properties);
947   CHECK_VTABLE_HOOK (reconfigure);
948   CHECK_VTABLE_HOOK (encode);
949   CHECK_VTABLE_HOOK (reordering);
950   CHECK_VTABLE_HOOK (flush);
951
952 #undef CHECK_VTABLE_HOOK
953
954   encoder->display = gst_vaapi_display_ref (display);
955   encoder->va_display = gst_vaapi_display_get_display (display);
956   encoder->va_context = VA_INVALID_ID;
957
958   gst_video_info_init (&encoder->video_info);
959
960   g_mutex_init (&encoder->mutex);
961   g_cond_init (&encoder->surface_free);
962   g_cond_init (&encoder->codedbuf_free);
963
964   encoder->codedbuf_queue = g_async_queue_new_full ((GDestroyNotify)
965       gst_vaapi_coded_buffer_proxy_unref);
966   if (!encoder->codedbuf_queue)
967     return FALSE;
968
969   if (!klass->init (encoder))
970     return FALSE;
971   if (!gst_vaapi_encoder_init_properties (encoder))
972     return FALSE;
973   return TRUE;
974
975   /* ERRORS */
976 error_invalid_vtable:
977   {
978     GST_ERROR ("invalid subclass hook (internal error)");
979     return FALSE;
980   }
981 }
982
983 /* Base encoder cleanup (internal) */
984 void
985 gst_vaapi_encoder_finalize (GstVaapiEncoder * encoder)
986 {
987   GstVaapiEncoderClass *const klass = GST_VAAPI_ENCODER_GET_CLASS (encoder);
988
989   klass->finalize (encoder);
990
991   gst_vaapi_object_replace (&encoder->context, NULL);
992   gst_vaapi_display_replace (&encoder->display, NULL);
993   encoder->va_display = NULL;
994
995   if (encoder->properties) {
996     g_ptr_array_unref (encoder->properties);
997     encoder->properties = NULL;
998   }
999
1000   gst_vaapi_video_pool_replace (&encoder->codedbuf_pool, NULL);
1001   if (encoder->codedbuf_queue) {
1002     g_async_queue_unref (encoder->codedbuf_queue);
1003     encoder->codedbuf_queue = NULL;
1004   }
1005   g_cond_clear (&encoder->surface_free);
1006   g_cond_clear (&encoder->codedbuf_free);
1007   g_mutex_clear (&encoder->mutex);
1008 }
1009
1010 /* Helper function to create new GstVaapiEncoder instances (internal) */
1011 GstVaapiEncoder *
1012 gst_vaapi_encoder_new (const GstVaapiEncoderClass * klass,
1013     GstVaapiDisplay * display)
1014 {
1015   GstVaapiEncoder *encoder;
1016
1017   encoder = (GstVaapiEncoder *)
1018       gst_vaapi_mini_object_new0 (GST_VAAPI_MINI_OBJECT_CLASS (klass));
1019   if (!encoder)
1020     return NULL;
1021
1022   if (!gst_vaapi_encoder_init (encoder, display))
1023     goto error;
1024   return encoder;
1025
1026 error:
1027   gst_vaapi_encoder_unref (encoder);
1028   return NULL;
1029 }
1030
1031 /** Returns a GType for the #GstVaapiEncoderTune set */
1032 GType
1033 gst_vaapi_encoder_tune_get_type (void)
1034 {
1035   static volatile gsize g_type = 0;
1036
1037   static const GEnumValue encoder_tune_values[] = {
1038     /* *INDENT-OFF* */
1039     { GST_VAAPI_ENCODER_TUNE_NONE,
1040       "None", "none" },
1041     { GST_VAAPI_ENCODER_TUNE_HIGH_COMPRESSION,
1042       "High compression", "high-compression" },
1043     { GST_VAAPI_ENCODER_TUNE_LOW_LATENCY,
1044       "Low latency", "low-latency" },
1045     { GST_VAAPI_ENCODER_TUNE_LOW_POWER,
1046       "Low power mode", "low-power" },
1047     { 0, NULL, NULL },
1048     /* *INDENT-ON* */
1049   };
1050
1051   if (g_once_init_enter (&g_type)) {
1052     GType type =
1053         g_enum_register_static ("GstVaapiEncoderTune", encoder_tune_values);
1054     g_once_init_leave (&g_type, type);
1055   }
1056   return g_type;
1057 }