1 /* GStreamer Intel MSDK plugin
2 * Copyright (c) 2016, Oblong Industries, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
28 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
29 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /* TODO:
33 * - Add support for interlaced content
34 * - Add support for MVC AVC
35 * - Wrap more configuration options and maybe move properties to derived
36 */
37
38 #ifdef HAVE_CONFIG_H
39 # include <config.h>
40 #endif
41 #ifdef _WIN32
42 # include <malloc.h>
43 #endif
44
45 #include <stdlib.h>
46
47 #include "gstmsdkenc.h"
48 #include "gstmsdkbufferpool.h"
49 #include "gstmsdkvideomemory.h"
50 #include "gstmsdksystemmemory.h"
51 #include "gstmsdkcontextutil.h"
52 #include "mfxjpeg.h"
53
54 #ifndef _WIN32
55 #include "gstmsdkallocator_libva.h"
56 #endif
57
58 static inline void *
_aligned_alloc(size_t alignment,size_t size)59 _aligned_alloc (size_t alignment, size_t size)
60 {
61 #ifdef _WIN32
62 return _aligned_malloc (size, alignment);
63 #else
64 void *out;
65 if (posix_memalign (&out, alignment, size) != 0)
66 out = NULL;
67 return out;
68 #endif
69 }
70
71 #ifndef _WIN32
72 #define _aligned_free free
73 #endif
74
75 static void gst_msdkenc_close_encoder (GstMsdkEnc * thiz);
76
77 GST_DEBUG_CATEGORY_EXTERN (gst_msdkenc_debug);
78 #define GST_CAT_DEFAULT gst_msdkenc_debug
79
80 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
81 GST_PAD_SINK,
82 GST_PAD_ALWAYS,
83 GST_STATIC_CAPS (GST_MSDK_CAPS_STR
84 ("{ NV12, I420, YV12, YUY2, UYVY, BGRA }", "NV12"))
85 );
86
87 #define PROP_HARDWARE_DEFAULT TRUE
88 #define PROP_ASYNC_DEPTH_DEFAULT 4
89 #define PROP_TARGET_USAGE_DEFAULT (MFX_TARGETUSAGE_BALANCED)
90 #define PROP_RATE_CONTROL_DEFAULT (MFX_RATECONTROL_CBR)
91 #define PROP_BITRATE_DEFAULT (2 * 1024)
92 #define PROP_QPI_DEFAULT 0
93 #define PROP_QPP_DEFAULT 0
94 #define PROP_QPB_DEFAULT 0
95 #define PROP_GOP_SIZE_DEFAULT 256
96 #define PROP_REF_FRAMES_DEFAULT 1
97 #define PROP_I_FRAMES_DEFAULT 0
98 #define PROP_B_FRAMES_DEFAULT 0
99 #define PROP_NUM_SLICES_DEFAULT 0
100 #define PROP_AVBR_ACCURACY_DEFAULT 0
101 #define PROP_AVBR_CONVERGENCE_DEFAULT 0
102 #define PROP_RC_LOOKAHEAD_DEPTH_DEFAULT 10
103 #define PROP_MAX_VBV_BITRATE_DEFAULT 0
104 #define PROP_MAX_FRAME_SIZE_DEFAULT 0
105 #define PROP_MBBRC_DEFAULT MFX_CODINGOPTION_OFF
106 #define PROP_ADAPTIVE_I_DEFAULT MFX_CODINGOPTION_OFF
107 #define PROP_ADAPTIVE_B_DEFAULT MFX_CODINGOPTION_OFF
108
109 /* External coding properties */
110 #define EC_PROPS_STRUCT_NAME "props"
111 #define EC_PROPS_EXTBRC "extbrc"
112
113 #define gst_msdkenc_parent_class parent_class
114 G_DEFINE_TYPE (GstMsdkEnc, gst_msdkenc, GST_TYPE_VIDEO_ENCODER);
115
116 typedef struct
117 {
118 mfxFrameSurface1 *surface;
119 GstBuffer *buf;
120 } MsdkSurface;
121
122 void
gst_msdkenc_add_extra_param(GstMsdkEnc * thiz,mfxExtBuffer * param)123 gst_msdkenc_add_extra_param (GstMsdkEnc * thiz, mfxExtBuffer * param)
124 {
125 if (thiz->num_extra_params < MAX_EXTRA_PARAMS) {
126 thiz->extra_params[thiz->num_extra_params] = param;
127 thiz->num_extra_params++;
128 }
129 }
130
131 static void
gst_msdkenc_set_context(GstElement * element,GstContext * context)132 gst_msdkenc_set_context (GstElement * element, GstContext * context)
133 {
134 GstMsdkContext *msdk_context = NULL;
135 GstMsdkEnc *thiz = GST_MSDKENC (element);
136
137 if (gst_msdk_context_get_context (context, &msdk_context)) {
138 gst_object_replace ((GstObject **) & thiz->context,
139 (GstObject *) msdk_context);
140 gst_object_unref (msdk_context);
141 }
142
143 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
144 }
145
146 static void
ensure_bitrate_control(GstMsdkEnc * thiz)147 ensure_bitrate_control (GstMsdkEnc * thiz)
148 {
149 mfxInfoMFX *mfx = &thiz->param.mfx;
150 mfxExtCodingOption2 *option2 = &thiz->option2;
151 mfxExtCodingOption3 *option3 = &thiz->option3;
152
153 GST_DEBUG_OBJECT (thiz, "set target bitrate: %u kbit/sec", thiz->bitrate);
154
155 mfx->RateControlMethod = thiz->rate_control;
156 /* No effect in CQP variant algorithms */
157 if ((mfx->RateControlMethod != MFX_RATECONTROL_CQP) &&
158 (thiz->bitrate > G_MAXUINT16 || thiz->max_vbv_bitrate > G_MAXUINT16)) {
159 mfxU32 max_val = MAX (thiz->max_vbv_bitrate, thiz->bitrate);
160
161 mfx->BRCParamMultiplier = (mfxU16) ((max_val + 0x10000) / 0x10000);
162 mfx->TargetKbps = (mfxU16) (thiz->bitrate / mfx->BRCParamMultiplier);
163 mfx->MaxKbps = (mfxU16) (thiz->max_vbv_bitrate / mfx->BRCParamMultiplier);
164 mfx->BufferSizeInKB =
165 (mfxU16) (mfx->BufferSizeInKB / mfx->BRCParamMultiplier);
166 /* Currently InitialDelayInKB is not used in this plugin */
167 mfx->InitialDelayInKB =
168 (mfxU16) (mfx->InitialDelayInKB / mfx->BRCParamMultiplier);
169 } else {
170 mfx->TargetKbps = thiz->bitrate;
171 mfx->MaxKbps = thiz->max_vbv_bitrate;
172 mfx->BRCParamMultiplier = 1;
173 }
174
175 switch (mfx->RateControlMethod) {
176 case MFX_RATECONTROL_CQP:
177 mfx->QPI = thiz->qpi;
178 mfx->QPP = thiz->qpp;
179 mfx->QPB = thiz->qpb;
180 break;
181
182 case MFX_RATECONTROL_LA_ICQ:
183 option2->LookAheadDepth = thiz->lookahead_depth;
184 case MFX_RATECONTROL_ICQ:
185 mfx->ICQQuality = CLAMP (thiz->qpi, 1, 51);
186 break;
187
188 case MFX_RATECONTROL_LA: /* VBR with LA. Only supported in H264?? */
189 case MFX_RATECONTROL_LA_HRD: /* VBR with LA, HRD compliant */
190 option2->LookAheadDepth = thiz->lookahead_depth;
191 break;
192
193 case MFX_RATECONTROL_QVBR:
194 option3->QVBRQuality = CLAMP (thiz->qpi, 1, 51);
195 thiz->enable_extopt3 = TRUE;
196 break;
197
198 case MFX_RATECONTROL_AVBR:
199 mfx->Accuracy = thiz->accuracy;
200 mfx->Convergence = thiz->convergence;
201 break;
202
203 case MFX_RATECONTROL_VBR:
204 option2->MaxFrameSize = thiz->max_frame_size * 1000;
205 break;
206
207 case MFX_RATECONTROL_VCM:
208 /*Non HRD compliant mode with no B-frame and interlaced support */
209 thiz->param.mfx.GopRefDist = 0;
210 break;
211
212 case MFX_RATECONTROL_CBR:
213 break;
214
215 default:
216 GST_ERROR ("Unsupported RateControl!");
217 break;
218 }
219 }
220
221 static gint16
coding_option_get_value(const gchar * key,const gchar * nickname)222 coding_option_get_value (const gchar * key, const gchar * nickname)
223 {
224 if (!g_strcmp0 (nickname, "on")) {
225 return MFX_CODINGOPTION_ON;
226 } else if (!g_strcmp0 (nickname, "off")) {
227 return MFX_CODINGOPTION_OFF;
228 } else if (!g_strcmp0 (nickname, "auto")) {
229 return MFX_CODINGOPTION_UNKNOWN;
230 }
231
232 GST_ERROR ("\"%s\" illegal option \"%s\", set to \"off\"", key, nickname);
233
234 return MFX_CODINGOPTION_OFF;
235 }
236
237 static gboolean
structure_transform(const GstStructure * src,GstStructure * dst)238 structure_transform (const GstStructure * src, GstStructure * dst)
239 {
240 guint len;
241 GValue dst_value = G_VALUE_INIT;
242 gboolean ret = TRUE;
243
244 g_return_val_if_fail (src != NULL, FALSE);
245 g_return_val_if_fail (dst != NULL, FALSE);
246
247 len = gst_structure_n_fields (src);
248
249 for (guint i = 0; i < len; i++) {
250 const gchar *key = gst_structure_nth_field_name (src, i);
251 const GValue *src_value = gst_structure_get_value (src, key);
252
253 if (!gst_structure_has_field (dst, key)) {
254 GST_ERROR ("structure \"%s\" does not support \"%s\"",
255 gst_structure_get_name (dst), key);
256 ret = FALSE;
257 continue;
258 }
259
260 g_value_init (&dst_value, gst_structure_get_field_type (dst, key));
261
262 if (g_value_transform (src_value, &dst_value)) {
263 gst_structure_set_value (dst, key, &dst_value);
264 } else {
265 GST_ERROR ("\"%s\" transform %s to %s failed", key,
266 G_VALUE_TYPE_NAME (src_value), G_VALUE_TYPE_NAME (&dst_value));
267 ret = FALSE;
268 }
269
270 g_value_unset (&dst_value);
271 }
272
273 return ret;
274 }
275
276 /* Supported types: gchar*, gboolean, gint, guint, gfloat, gdouble */
277 static gboolean
structure_get_value(const GstStructure * s,const gchar * key,gpointer value)278 structure_get_value (const GstStructure * s, const gchar * key, gpointer value)
279 {
280 const GValue *gvalue = gst_structure_get_value (s, key);
281 if (!gvalue) {
282 GST_ERROR ("structure \"%s\" does not support \"%s\"",
283 gst_structure_get_name (s), key);
284 return FALSE;
285 }
286
287 switch (G_VALUE_TYPE (gvalue)) {
288 case G_TYPE_STRING:{
289 const gchar **val = (const gchar **) value;
290 *val = g_value_get_string (gvalue);
291 break;
292 }
293 case G_TYPE_BOOLEAN:{
294 gboolean *val = (gboolean *) value;
295 *val = g_value_get_boolean (gvalue);
296 break;
297 }
298 case G_TYPE_INT:{
299 gint *val = (gint *) value;
300 *val = g_value_get_int (gvalue);
301 break;
302 }
303 case G_TYPE_UINT:{
304 guint *val = (guint *) value;
305 *val = g_value_get_uint (gvalue);
306 break;
307 }
308 case G_TYPE_FLOAT:{
309 gfloat *val = (gfloat *) value;
310 *val = g_value_get_float (gvalue);
311 break;
312 }
313 case G_TYPE_DOUBLE:{
314 gdouble *val = (gdouble *) value;
315 *val = g_value_get_double (gvalue);
316 break;
317 }
318 default:
319 GST_ERROR ("\"%s\" unsupported type %s", key, G_VALUE_TYPE_NAME (gvalue));
320 return FALSE;
321 }
322
323 return TRUE;
324 }
325
326 static gboolean
ext_coding_props_get_value(GstMsdkEnc * thiz,const gchar * key,gpointer value)327 ext_coding_props_get_value (GstMsdkEnc * thiz,
328 const gchar * key, gpointer value)
329 {
330 gboolean ret;
331 if (!(ret = structure_get_value (thiz->ext_coding_props, key, value))) {
332 GST_ERROR_OBJECT (thiz, "structure \"%s\" failed to get value for \"%s\"",
333 gst_structure_get_name (thiz->ext_coding_props), key);
334 }
335
336 return ret;
337 }
338
339 void
gst_msdkenc_ensure_extended_coding_options(GstMsdkEnc * thiz)340 gst_msdkenc_ensure_extended_coding_options (GstMsdkEnc * thiz)
341 {
342 mfxExtCodingOption2 *option2 = &thiz->option2;
343 mfxExtCodingOption3 *option3 = &thiz->option3;
344
345 gchar *extbrc;
346 ext_coding_props_get_value (thiz, EC_PROPS_EXTBRC, &extbrc);
347
348 /* Fill ExtendedCodingOption2, set non-zero defaults too */
349 option2->Header.BufferId = MFX_EXTBUFF_CODING_OPTION2;
350 option2->Header.BufferSz = sizeof (thiz->option2);
351 option2->MBBRC = thiz->mbbrc;
352 option2->ExtBRC = coding_option_get_value (EC_PROPS_EXTBRC, extbrc);
353 option2->AdaptiveI = thiz->adaptive_i;
354 option2->AdaptiveB = thiz->adaptive_b;
355 option2->BitrateLimit = MFX_CODINGOPTION_OFF;
356 option2->EnableMAD = MFX_CODINGOPTION_OFF;
357 option2->UseRawRef = MFX_CODINGOPTION_OFF;
358 gst_msdkenc_add_extra_param (thiz, (mfxExtBuffer *) option2);
359
360 if (thiz->enable_extopt3) {
361 option3->Header.BufferId = MFX_EXTBUFF_CODING_OPTION3;
362 option3->Header.BufferSz = sizeof (thiz->option3);
363 gst_msdkenc_add_extra_param (thiz, (mfxExtBuffer *) option3);
364 }
365 }
366
367 /* Return TRUE if ROI is changed and update ROI parameters in encoder_roi */
368 gboolean
gst_msdkenc_get_roi_params(GstMsdkEnc * thiz,GstVideoCodecFrame * frame,mfxExtEncoderROI * encoder_roi)369 gst_msdkenc_get_roi_params (GstMsdkEnc * thiz,
370 GstVideoCodecFrame * frame, mfxExtEncoderROI * encoder_roi)
371 {
372 GstBuffer *input;
373 guint num_roi, i, num_valid_roi = 0;
374 gushort roi_mode = G_MAXUINT16;
375 gpointer state = NULL;
376 mfxExtEncoderROI *curr_roi = encoder_roi;
377 mfxExtEncoderROI *prev_roi = encoder_roi + 1;
378
379 if (!frame || !frame->input_buffer)
380 return FALSE;
381
382 memset (curr_roi, 0, sizeof (mfxExtEncoderROI));
383 input = frame->input_buffer;
384
385 num_roi =
386 gst_buffer_get_n_meta (input, GST_VIDEO_REGION_OF_INTEREST_META_API_TYPE);
387
388 if (num_roi == 0)
389 goto end;
390
391 curr_roi->Header.BufferId = MFX_EXTBUFF_ENCODER_ROI;
392 curr_roi->Header.BufferSz = sizeof (mfxExtEncoderROI);
393
394 for (i = 0; i < num_roi && num_valid_roi < 256; i++) {
395 GstVideoRegionOfInterestMeta *roi;
396 GstStructure *s;
397
398 roi = (GstVideoRegionOfInterestMeta *)
399 gst_buffer_iterate_meta_filtered (input, &state,
400 GST_VIDEO_REGION_OF_INTEREST_META_API_TYPE);
401
402 if (!roi)
403 continue;
404
405 /* ignore roi if overflow */
406 if ((roi->x > G_MAXINT16) || (roi->y > G_MAXINT16)
407 || (roi->w > G_MAXUINT16) || (roi->h > G_MAXUINT16)) {
408 GST_DEBUG_OBJECT (thiz, "Ignoring ROI... ROI overflow");
409 continue;
410 }
411
412 GST_LOG ("Input buffer ROI: type=%s id=%d (%d, %d) %dx%d",
413 g_quark_to_string (roi->roi_type), roi->id, roi->x, roi->y, roi->w,
414 roi->h);
415
416 curr_roi->ROI[num_valid_roi].Left = roi->x;
417 curr_roi->ROI[num_valid_roi].Top = roi->y;
418 curr_roi->ROI[num_valid_roi].Right = roi->x + roi->w;
419 curr_roi->ROI[num_valid_roi].Bottom = roi->y + roi->h;
420
421 s = gst_video_region_of_interest_meta_get_param (roi, "roi/msdk");
422
423 if (s) {
424 int value = 0;
425
426 if (roi_mode == G_MAXUINT16) {
427 if (gst_structure_get_int (s, "delta-qp", &value)) {
428 #if (MFX_VERSION >= 1022)
429 roi_mode = MFX_ROI_MODE_QP_DELTA;
430 curr_roi->ROI[num_valid_roi].DeltaQP = CLAMP (value, -51, 51);
431 GST_LOG ("Use delta-qp %d", value);
432 #else
433 GST_WARNING
434 ("Ignore delta QP because the MFX doesn't support delta QP mode");
435 #endif
436 } else if (gst_structure_get_int (s, "priority", &value)) {
437 roi_mode = MFX_ROI_MODE_PRIORITY;
438 curr_roi->ROI[num_valid_roi].Priority = CLAMP (value, -3, 3);
439 GST_LOG ("Use priority %d", value);
440 } else
441 continue;
442 #if (MFX_VERSION >= 1022)
443 } else if (roi_mode == MFX_ROI_MODE_QP_DELTA &&
444 gst_structure_get_int (s, "delta-qp", &value)) {
445 curr_roi->ROI[num_valid_roi].DeltaQP = CLAMP (value, -51, 51);
446 #endif
447 } else if (roi_mode == MFX_ROI_MODE_PRIORITY &&
448 gst_structure_get_int (s, "priority", &value)) {
449 curr_roi->ROI[num_valid_roi].Priority = CLAMP (value, -3, 3);
450 } else
451 continue;
452
453 num_valid_roi++;
454 }
455 }
456
457 #if (MFX_VERSION >= 1022)
458 curr_roi->ROIMode = roi_mode;
459 #endif
460
461 curr_roi->NumROI = num_valid_roi;
462
463 end:
464 if (curr_roi->NumROI == 0 && prev_roi->NumROI == 0)
465 return FALSE;
466
467 if (curr_roi->NumROI != prev_roi->NumROI ||
468 memcmp (curr_roi, prev_roi, sizeof (mfxExtEncoderROI)) != 0) {
469 *prev_roi = *curr_roi;
470 return TRUE;
471 }
472
473 return FALSE;
474 }
475
476 static gboolean
gst_msdkenc_init_encoder(GstMsdkEnc * thiz)477 gst_msdkenc_init_encoder (GstMsdkEnc * thiz)
478 {
479 GstMsdkEncClass *klass = GST_MSDKENC_GET_CLASS (thiz);
480 GstVideoInfo *info;
481 mfxSession session;
482 mfxStatus status;
483 mfxFrameAllocRequest request[2];
484 guint i;
485 gboolean need_vpp = TRUE;
486 GstVideoFormat encoder_input_fmt;
487 mfxExtVideoSignalInfo ext_vsi;
488
489 if (thiz->initialized) {
490 GST_DEBUG_OBJECT (thiz, "Already initialized");
491 return TRUE;
492 }
493
494 if (!thiz->context) {
495 GST_WARNING_OBJECT (thiz, "No MSDK Context");
496 return FALSE;
497 }
498
499 if (!thiz->input_state) {
500 GST_DEBUG_OBJECT (thiz, "Have no input state yet");
501 return FALSE;
502 }
503 info = &thiz->input_state->info;
504
505 GST_OBJECT_LOCK (thiz);
506 session = gst_msdk_context_get_session (thiz->context);
507 thiz->codename = msdk_get_platform_codename (session);
508
509 thiz->has_vpp = FALSE;
510 if (thiz->use_video_memory)
511 gst_msdk_set_frame_allocator (thiz->context);
512
513 encoder_input_fmt = GST_VIDEO_INFO_FORMAT (info);
514 need_vpp = klass->need_conversion (thiz, info, &encoder_input_fmt);
515
516 if (need_vpp) {
517 switch (GST_VIDEO_INFO_FORMAT (info)) {
518 case GST_VIDEO_FORMAT_YV12:
519 case GST_VIDEO_FORMAT_I420:
520 thiz->vpp_param.vpp.In.FourCC = MFX_FOURCC_YV12;
521 thiz->vpp_param.vpp.In.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
522 break;
523 case GST_VIDEO_FORMAT_YUY2:
524 thiz->vpp_param.vpp.In.FourCC = MFX_FOURCC_YUY2;
525 thiz->vpp_param.vpp.In.ChromaFormat = MFX_CHROMAFORMAT_YUV422;
526 break;
527 case GST_VIDEO_FORMAT_UYVY:
528 thiz->vpp_param.vpp.In.FourCC = MFX_FOURCC_UYVY;
529 thiz->vpp_param.vpp.In.ChromaFormat = MFX_CHROMAFORMAT_YUV422;
530 break;
531 case GST_VIDEO_FORMAT_BGRA:
532 thiz->vpp_param.vpp.In.FourCC = MFX_FOURCC_RGB4;
533 thiz->vpp_param.vpp.In.ChromaFormat = MFX_CHROMAFORMAT_YUV444;
534 break;
535 default:
536 g_assert_not_reached ();
537 break;
538 }
539
540 if (thiz->use_video_memory)
541 thiz->vpp_param.IOPattern =
542 MFX_IOPATTERN_IN_VIDEO_MEMORY | MFX_IOPATTERN_OUT_VIDEO_MEMORY;
543 else
544 thiz->vpp_param.IOPattern =
545 MFX_IOPATTERN_IN_SYSTEM_MEMORY | MFX_IOPATTERN_OUT_SYSTEM_MEMORY;
546
547 thiz->vpp_param.vpp.In.Width = GST_ROUND_UP_16 (info->width);
548 thiz->vpp_param.vpp.In.Height = GST_ROUND_UP_32 (info->height);
549 thiz->vpp_param.vpp.In.CropW = info->width;
550 thiz->vpp_param.vpp.In.CropH = info->height;
551 thiz->vpp_param.vpp.In.FrameRateExtN = info->fps_n;
552 thiz->vpp_param.vpp.In.FrameRateExtD = info->fps_d;
553 thiz->vpp_param.vpp.In.AspectRatioW = info->par_n;
554 thiz->vpp_param.vpp.In.AspectRatioH = info->par_d;
555 thiz->vpp_param.vpp.In.PicStruct = MFX_PICSTRUCT_PROGRESSIVE;
556
557 /* work-around to avoid zero fps in msdk structure */
558 if (0 == thiz->vpp_param.vpp.In.FrameRateExtN)
559 thiz->vpp_param.vpp.In.FrameRateExtN = 30;
560
561 thiz->vpp_param.vpp.Out = thiz->vpp_param.vpp.In;
562
563 switch (encoder_input_fmt) {
564 case GST_VIDEO_FORMAT_P010_10LE:
565 thiz->vpp_param.vpp.Out.FourCC = MFX_FOURCC_P010;
566 thiz->vpp_param.vpp.Out.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
567 break;
568
569 case GST_VIDEO_FORMAT_YUY2:
570 thiz->vpp_param.vpp.Out.FourCC = MFX_FOURCC_YUY2;
571 thiz->vpp_param.vpp.Out.ChromaFormat = MFX_CHROMAFORMAT_YUV422;
572 break;
573
574 default:
575 thiz->vpp_param.vpp.Out.FourCC = MFX_FOURCC_NV12;
576 thiz->vpp_param.vpp.Out.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
577 break;
578 }
579
580 /* validate parameters and allow MFX to make adjustments */
581 status = MFXVideoVPP_Query (session, &thiz->vpp_param, &thiz->vpp_param);
582 if (status < MFX_ERR_NONE) {
583 GST_ERROR_OBJECT (thiz, "Video VPP Query failed (%s)",
584 msdk_status_to_string (status));
585 goto failed;
586 } else if (status > MFX_ERR_NONE) {
587 GST_WARNING_OBJECT (thiz, "Video VPP Query returned: %s",
588 msdk_status_to_string (status));
589 }
590
591 status = MFXVideoVPP_QueryIOSurf (session, &thiz->vpp_param, request);
592 if (status < MFX_ERR_NONE) {
593 GST_ERROR_OBJECT (thiz, "VPP Query IO surfaces failed (%s)",
594 msdk_status_to_string (status));
595 goto failed;
596 } else if (status > MFX_ERR_NONE) {
597 GST_WARNING_OBJECT (thiz, "VPP Query IO surfaces returned: %s",
598 msdk_status_to_string (status));
599 }
600
601 if (thiz->use_video_memory)
602 request[0].NumFrameSuggested +=
603 gst_msdk_context_get_shared_async_depth (thiz->context);
604 thiz->num_vpp_surfaces = request[0].NumFrameSuggested;
605
606 if (thiz->use_video_memory)
607 gst_msdk_frame_alloc (thiz->context, &(request[0]),
608 &thiz->vpp_alloc_resp);
609
610 status = MFXVideoVPP_Init (session, &thiz->vpp_param);
611 if (status < MFX_ERR_NONE) {
612 GST_ERROR_OBJECT (thiz, "Init failed (%s)",
613 msdk_status_to_string (status));
614 goto no_vpp_free_resource;
615 } else if (status > MFX_ERR_NONE) {
616 GST_WARNING_OBJECT (thiz, "Init returned: %s",
617 msdk_status_to_string (status));
618 }
619
620 status = MFXVideoVPP_GetVideoParam (session, &thiz->vpp_param);
621 if (status < MFX_ERR_NONE) {
622 mfxStatus status1;
623 GST_ERROR_OBJECT (thiz, "Get VPP Parameters failed (%s)",
624 msdk_status_to_string (status));
625 status1 = MFXVideoVPP_Close (session);
626 if (status1 != MFX_ERR_NONE && status1 != MFX_ERR_NOT_INITIALIZED)
627 GST_WARNING_OBJECT (thiz, "VPP close failed (%s)",
628 msdk_status_to_string (status1));
629
630 goto no_vpp_free_resource;
631 } else if (status > MFX_ERR_NONE) {
632 GST_WARNING_OBJECT (thiz, "Get VPP Parameters returned: %s",
633 msdk_status_to_string (status));
634 }
635
636 thiz->has_vpp = TRUE;
637 }
638
639 thiz->param.AsyncDepth = thiz->async_depth;
640 if (thiz->use_video_memory)
641 thiz->param.IOPattern = MFX_IOPATTERN_IN_VIDEO_MEMORY;
642 else
643 thiz->param.IOPattern = MFX_IOPATTERN_IN_SYSTEM_MEMORY;
644
645 thiz->param.mfx.TargetUsage = thiz->target_usage;
646 thiz->param.mfx.GopPicSize = thiz->gop_size;
647 thiz->param.mfx.GopRefDist = thiz->b_frames + 1;
648 thiz->param.mfx.IdrInterval = thiz->i_frames;
649 thiz->param.mfx.NumSlice = thiz->num_slices;
650 thiz->param.mfx.NumRefFrame = thiz->ref_frames;
651 thiz->param.mfx.EncodedOrder = 0; /* Take input frames in display order */
652
653 thiz->param.mfx.FrameInfo.Width = GST_ROUND_UP_16 (info->width);
654 thiz->param.mfx.FrameInfo.Height = GST_ROUND_UP_32 (info->height);
655 thiz->param.mfx.FrameInfo.CropW = info->width;
656 thiz->param.mfx.FrameInfo.CropH = info->height;
657 thiz->param.mfx.FrameInfo.FrameRateExtN = info->fps_n;
658 thiz->param.mfx.FrameInfo.FrameRateExtD = info->fps_d;
659 thiz->param.mfx.FrameInfo.AspectRatioW = info->par_n;
660 thiz->param.mfx.FrameInfo.AspectRatioH = info->par_d;
661 thiz->param.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE;
662 thiz->param.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
663
664 switch (encoder_input_fmt) {
665 case GST_VIDEO_FORMAT_P010_10LE:
666 thiz->param.mfx.FrameInfo.FourCC = MFX_FOURCC_P010;
667 thiz->param.mfx.FrameInfo.BitDepthLuma = 10;
668 thiz->param.mfx.FrameInfo.BitDepthChroma = 10;
669 thiz->param.mfx.FrameInfo.Shift = 1;
670 break;
671 case GST_VIDEO_FORMAT_VUYA:
672 thiz->param.mfx.FrameInfo.FourCC = MFX_FOURCC_AYUV;
673 thiz->param.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV444;
674 thiz->param.mfx.FrameInfo.BitDepthLuma = 8;
675 thiz->param.mfx.FrameInfo.BitDepthChroma = 8;
676 break;
677 #if (MFX_VERSION >= 1027)
678 case GST_VIDEO_FORMAT_Y410:
679 thiz->param.mfx.FrameInfo.FourCC = MFX_FOURCC_Y410;
680 thiz->param.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV444;
681 thiz->param.mfx.FrameInfo.BitDepthLuma = 10;
682 thiz->param.mfx.FrameInfo.BitDepthChroma = 10;
683 break;
684 case GST_VIDEO_FORMAT_Y210:
685 thiz->param.mfx.FrameInfo.FourCC = MFX_FOURCC_Y210;
686 thiz->param.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV422;
687 thiz->param.mfx.FrameInfo.BitDepthLuma = 10;
688 thiz->param.mfx.FrameInfo.BitDepthChroma = 10;
689 thiz->param.mfx.FrameInfo.Shift = 1;
690 break;
691 #endif
692 case GST_VIDEO_FORMAT_BGRA:
693 thiz->param.mfx.FrameInfo.FourCC = MFX_FOURCC_RGB4;
694 thiz->param.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV444;
695 thiz->param.mfx.FrameInfo.BitDepthLuma = 8;
696 thiz->param.mfx.FrameInfo.BitDepthChroma = 8;
697 break;
698 case GST_VIDEO_FORMAT_BGR10A2_LE:
699 thiz->param.mfx.FrameInfo.FourCC = MFX_FOURCC_A2RGB10;
700 thiz->param.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV444;
701 thiz->param.mfx.FrameInfo.BitDepthLuma = 10;
702 thiz->param.mfx.FrameInfo.BitDepthChroma = 10;
703 break;
704 case GST_VIDEO_FORMAT_YUY2:
705 thiz->param.mfx.FrameInfo.FourCC = MFX_FOURCC_YUY2;
706 thiz->param.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV422;
707 thiz->param.mfx.FrameInfo.BitDepthLuma = 8;
708 thiz->param.mfx.FrameInfo.BitDepthChroma = 8;
709 break;
710 #if (MFX_VERSION >= 1031)
711 case GST_VIDEO_FORMAT_P012_LE:
712 thiz->param.mfx.FrameInfo.FourCC = MFX_FOURCC_P016;
713 thiz->param.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
714 thiz->param.mfx.FrameInfo.BitDepthLuma = 12;
715 thiz->param.mfx.FrameInfo.BitDepthChroma = 12;
716 thiz->param.mfx.FrameInfo.Shift = 1;
717 break;
718 #endif
719 default:
720 thiz->param.mfx.FrameInfo.FourCC = MFX_FOURCC_NV12;
721 thiz->param.mfx.FrameInfo.BitDepthLuma = 8;
722 thiz->param.mfx.FrameInfo.BitDepthChroma = 8;
723 }
724
725 /* work-around to avoid zero fps in msdk structure */
726 if (0 == thiz->param.mfx.FrameInfo.FrameRateExtN)
727 thiz->param.mfx.FrameInfo.FrameRateExtN = 30;
728
729 /* ensure bitrate control parameters */
730 ensure_bitrate_control (thiz);
731
732 /* allow subclass configure further */
733 if (klass->configure) {
734 if (!klass->configure (thiz))
735 goto failed;
736 }
737
738 /* If color properties are available from upstream, set it and pass to MediaSDK here.
739 * MJPEG and VP9 are excluded as MediaSDK does not support to handle video param
740 * extbuff with buffer id equals to MFX_EXTBUFF_VIDEO_SIGNAL_INFO.
741 */
742 if (thiz->param.mfx.CodecId != MFX_CODEC_JPEG &&
743 thiz->param.mfx.CodecId != MFX_CODEC_VP9 &&
744 (info->colorimetry.primaries || info->colorimetry.transfer
745 || info->colorimetry.matrix)) {
746 memset (&ext_vsi, 0, sizeof (ext_vsi));
747 ext_vsi.Header.BufferId = MFX_EXTBUFF_VIDEO_SIGNAL_INFO;
748 ext_vsi.Header.BufferSz = sizeof (ext_vsi);
749 ext_vsi.ColourDescriptionPresent = 1;
750 ext_vsi.ColourPrimaries =
751 gst_video_color_primaries_to_iso (info->colorimetry.primaries);
752 ext_vsi.TransferCharacteristics =
753 gst_video_transfer_function_to_iso (info->colorimetry.transfer);
754 ext_vsi.MatrixCoefficients =
755 gst_video_color_matrix_to_iso (info->colorimetry.matrix);
756 gst_msdkenc_add_extra_param (thiz, (mfxExtBuffer *) & ext_vsi);
757 }
758
759 if (thiz->num_extra_params) {
760 thiz->param.NumExtParam = thiz->num_extra_params;
761 thiz->param.ExtParam = thiz->extra_params;
762 }
763
764 /* validate parameters and allow MFX to make adjustments */
765 status = MFXVideoENCODE_Query (session, &thiz->param, &thiz->param);
766 if (status < MFX_ERR_NONE) {
767 GST_ERROR_OBJECT (thiz, "Video Encode Query failed (%s)",
768 msdk_status_to_string (status));
769 goto failed;
770 } else if (status > MFX_ERR_NONE) {
771 GST_WARNING_OBJECT (thiz, "Video Encode Query returned: %s",
772 msdk_status_to_string (status));
773 }
774
775 status = MFXVideoENCODE_QueryIOSurf (session, &thiz->param, request);
776 if (status < MFX_ERR_NONE) {
777 GST_ERROR_OBJECT (thiz, "Encode Query IO surfaces failed (%s)",
778 msdk_status_to_string (status));
779 goto failed;
780 } else if (status > MFX_ERR_NONE) {
781 GST_WARNING_OBJECT (thiz, "Encode Query IO surfaces returned: %s",
782 msdk_status_to_string (status));
783 }
784
785 request[0].NumFrameSuggested += thiz->num_extra_frames;
786
787 if (thiz->has_vpp)
788 request[0].NumFrameSuggested += thiz->num_vpp_surfaces + 1 - 4;
789
790 if (thiz->use_video_memory) {
791 if (thiz->use_dmabuf && !thiz->has_vpp)
792 request[0].Type |= MFX_MEMTYPE_EXPORT_FRAME;
793 gst_msdk_frame_alloc (thiz->context, &(request[0]), &thiz->alloc_resp);
794 }
795
796 /* Maximum of VPP output and encoder input, if using VPP */
797 if (thiz->has_vpp)
798 request[0].NumFrameSuggested =
799 MAX (request[0].NumFrameSuggested, request[1].NumFrameSuggested);
800 if (request[0].NumFrameSuggested < thiz->param.AsyncDepth) {
801 GST_ERROR_OBJECT (thiz, "Required %d surfaces (%d suggested), async %d",
802 request[0].NumFrameMin, request[0].NumFrameSuggested,
803 thiz->param.AsyncDepth);
804 goto failed;
805 }
806
807 /* This is VPP output (if any) and encoder input */
808 thiz->num_surfaces = request[0].NumFrameSuggested;
809
810 GST_DEBUG_OBJECT (thiz, "Required %d surfaces (%d suggested), allocated %d",
811 request[0].NumFrameMin, request[0].NumFrameSuggested, thiz->num_surfaces);
812
813 status = MFXVideoENCODE_Init (session, &thiz->param);
814 if (status < MFX_ERR_NONE) {
815 GST_ERROR_OBJECT (thiz, "Init failed (%s)", msdk_status_to_string (status));
816 goto failed;
817 } else if (status > MFX_ERR_NONE) {
818 GST_WARNING_OBJECT (thiz, "Init returned: %s",
819 msdk_status_to_string (status));
820 }
821
822 status = MFXVideoENCODE_GetVideoParam (session, &thiz->param);
823 if (status < MFX_ERR_NONE) {
824 GST_ERROR_OBJECT (thiz, "Get Video Parameters failed (%s)",
825 msdk_status_to_string (status));
826 goto failed;
827 } else if (status > MFX_ERR_NONE) {
828 GST_WARNING_OBJECT (thiz, "Get Video Parameters returned: %s",
829 msdk_status_to_string (status));
830 }
831
832 thiz->num_tasks = thiz->param.AsyncDepth;
833 thiz->tasks = g_new0 (MsdkEncTask, thiz->num_tasks);
834 for (i = 0; i < thiz->num_tasks; i++) {
835 thiz->tasks[i].output_bitstream.Data = _aligned_alloc (32,
836 thiz->param.mfx.BufferSizeInKB * thiz->param.mfx.BRCParamMultiplier *
837 1024);
838 if (!thiz->tasks[i].output_bitstream.Data) {
839 GST_ERROR_OBJECT (thiz, "Memory allocation failed");
840 goto failed;
841 }
842 thiz->tasks[i].output_bitstream.MaxLength =
843 thiz->param.mfx.BufferSizeInKB * thiz->param.mfx.BRCParamMultiplier *
844 1024;
845 }
846 thiz->next_task = 0;
847
848 thiz->reconfig = FALSE;
849 thiz->initialized = TRUE;
850
851 GST_OBJECT_UNLOCK (thiz);
852
853 return TRUE;
854
855 no_vpp_free_resource:
856 if (thiz->use_video_memory)
857 gst_msdk_frame_free (thiz->context, &thiz->vpp_alloc_resp);
858 failed:
859 GST_OBJECT_UNLOCK (thiz);
860 return FALSE;
861 }
862
863 static void
gst_msdkenc_close_encoder(GstMsdkEnc * thiz)864 gst_msdkenc_close_encoder (GstMsdkEnc * thiz)
865 {
866 guint i;
867 mfxStatus status;
868
869 if (!thiz->context || !thiz->initialized)
870 return;
871
872 GST_DEBUG_OBJECT (thiz, "Closing encoder with context %" GST_PTR_FORMAT,
873 thiz->context);
874
875 gst_clear_object (&thiz->msdk_pool);
876 gst_clear_object (&thiz->msdk_converted_pool);
877
878 if (thiz->use_video_memory)
879 gst_msdk_frame_free (thiz->context, &thiz->alloc_resp);
880
881 status = MFXVideoENCODE_Close (gst_msdk_context_get_session (thiz->context));
882 if (status != MFX_ERR_NONE && status != MFX_ERR_NOT_INITIALIZED) {
883 GST_WARNING_OBJECT (thiz, "Encoder close failed (%s)",
884 msdk_status_to_string (status));
885 }
886
887 if (thiz->tasks) {
888 for (i = 0; i < thiz->num_tasks; i++) {
889 MsdkEncTask *task = &thiz->tasks[i];
890 if (task->output_bitstream.Data) {
891 _aligned_free (task->output_bitstream.Data);
892 }
893 }
894 }
895 g_free (thiz->tasks);
896 thiz->tasks = NULL;
897
898 /* Close VPP before freeing the surfaces. They are shared between encoder
899 * and VPP */
900 if (thiz->has_vpp) {
901 if (thiz->use_video_memory)
902 gst_msdk_frame_free (thiz->context, &thiz->vpp_alloc_resp);
903
904 status = MFXVideoVPP_Close (gst_msdk_context_get_session (thiz->context));
905 if (status != MFX_ERR_NONE && status != MFX_ERR_NOT_INITIALIZED) {
906 GST_WARNING_OBJECT (thiz, "VPP close failed (%s)",
907 msdk_status_to_string (status));
908 }
909 }
910
911 memset (&thiz->param, 0, sizeof (thiz->param));
912 thiz->num_extra_params = 0;
913 thiz->initialized = FALSE;
914 }
915
916 typedef struct
917 {
918 GstVideoCodecFrame *frame;
919 MsdkSurface *frame_surface;
920 MsdkSurface *converted_surface;
921 } FrameData;
922
923 static FrameData *
gst_msdkenc_queue_frame(GstMsdkEnc * thiz,GstVideoCodecFrame * frame,GstVideoInfo * info)924 gst_msdkenc_queue_frame (GstMsdkEnc * thiz, GstVideoCodecFrame * frame,
925 GstVideoInfo * info)
926 {
927 FrameData *fdata;
928
929 fdata = g_slice_new (FrameData);
930 fdata->frame = gst_video_codec_frame_ref (frame);
931
932 thiz->pending_frames = g_list_prepend (thiz->pending_frames, fdata);
933
934 return fdata;
935 }
936
937 static MsdkSurface *
gst_msdkenc_create_surface(mfxFrameSurface1 * surface,GstBuffer * buf)938 gst_msdkenc_create_surface (mfxFrameSurface1 * surface, GstBuffer * buf)
939 {
940 MsdkSurface *msdk_surface;
941 msdk_surface = g_slice_new0 (MsdkSurface);
942 msdk_surface->surface = surface;
943 msdk_surface->buf = buf;
944
945 return msdk_surface;
946 }
947
948 static void
gst_msdkenc_free_surface(MsdkSurface * surface)949 gst_msdkenc_free_surface (MsdkSurface * surface)
950 {
951 if (surface->buf)
952 gst_buffer_unref (surface->buf);
953
954 g_slice_free (MsdkSurface, surface);
955 }
956
957 static void
gst_msdkenc_free_frame_data(GstMsdkEnc * thiz,FrameData * fdata)958 gst_msdkenc_free_frame_data (GstMsdkEnc * thiz, FrameData * fdata)
959 {
960 if (fdata->frame_surface)
961 gst_msdkenc_free_surface (fdata->frame_surface);
962 if (thiz->has_vpp)
963 gst_msdkenc_free_surface (fdata->converted_surface);
964
965 gst_video_codec_frame_unref (fdata->frame);
966 g_slice_free (FrameData, fdata);
967 }
968
969 static void
gst_msdkenc_dequeue_frame(GstMsdkEnc * thiz,GstVideoCodecFrame * frame)970 gst_msdkenc_dequeue_frame (GstMsdkEnc * thiz, GstVideoCodecFrame * frame)
971 {
972 GList *l;
973
974 for (l = thiz->pending_frames; l;) {
975 FrameData *fdata = l->data;
976 GList *l1 = l;
977
978 l = l->next;
979
980 if (fdata->frame != frame)
981 continue;
982
983 gst_msdkenc_free_frame_data (thiz, fdata);
984
985 thiz->pending_frames = g_list_delete_link (thiz->pending_frames, l1);
986 return;
987 }
988 }
989
990 static void
gst_msdkenc_dequeue_all_frames(GstMsdkEnc * thiz)991 gst_msdkenc_dequeue_all_frames (GstMsdkEnc * thiz)
992 {
993 GList *l;
994
995 for (l = thiz->pending_frames; l; l = l->next) {
996 FrameData *fdata = l->data;
997
998 gst_msdkenc_free_frame_data (thiz, fdata);
999 }
1000 g_list_free (thiz->pending_frames);
1001 thiz->pending_frames = NULL;
1002 }
1003
1004 static MsdkEncTask *
gst_msdkenc_get_free_task(GstMsdkEnc * thiz)1005 gst_msdkenc_get_free_task (GstMsdkEnc * thiz)
1006 {
1007 MsdkEncTask *tasks = thiz->tasks;
1008 guint size = thiz->num_tasks;
1009 guint start = thiz->next_task;
1010 guint i;
1011
1012 if (tasks) {
1013 for (i = 0; i < size; i++) {
1014 guint t = (start + i) % size;
1015 if (tasks[t].sync_point == NULL)
1016 return &tasks[t];
1017 }
1018 }
1019 return NULL;
1020 }
1021
1022 static void
gst_msdkenc_reset_task(MsdkEncTask * task)1023 gst_msdkenc_reset_task (MsdkEncTask * task)
1024 {
1025 task->output_bitstream.DataLength = 0;
1026 task->sync_point = NULL;
1027 }
1028
1029 static GstVideoCodecFrame *
gst_msdkenc_find_best_frame(GstMsdkEnc * thiz,GList * frames,mfxBitstream * bitstream)1030 gst_msdkenc_find_best_frame (GstMsdkEnc * thiz, GList * frames,
1031 mfxBitstream * bitstream)
1032 {
1033 GList *iter;
1034 GstVideoCodecFrame *ret = NULL;
1035 GstClockTime pts;
1036 GstClockTimeDiff best_diff = GST_CLOCK_STIME_NONE;
1037
1038 if (!bitstream)
1039 return NULL;
1040
1041 if (bitstream->TimeStamp == MFX_TIMESTAMP_UNKNOWN) {
1042 pts = GST_CLOCK_TIME_NONE;
1043 } else {
1044 pts = gst_util_uint64_scale (bitstream->TimeStamp, GST_SECOND, 90000);
1045 }
1046
1047 for (iter = frames; iter; iter = g_list_next (iter)) {
1048 GstVideoCodecFrame *frame = (GstVideoCodecFrame *) iter->data;
1049
1050 /* if we don't know the time stamp, find the first frame which
1051 * has unknown timestamp */
1052 if (!GST_CLOCK_TIME_IS_VALID (pts)) {
1053 if (!GST_CLOCK_TIME_IS_VALID (frame->pts)) {
1054 ret = frame;
1055 break;
1056 }
1057 } else {
1058 GstClockTimeDiff abs_diff = ABS (GST_CLOCK_DIFF (frame->pts, pts));
1059 if (abs_diff == 0) {
1060 ret = frame;
1061 break;
1062 }
1063
1064 if (!GST_CLOCK_STIME_IS_VALID (best_diff) || abs_diff < best_diff) {
1065 ret = frame;
1066 best_diff = abs_diff;
1067 }
1068 }
1069 }
1070
1071 if (ret)
1072 gst_video_codec_frame_ref (ret);
1073
1074 return ret;
1075 }
1076
1077 static GstFlowReturn
gst_msdkenc_finish_frame(GstMsdkEnc * thiz,MsdkEncTask * task,gboolean discard)1078 gst_msdkenc_finish_frame (GstMsdkEnc * thiz, MsdkEncTask * task,
1079 gboolean discard)
1080 {
1081 GstVideoCodecFrame *frame;
1082 GList *list;
1083
1084 if (!task->sync_point)
1085 return GST_FLOW_OK;
1086
1087 list = gst_video_encoder_get_frames (GST_VIDEO_ENCODER (thiz));
1088
1089 if (!list) {
1090 GST_ERROR_OBJECT (thiz, "failed to get list of frame");
1091 return GST_FLOW_ERROR;
1092 }
1093
1094 /* Wait for encoding operation to complete, the magic number 300000 below
1095 * is used in MSDK samples
1096 * #define MSDK_ENC_WAIT_INTERVAL 300000
1097 */
1098 if (MFXVideoCORE_SyncOperation (gst_msdk_context_get_session (thiz->context),
1099 task->sync_point, 300000) != MFX_ERR_NONE)
1100 GST_WARNING_OBJECT (thiz, "failed to do sync operation");
1101
1102 if (!discard && task->output_bitstream.DataLength) {
1103 GstBuffer *out_buf = NULL;
1104 guint8 *data =
1105 task->output_bitstream.Data + task->output_bitstream.DataOffset;
1106 gsize size = task->output_bitstream.DataLength;
1107
1108 frame = gst_msdkenc_find_best_frame (thiz, list, &task->output_bitstream);
1109 if (!frame) {
1110 /* just pick the oldest one */
1111 frame = gst_video_encoder_get_oldest_frame (GST_VIDEO_ENCODER (thiz));
1112 }
1113
1114 out_buf = gst_buffer_new_allocate (NULL, size, NULL);
1115 gst_buffer_fill (out_buf, 0, data, size);
1116 frame->output_buffer = out_buf;
1117 frame->pts =
1118 gst_util_uint64_scale (task->output_bitstream.TimeStamp, GST_SECOND,
1119 90000);
1120 frame->dts =
1121 gst_util_uint64_scale (task->output_bitstream.DecodeTimeStamp,
1122 GST_SECOND, 90000);
1123
1124 if ((task->output_bitstream.FrameType & MFX_FRAMETYPE_IDR) != 0 ||
1125 (task->output_bitstream.FrameType & MFX_FRAMETYPE_xIDR) != 0) {
1126 GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
1127 }
1128
1129 /* Mark task as available */
1130 gst_msdkenc_reset_task (task);
1131 } else {
1132 frame = gst_video_encoder_get_oldest_frame (GST_VIDEO_ENCODER (thiz));
1133 }
1134
1135 g_list_free_full (list, (GDestroyNotify) gst_video_codec_frame_unref);
1136
1137 gst_video_codec_frame_unref (frame);
1138 gst_msdkenc_dequeue_frame (thiz, frame);
1139
1140 return gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (thiz), frame);
1141 }
1142
1143 static GstFlowReturn
gst_msdkenc_encode_frame(GstMsdkEnc * thiz,mfxFrameSurface1 * surface,GstVideoCodecFrame * input_frame)1144 gst_msdkenc_encode_frame (GstMsdkEnc * thiz, mfxFrameSurface1 * surface,
1145 GstVideoCodecFrame * input_frame)
1146 {
1147 mfxSession session;
1148 MsdkEncTask *task;
1149 mfxStatus status;
1150
1151 if (G_UNLIKELY (thiz->context == NULL)) {
1152 gst_msdkenc_dequeue_frame (thiz, input_frame);
1153 gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (thiz), input_frame);
1154 return GST_FLOW_NOT_NEGOTIATED;
1155 }
1156 session = gst_msdk_context_get_session (thiz->context);
1157
1158 task = gst_msdkenc_get_free_task (thiz);
1159
1160 for (;;) {
1161 /* Force key-frame if needed */
1162 if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (input_frame))
1163 thiz->enc_cntrl.FrameType =
1164 MFX_FRAMETYPE_I | MFX_FRAMETYPE_IDR | MFX_FRAMETYPE_REF;
1165 else
1166 thiz->enc_cntrl.FrameType = MFX_FRAMETYPE_UNKNOWN;
1167
1168 status =
1169 MFXVideoENCODE_EncodeFrameAsync (session, &thiz->enc_cntrl, surface,
1170 &task->output_bitstream, &task->sync_point);
1171 if (status != MFX_WRN_DEVICE_BUSY)
1172 break;
1173 /* If device is busy, wait 1ms and retry, as per MSDK's recomendation */
1174 g_usleep (1000);
1175 };
1176
1177 if (status != MFX_ERR_NONE && status != MFX_ERR_MORE_DATA) {
1178 GST_ELEMENT_ERROR (thiz, STREAM, ENCODE, ("Encode frame failed."),
1179 ("MSDK encode error (%s)", msdk_status_to_string (status)));
1180 gst_msdkenc_dequeue_frame (thiz, input_frame);
1181 gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (thiz), input_frame);
1182 return GST_FLOW_ERROR;
1183 }
1184
1185 if (task->sync_point) {
1186 thiz->next_task = ((task - thiz->tasks) + 1) % thiz->num_tasks;
1187 } else if (status == MFX_ERR_MORE_DATA) {
1188 gst_msdkenc_dequeue_frame (thiz, input_frame);
1189 }
1190
1191 /* Ensure that next task is available */
1192 task = thiz->tasks + thiz->next_task;
1193 return gst_msdkenc_finish_frame (thiz, task, FALSE);
1194 }
1195
1196 static guint
gst_msdkenc_maximum_delayed_frames(GstMsdkEnc * thiz)1197 gst_msdkenc_maximum_delayed_frames (GstMsdkEnc * thiz)
1198 {
1199 return thiz->num_tasks;
1200 }
1201
1202 static void
gst_msdkenc_set_latency(GstMsdkEnc * thiz)1203 gst_msdkenc_set_latency (GstMsdkEnc * thiz)
1204 {
1205 GstVideoInfo *info = &thiz->input_state->info;
1206 gint max_delayed_frames;
1207 GstClockTime latency;
1208
1209 max_delayed_frames = gst_msdkenc_maximum_delayed_frames (thiz);
1210
1211 if (info->fps_n) {
1212 latency = gst_util_uint64_scale_ceil (GST_SECOND * info->fps_d,
1213 max_delayed_frames, info->fps_n);
1214 } else {
1215 /* FIXME: Assume 25fps. This is better than reporting no latency at
1216 * all and then later failing in live pipelines
1217 */
1218 latency = gst_util_uint64_scale_ceil (GST_SECOND * 1,
1219 max_delayed_frames, 25);
1220 }
1221
1222 GST_INFO_OBJECT (thiz,
1223 "Updating latency to %" GST_TIME_FORMAT " (%d frames)",
1224 GST_TIME_ARGS (latency), max_delayed_frames);
1225
1226 gst_video_encoder_set_latency (GST_VIDEO_ENCODER (thiz), latency, latency);
1227 }
1228
1229 static void
gst_msdkenc_flush_frames(GstMsdkEnc * thiz,gboolean discard)1230 gst_msdkenc_flush_frames (GstMsdkEnc * thiz, gboolean discard)
1231 {
1232 mfxStatus status;
1233 mfxSession session;
1234 MsdkEncTask *task;
1235 guint i, t;
1236
1237 if (!thiz->tasks)
1238 return;
1239
1240 GST_DEBUG_OBJECT (thiz, "flush frames");
1241
1242 session = gst_msdk_context_get_session (thiz->context);
1243
1244 for (;;) {
1245 task = thiz->tasks + thiz->next_task;
1246 gst_msdkenc_finish_frame (thiz, task, FALSE);
1247
1248 status = MFXVideoENCODE_EncodeFrameAsync (session, NULL, NULL,
1249 &task->output_bitstream, &task->sync_point);
1250
1251 if (status != MFX_ERR_NONE && status != MFX_ERR_MORE_DATA) {
1252 GST_ELEMENT_ERROR (thiz, STREAM, ENCODE, ("Encode frame failed."),
1253 ("MSDK encode error (%s)", msdk_status_to_string (status)));
1254 break;
1255 }
1256
1257 if (task->sync_point) {
1258 thiz->next_task = ((task - thiz->tasks) + 1) % thiz->num_tasks;
1259 } else if (status == MFX_ERR_MORE_DATA) {
1260 break;
1261 }
1262 };
1263
1264 t = thiz->next_task;
1265 for (i = 0; i < thiz->num_tasks; i++) {
1266 gst_msdkenc_finish_frame (thiz, &thiz->tasks[t], discard);
1267 t = (t + 1) % thiz->num_tasks;
1268 }
1269 }
1270
1271 static gboolean
gst_msdkenc_set_src_caps(GstMsdkEnc * thiz)1272 gst_msdkenc_set_src_caps (GstMsdkEnc * thiz)
1273 {
1274 GstMsdkEncClass *klass = GST_MSDKENC_GET_CLASS (thiz);
1275 GstCaps *outcaps = NULL;
1276 GstVideoCodecState *state;
1277 GstTagList *tags;
1278
1279 if (klass->set_src_caps)
1280 outcaps = klass->set_src_caps (thiz);
1281
1282 if (!outcaps)
1283 return FALSE;
1284
1285 state = gst_video_encoder_set_output_state (GST_VIDEO_ENCODER (thiz),
1286 outcaps, thiz->input_state);
1287 GST_DEBUG_OBJECT (thiz, "output caps: %" GST_PTR_FORMAT, state->caps);
1288
1289 gst_video_codec_state_unref (state);
1290
1291 tags = gst_tag_list_new_empty ();
1292 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, "msdkenc",
1293 GST_TAG_MAXIMUM_BITRATE, thiz->bitrate * 1024,
1294 GST_TAG_NOMINAL_BITRATE, thiz->bitrate * 1024, NULL);
1295 gst_video_encoder_merge_tags (GST_VIDEO_ENCODER (thiz), tags,
1296 GST_TAG_MERGE_REPLACE);
1297 gst_tag_list_unref (tags);
1298
1299 return TRUE;
1300 }
1301
1302 static GstBufferPool *
gst_msdkenc_create_buffer_pool(GstMsdkEnc * thiz,GstCaps * caps,guint num_buffers,gboolean set_align)1303 gst_msdkenc_create_buffer_pool (GstMsdkEnc * thiz, GstCaps * caps,
1304 guint num_buffers, gboolean set_align)
1305 {
1306 GstBufferPool *pool = NULL;
1307 GstStructure *config;
1308 GstAllocator *allocator = NULL;
1309 GstVideoInfo info;
1310 GstVideoAlignment align;
1311 GstAllocationParams params = { 0, 31, 0, 0, };
1312 mfxFrameAllocResponse *alloc_resp = NULL;
1313
1314 if (thiz->has_vpp)
1315 alloc_resp = set_align ? &thiz->vpp_alloc_resp : &thiz->alloc_resp;
1316 else
1317 alloc_resp = &thiz->alloc_resp;
1318
1319 pool = gst_msdk_buffer_pool_new (thiz->context, alloc_resp);
1320 if (!pool)
1321 goto error_no_pool;
1322
1323 if (!gst_video_info_from_caps (&info, caps)) {
1324 GST_INFO_OBJECT (thiz, "failed to get video info");
1325 return NULL;
1326 }
1327
1328 gst_msdk_set_video_alignment (&info, 0, 0, &align);
1329 gst_video_info_align (&info, &align);
1330
1331 if (thiz->use_dmabuf)
1332 allocator =
1333 gst_msdk_dmabuf_allocator_new (thiz->context, &info, alloc_resp);
1334 else if (thiz->use_video_memory)
1335 allocator = gst_msdk_video_allocator_new (thiz->context, &info, alloc_resp);
1336 else
1337 allocator = gst_msdk_system_allocator_new (&info);
1338
1339 if (!allocator)
1340 goto error_no_allocator;
1341
1342 config = gst_buffer_pool_get_config (GST_BUFFER_POOL_CAST (pool));
1343 gst_buffer_pool_config_set_params (config, caps, info.size, num_buffers, 0);
1344 gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
1345 gst_buffer_pool_config_add_option (config,
1346 GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
1347
1348 if (thiz->use_video_memory) {
1349 gst_buffer_pool_config_add_option (config,
1350 GST_BUFFER_POOL_OPTION_MSDK_USE_VIDEO_MEMORY);
1351 if (thiz->use_dmabuf)
1352 gst_buffer_pool_config_add_option (config,
1353 GST_BUFFER_POOL_OPTION_MSDK_USE_DMABUF);
1354 }
1355
1356 gst_buffer_pool_config_set_video_alignment (config, &align);
1357 gst_buffer_pool_config_set_allocator (config, allocator, ¶ms);
1358 gst_object_unref (allocator);
1359
1360 if (!gst_buffer_pool_set_config (pool, config))
1361 goto error_pool_config;
1362
1363 if (set_align)
1364 thiz->aligned_info = info;
1365
1366 return pool;
1367
1368 error_no_pool:
1369 {
1370 GST_INFO_OBJECT (thiz, "failed to create bufferpool");
1371 return NULL;
1372 }
1373 error_no_allocator:
1374 {
1375 GST_INFO_OBJECT (thiz, "failed to create allocator");
1376 gst_object_unref (pool);
1377 return NULL;
1378 }
1379 error_pool_config:
1380 {
1381 GST_INFO_OBJECT (thiz, "failed to set config");
1382 gst_object_unref (pool);
1383 gst_object_unref (allocator);
1384 return NULL;
1385 }
1386 }
1387
1388 /* Fixme: Common routine used by all msdk elements, should be
1389 * moved to a common util file */
1390 static gboolean
_gst_caps_has_feature(const GstCaps * caps,const gchar * feature)1391 _gst_caps_has_feature (const GstCaps * caps, const gchar * feature)
1392 {
1393 guint i;
1394
1395 for (i = 0; i < gst_caps_get_size (caps); i++) {
1396 GstCapsFeatures *const features = gst_caps_get_features (caps, i);
1397 /* Skip ANY features, we need an exact match for correct evaluation */
1398 if (gst_caps_features_is_any (features))
1399 continue;
1400 if (gst_caps_features_contains (features, feature))
1401 return TRUE;
1402 }
1403 return FALSE;
1404 }
1405
1406 static gboolean
sinkpad_can_dmabuf(GstMsdkEnc * thiz)1407 sinkpad_can_dmabuf (GstMsdkEnc * thiz)
1408 {
1409 gboolean ret = FALSE;
1410 GstCaps *caps, *allowed_caps;
1411 GstPad *sinkpad;
1412
1413 sinkpad = GST_VIDEO_ENCODER_SINK_PAD (thiz);
1414 caps = gst_pad_get_pad_template_caps (sinkpad);
1415
1416 allowed_caps = gst_pad_peer_query_caps (sinkpad, caps);
1417 if (!allowed_caps)
1418 goto done;
1419 if (gst_caps_is_any (allowed_caps) || gst_caps_is_empty (allowed_caps)
1420 || allowed_caps == caps)
1421 goto done;
1422
1423 if (_gst_caps_has_feature (allowed_caps, GST_CAPS_FEATURE_MEMORY_DMABUF))
1424 ret = TRUE;
1425
1426 done:
1427 if (caps)
1428 gst_caps_unref (caps);
1429 if (allowed_caps)
1430 gst_caps_unref (allowed_caps);
1431 return ret;
1432 }
1433
1434 static gboolean
gst_msdkenc_set_format(GstVideoEncoder * encoder,GstVideoCodecState * state)1435 gst_msdkenc_set_format (GstVideoEncoder * encoder, GstVideoCodecState * state)
1436 {
1437 GstMsdkEnc *thiz = GST_MSDKENC (encoder);
1438 GstMsdkEncClass *klass = GST_MSDKENC_GET_CLASS (thiz);
1439
1440 if (state) {
1441 if (thiz->input_state) {
1442 if (!gst_video_info_is_equal (&thiz->input_state->info, &state->info)) {
1443 GST_INFO_OBJECT (thiz, "Re-init the encoder as info changed");
1444 gst_msdkenc_flush_frames (thiz, FALSE);
1445 gst_msdkenc_close_encoder (thiz);
1446 }
1447 gst_video_codec_state_unref (thiz->input_state);
1448 }
1449 thiz->input_state = gst_video_codec_state_ref (state);
1450 }
1451
1452 /* TODO: Currently d3d allocator is not implemented.
1453 * So encoder uses system memory by default on Windows.
1454 */
1455 #ifndef _WIN32
1456 thiz->use_video_memory = TRUE;
1457 #else
1458 thiz->use_video_memory = FALSE;
1459 #endif
1460
1461 GST_INFO_OBJECT (encoder, "This MSDK encoder uses %s memory",
1462 thiz->use_video_memory ? "video" : "system");
1463
1464 if (klass->set_format) {
1465 if (!klass->set_format (thiz))
1466 return FALSE;
1467 }
1468
1469 /* If upstream supports DMABufCapsfeatures, then we request for the dmabuf
1470 * based pipeline usage. Ideally we should have dmabuf support even with
1471 * raw-caps negotiation, but we don't have dmabuf-import support in msdk
1472 * plugin yet */
1473 if (sinkpad_can_dmabuf (thiz)) {
1474 thiz->input_state->caps = gst_caps_make_writable (thiz->input_state->caps);
1475 gst_caps_set_features (thiz->input_state->caps, 0,
1476 gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_DMABUF, NULL));
1477 thiz->use_dmabuf = TRUE;
1478 }
1479
1480 if (!gst_msdkenc_init_encoder (thiz))
1481 return FALSE;
1482
1483 if (!gst_msdkenc_set_src_caps (thiz)) {
1484 gst_msdkenc_close_encoder (thiz);
1485 return FALSE;
1486 }
1487
1488 if (!thiz->msdk_pool) {
1489 guint num_buffers = gst_msdkenc_maximum_delayed_frames (thiz) + 1;
1490 thiz->msdk_pool =
1491 gst_msdkenc_create_buffer_pool (thiz, thiz->input_state->caps,
1492 num_buffers, TRUE);
1493 }
1494
1495 gst_msdkenc_set_latency (thiz);
1496
1497 /* Create another bufferpool if VPP requires */
1498 if (thiz->has_vpp) {
1499 GstVideoInfo *info = &thiz->input_state->info;
1500 GstVideoInfo out_info;
1501 GstVideoFormat out_fmt;
1502 GstCaps *caps;
1503 GstBufferPool *pool = NULL;
1504
1505 gst_video_info_init (&out_info);
1506 out_fmt =
1507 gst_msdk_get_video_format_from_mfx_fourcc (thiz->vpp_param.vpp.
1508 Out.FourCC);
1509 gst_video_info_set_format (&out_info, out_fmt, info->width, info->height);
1510 caps = gst_video_info_to_caps (&out_info);
1511
1512 /* If there's an existing pool try to reuse it when is compatible */
1513 if (thiz->msdk_converted_pool) {
1514 GstStructure *config;
1515 GstCaps *pool_caps;
1516 gboolean is_pool_compatible = FALSE;
1517
1518 config = gst_buffer_pool_get_config (thiz->msdk_converted_pool);
1519 gst_buffer_pool_config_get_params (config, &pool_caps, NULL, NULL, NULL);
1520 if (caps && pool_caps)
1521 is_pool_compatible = gst_caps_is_equal (caps, pool_caps);
1522 gst_structure_free (config);
1523
1524 /* If caps are the same then we are done */
1525 if (is_pool_compatible) {
1526 gst_caps_unref (caps);
1527 goto done;
1528 }
1529 /* Release current pool because we are going to create a new one */
1530 gst_clear_object (&thiz->msdk_converted_pool);
1531 }
1532
1533 /* Otherwise create a new pool */
1534 pool =
1535 gst_msdkenc_create_buffer_pool (thiz, caps, thiz->num_surfaces, FALSE);
1536
1537 thiz->msdk_converted_pool = pool;
1538 gst_caps_unref (caps);
1539 }
1540
1541 done:
1542 return TRUE;
1543 }
1544
1545 static MsdkSurface *
gst_msdkenc_get_surface_from_pool(GstMsdkEnc * thiz,GstBufferPool * pool,GstBufferPoolAcquireParams * params)1546 gst_msdkenc_get_surface_from_pool (GstMsdkEnc * thiz, GstBufferPool * pool,
1547 GstBufferPoolAcquireParams * params)
1548 {
1549 GstBuffer *new_buffer;
1550 mfxFrameSurface1 *new_surface;
1551 MsdkSurface *msdk_surface;
1552
1553 if (!gst_buffer_pool_is_active (pool) &&
1554 !gst_buffer_pool_set_active (pool, TRUE)) {
1555 GST_ERROR_OBJECT (pool, "failed to activate buffer pool");
1556 return NULL;
1557 }
1558
1559 if (gst_buffer_pool_acquire_buffer (pool, &new_buffer, params) != GST_FLOW_OK) {
1560 GST_ERROR_OBJECT (pool, "failed to acquire a buffer from pool");
1561 return NULL;
1562 }
1563
1564 if (gst_msdk_is_msdk_buffer (new_buffer))
1565 new_surface = gst_msdk_get_surface_from_buffer (new_buffer);
1566 else {
1567 GST_ERROR_OBJECT (pool, "the acquired memory is not MSDK memory");
1568 return NULL;
1569 }
1570
1571 msdk_surface = gst_msdkenc_create_surface (new_surface, new_buffer);
1572
1573 return msdk_surface;
1574 }
1575
1576 #ifndef _WIN32
1577 static gboolean
import_dmabuf_to_msdk_surface(GstMsdkEnc * thiz,GstBuffer * buf,MsdkSurface * msdk_surface)1578 import_dmabuf_to_msdk_surface (GstMsdkEnc * thiz, GstBuffer * buf,
1579 MsdkSurface * msdk_surface)
1580 {
1581 GstMemory *mem = NULL;
1582 GstVideoInfo vinfo;
1583 GstVideoMeta *vmeta;
1584 GstMsdkMemoryID *msdk_mid = NULL;
1585 mfxFrameSurface1 *mfx_surface = NULL;
1586 gint fd, i;
1587 mem = gst_buffer_peek_memory (buf, 0);
1588 fd = gst_dmabuf_memory_get_fd (mem);
1589 if (fd < 0)
1590 return FALSE;
1591
1592 vinfo = thiz->input_state->info;
1593 /* Update offset/stride/size if there is VideoMeta attached to
1594 * the buffer */
1595 vmeta = gst_buffer_get_video_meta (buf);
1596 if (vmeta) {
1597 if (GST_VIDEO_INFO_FORMAT (&vinfo) != vmeta->format ||
1598 GST_VIDEO_INFO_WIDTH (&vinfo) != vmeta->width ||
1599 GST_VIDEO_INFO_HEIGHT (&vinfo) != vmeta->height ||
1600 GST_VIDEO_INFO_N_PLANES (&vinfo) != vmeta->n_planes) {
1601 GST_ERROR_OBJECT (thiz, "VideoMeta attached to buffer is not matching"
1602 "the negotiated width/height/format");
1603 return FALSE;
1604 }
1605 for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&vinfo); ++i) {
1606 GST_VIDEO_INFO_PLANE_OFFSET (&vinfo, i) = vmeta->offset[i];
1607 GST_VIDEO_INFO_PLANE_STRIDE (&vinfo, i) = vmeta->stride[i];
1608 }
1609 GST_VIDEO_INFO_SIZE (&vinfo) = gst_buffer_get_size (buf);
1610 }
1611
1612 /* Upstream neither accepted the msdk pool nor the msdk buffer size restrictions.
1613 * Current media-driver and GMMLib will fail due to strict memory size restrictions.
1614 * Ideally, media-driver should accept what ever memory coming from other drivers
1615 * in case of dmabuf-import and this is how the intel-vaapi-driver works.
1616 * For now, in order to avoid any crash we check the buffer size and fallback
1617 * to copy frame method.
1618 *
1619 * See this: https://github.com/intel/media-driver/issues/169
1620 * */
1621 if (GST_VIDEO_INFO_SIZE (&vinfo) < GST_VIDEO_INFO_SIZE (&thiz->aligned_info))
1622 return FALSE;
1623
1624 mfx_surface = msdk_surface->surface;
1625 msdk_mid = (GstMsdkMemoryID *) mfx_surface->Data.MemId;
1626
1627 /* release the internal memory storage of associated mfxSurface */
1628 gst_msdk_replace_mfx_memid (thiz->context, mfx_surface, VA_INVALID_ID);
1629
1630 /* export dmabuf to vasurface */
1631 if (!gst_msdk_export_dmabuf_to_vasurface (thiz->context, &vinfo, fd,
1632 msdk_mid->surface))
1633 return FALSE;
1634
1635 return TRUE;
1636 }
1637 #endif
1638
1639 static MsdkSurface *
gst_msdkenc_get_surface_from_frame(GstMsdkEnc * thiz,GstVideoCodecFrame * frame)1640 gst_msdkenc_get_surface_from_frame (GstMsdkEnc * thiz,
1641 GstVideoCodecFrame * frame)
1642 {
1643 GstVideoFrame src_frame, out_frame;
1644 MsdkSurface *msdk_surface;
1645 GstBuffer *inbuf;
1646 #ifndef _WIN32
1647 GstMemory *mem = NULL;
1648 #endif
1649
1650 inbuf = frame->input_buffer;
1651 if (gst_msdk_is_msdk_buffer (inbuf)) {
1652 msdk_surface = g_slice_new0 (MsdkSurface);
1653 msdk_surface->surface = gst_msdk_get_surface_from_buffer (inbuf);
1654 return msdk_surface;
1655 }
1656
1657 /* If upstream hasn't accpeted the proposed msdk bufferpool,
1658 * just copy frame (if not dmabuf backed )to msdk buffer and take a surface from it.
1659 */
1660 if (!(msdk_surface =
1661 gst_msdkenc_get_surface_from_pool (thiz, thiz->msdk_pool, NULL)))
1662 goto error;
1663
1664 #ifndef _WIN32
1665 /************ dmabuf-import ************* */
1666 /* if upstream provided a dmabuf backed memory, but not an msdk
1667 * buffer, we could try to export the dmabuf to underlined vasurface */
1668 mem = gst_buffer_peek_memory (inbuf, 0);
1669 if (gst_is_dmabuf_memory (mem)) {
1670 if (import_dmabuf_to_msdk_surface (thiz, inbuf, msdk_surface))
1671 return msdk_surface;
1672 else
1673 GST_INFO_OBJECT (thiz, "Upstream dmabuf-backed memory is not imported"
1674 "to the msdk surface, fall back to the copy input frame method");
1675 }
1676 #endif
1677
1678 if (!gst_video_frame_map (&src_frame, &thiz->input_state->info, inbuf,
1679 GST_MAP_READ)) {
1680 GST_ERROR_OBJECT (thiz, "failed to map the frame for source");
1681 goto error;
1682 }
1683
1684 if (!gst_video_frame_map (&out_frame, &thiz->aligned_info, msdk_surface->buf,
1685 GST_MAP_WRITE)) {
1686 GST_ERROR_OBJECT (thiz, "failed to map the frame for destination");
1687 gst_video_frame_unmap (&src_frame);
1688 goto error;
1689 }
1690
1691 if (!gst_video_frame_copy (&out_frame, &src_frame)) {
1692 GST_ERROR_OBJECT (thiz, "failed to copy frame");
1693 gst_video_frame_unmap (&out_frame);
1694 gst_video_frame_unmap (&src_frame);
1695 goto error;
1696 }
1697
1698 gst_video_frame_unmap (&out_frame);
1699 gst_video_frame_unmap (&src_frame);
1700
1701 gst_buffer_replace (&frame->input_buffer, msdk_surface->buf);
1702 gst_buffer_unref (msdk_surface->buf);
1703 msdk_surface->buf = NULL;
1704
1705 return msdk_surface;
1706
1707 error:
1708 if (msdk_surface) {
1709 if (msdk_surface->buf)
1710 gst_buffer_unref (msdk_surface->buf);
1711 g_slice_free (MsdkSurface, msdk_surface);
1712 }
1713 return NULL;
1714 }
1715
1716 static GstFlowReturn
gst_msdkenc_handle_frame(GstVideoEncoder * encoder,GstVideoCodecFrame * frame)1717 gst_msdkenc_handle_frame (GstVideoEncoder * encoder, GstVideoCodecFrame * frame)
1718 {
1719 GstMsdkEnc *thiz = GST_MSDKENC (encoder);
1720 GstMsdkEncClass *klass = GST_MSDKENC_GET_CLASS (thiz);
1721 GstVideoInfo *info = &thiz->input_state->info;
1722 FrameData *fdata;
1723 MsdkSurface *surface;
1724
1725 if (thiz->reconfig || klass->need_reconfig (thiz, frame)) {
1726 gst_msdkenc_flush_frames (thiz, FALSE);
1727 gst_msdkenc_close_encoder (thiz);
1728
1729 klass->set_extra_params (thiz, frame);
1730
1731 // This will reinitialized the encoder but keep same input format.
1732 gst_msdkenc_set_format (encoder, NULL);
1733 }
1734
1735 if (G_UNLIKELY (thiz->context == NULL))
1736 goto not_inited;
1737
1738 if (thiz->has_vpp) {
1739 MsdkSurface *vpp_surface;
1740 GstVideoFrame vframe;
1741 mfxSession session;
1742 mfxSyncPoint vpp_sync_point = NULL;
1743 mfxStatus status;
1744
1745 vpp_surface = gst_msdkenc_get_surface_from_frame (thiz, frame);
1746 if (!vpp_surface)
1747 goto invalid_surface;
1748 surface =
1749 gst_msdkenc_get_surface_from_pool (thiz, thiz->msdk_converted_pool,
1750 NULL);
1751 if (!surface)
1752 goto invalid_surface;
1753
1754 if (!gst_video_frame_map (&vframe, info, frame->input_buffer, GST_MAP_READ))
1755 goto invalid_frame;
1756
1757 if (frame->pts != GST_CLOCK_TIME_NONE) {
1758 vpp_surface->surface->Data.TimeStamp =
1759 gst_util_uint64_scale (frame->pts, 90000, GST_SECOND);
1760 surface->surface->Data.TimeStamp =
1761 gst_util_uint64_scale (frame->pts, 90000, GST_SECOND);
1762 } else {
1763 vpp_surface->surface->Data.TimeStamp = MFX_TIMESTAMP_UNKNOWN;
1764 surface->surface->Data.TimeStamp = MFX_TIMESTAMP_UNKNOWN;
1765 }
1766
1767 session = gst_msdk_context_get_session (thiz->context);
1768 for (;;) {
1769 status =
1770 MFXVideoVPP_RunFrameVPPAsync (session, vpp_surface->surface,
1771 surface->surface, NULL, &vpp_sync_point);
1772 if (status != MFX_WRN_DEVICE_BUSY)
1773 break;
1774 /* If device is busy, wait 1ms and retry, as per MSDK's recomendation */
1775 g_usleep (1000);
1776 };
1777
1778 gst_video_frame_unmap (&vframe);
1779
1780 if (status != MFX_ERR_NONE && status != MFX_ERR_MORE_DATA) {
1781 GST_ELEMENT_ERROR (thiz, STREAM, ENCODE, ("Converting frame failed."),
1782 ("MSDK VPP error (%s)", msdk_status_to_string (status)));
1783 gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (thiz), frame);
1784 return GST_FLOW_ERROR;
1785 }
1786
1787 fdata = g_slice_new0 (FrameData);
1788 fdata->frame = gst_video_codec_frame_ref (frame);
1789 fdata->frame_surface = vpp_surface;
1790 fdata->converted_surface = surface;
1791
1792 thiz->pending_frames = g_list_prepend (thiz->pending_frames, fdata);
1793 } else {
1794 surface = gst_msdkenc_get_surface_from_frame (thiz, frame);
1795 if (!surface)
1796 goto invalid_surface;
1797
1798 fdata = gst_msdkenc_queue_frame (thiz, frame, info);
1799 if (!fdata)
1800 goto invalid_frame;
1801
1802 fdata->frame_surface = surface;
1803
1804 if (frame->pts != GST_CLOCK_TIME_NONE) {
1805 surface->surface->Data.TimeStamp =
1806 gst_util_uint64_scale (frame->pts, 90000, GST_SECOND);
1807 } else {
1808 surface->surface->Data.TimeStamp = MFX_TIMESTAMP_UNKNOWN;
1809 }
1810 }
1811
1812 return gst_msdkenc_encode_frame (thiz, surface->surface, frame);
1813
1814 /* ERRORS */
1815 not_inited:
1816 {
1817 GST_WARNING_OBJECT (encoder, "Got buffer before set_caps was called");
1818 return GST_FLOW_NOT_NEGOTIATED;
1819 }
1820 invalid_surface:
1821 {
1822 GST_ERROR_OBJECT (encoder, "Surface pool is full");
1823 return GST_FLOW_ERROR;
1824 }
1825 invalid_frame:
1826 {
1827 GST_WARNING_OBJECT (encoder, "Failed to map frame");
1828 return GST_FLOW_OK;
1829 }
1830 }
1831
1832 static gboolean
gst_msdkenc_context_prepare(GstMsdkEnc * thiz)1833 gst_msdkenc_context_prepare (GstMsdkEnc * thiz)
1834 {
1835 /* Try to find an existing context from the pipeline. This may (indirectly)
1836 * invoke gst_msdkenc_set_context, which will set thiz->context. */
1837 if (!gst_msdk_context_find (GST_ELEMENT_CAST (thiz), &thiz->context))
1838 return FALSE;
1839
1840 if (thiz->context == thiz->old_context) {
1841 GST_INFO_OBJECT (thiz, "Found old context %" GST_PTR_FORMAT
1842 ", reusing as-is", thiz->context);
1843 return TRUE;
1844 }
1845
1846 GST_INFO_OBJECT (thiz, "Found context %" GST_PTR_FORMAT " from neighbour",
1847 thiz->context);
1848
1849 /* Check GST_MSDK_JOB_VPP and GST_MSDK_JOB_ENCODER together to avoid sharing context
1850 * between VPP and ENCODER
1851 * Example:
1852 * gst-launch-1.0 videotestsrc ! video/x-raw,format=I420 ! msdkh264enc ! \
1853 * msdkh264dec ! msdkvpp ! video/x-raw,format=YUY2 ! fakesink
1854 */
1855 if (!(gst_msdk_context_get_job_type (thiz->context) & (GST_MSDK_JOB_VPP |
1856 GST_MSDK_JOB_ENCODER))) {
1857 gst_msdk_context_add_job_type (thiz->context, GST_MSDK_JOB_ENCODER);
1858 return TRUE;
1859 }
1860
1861 /* Found an existing context that's already being used as an encoder, clone
1862 * the MFX session inside it to create a new one */
1863 {
1864 GstMsdkContext *parent_context, *msdk_context;
1865
1866 GST_INFO_OBJECT (thiz, "Creating new context %" GST_PTR_FORMAT " with "
1867 "joined session", thiz->context);
1868 parent_context = thiz->context;
1869 msdk_context = gst_msdk_context_new_with_parent (parent_context);
1870
1871 if (!msdk_context) {
1872 GST_ERROR_OBJECT (thiz, "Failed to create a context with parent context "
1873 "as %" GST_PTR_FORMAT, parent_context);
1874 return FALSE;
1875 }
1876
1877 thiz->context = msdk_context;
1878 gst_object_unref (parent_context);
1879 }
1880
1881 return TRUE;
1882 }
1883
1884 static gboolean
gst_msdkenc_start(GstVideoEncoder * encoder)1885 gst_msdkenc_start (GstVideoEncoder * encoder)
1886 {
1887 GstMsdkEnc *thiz = GST_MSDKENC (encoder);
1888
1889 if (!gst_msdkenc_context_prepare (thiz)) {
1890 if (!gst_msdk_context_ensure_context (GST_ELEMENT_CAST (thiz),
1891 thiz->hardware, GST_MSDK_JOB_ENCODER))
1892 return FALSE;
1893 GST_INFO_OBJECT (thiz, "Creating new context %" GST_PTR_FORMAT,
1894 thiz->context);
1895 }
1896
1897 /* Save the current context in a separate field so that we know whether it
1898 * has changed between calls to _start() */
1899 gst_object_replace ((GstObject **) & thiz->old_context,
1900 (GstObject *) thiz->context);
1901
1902 gst_msdk_context_add_shared_async_depth (thiz->context, thiz->async_depth);
1903
1904 /* Set the minimum pts to some huge value (1000 hours). This keeps
1905 the dts at the start of the stream from needing to be
1906 negative. */
1907 gst_video_encoder_set_min_pts (encoder, GST_SECOND * 60 * 60 * 1000);
1908
1909 return TRUE;
1910 }
1911
1912 static gboolean
gst_msdkenc_stop(GstVideoEncoder * encoder)1913 gst_msdkenc_stop (GstVideoEncoder * encoder)
1914 {
1915 GstMsdkEnc *thiz = GST_MSDKENC (encoder);
1916
1917 gst_msdkenc_flush_frames (thiz, TRUE);
1918 gst_msdkenc_close_encoder (thiz);
1919 gst_msdkenc_dequeue_all_frames (thiz);
1920
1921 if (thiz->input_state)
1922 gst_video_codec_state_unref (thiz->input_state);
1923 thiz->input_state = NULL;
1924
1925 gst_clear_object (&thiz->context);
1926
1927 return TRUE;
1928 }
1929
1930 static gboolean
gst_msdkenc_flush(GstVideoEncoder * encoder)1931 gst_msdkenc_flush (GstVideoEncoder * encoder)
1932 {
1933 GstMsdkEnc *thiz = GST_MSDKENC (encoder);
1934
1935 GST_DEBUG_OBJECT (encoder, "flush and close encoder");
1936
1937 gst_msdkenc_flush_frames (thiz, TRUE);
1938 gst_msdkenc_close_encoder (thiz);
1939 gst_msdkenc_dequeue_all_frames (thiz);
1940
1941 gst_msdkenc_init_encoder (thiz);
1942
1943 return TRUE;
1944 }
1945
1946 static GstFlowReturn
gst_msdkenc_finish(GstVideoEncoder * encoder)1947 gst_msdkenc_finish (GstVideoEncoder * encoder)
1948 {
1949 GstMsdkEnc *thiz = GST_MSDKENC (encoder);
1950
1951 gst_msdkenc_flush_frames (thiz, FALSE);
1952
1953 return GST_FLOW_OK;
1954 }
1955
1956
1957 static gboolean
gst_msdkenc_propose_allocation(GstVideoEncoder * encoder,GstQuery * query)1958 gst_msdkenc_propose_allocation (GstVideoEncoder * encoder, GstQuery * query)
1959 {
1960 GstMsdkEnc *thiz = GST_MSDKENC (encoder);
1961 GstVideoInfo info;
1962 GstBufferPool *pool = NULL;
1963 GstAllocator *allocator = NULL;
1964 GstCaps *caps;
1965 guint num_buffers;
1966
1967 if (!thiz->input_state)
1968 return FALSE;
1969
1970 gst_query_parse_allocation (query, &caps, NULL);
1971
1972 if (!caps) {
1973 GST_INFO_OBJECT (encoder, "failed to get caps");
1974 return FALSE;
1975 }
1976
1977 if (!gst_video_info_from_caps (&info, caps)) {
1978 GST_INFO_OBJECT (encoder, "failed to get video info");
1979 return FALSE;
1980 }
1981
1982 /* if upstream allocation query supports dmabuf-capsfeatures,
1983 * we do allocate dmabuf backed memory */
1984 if (_gst_caps_has_feature (caps, GST_CAPS_FEATURE_MEMORY_DMABUF)) {
1985 GST_INFO_OBJECT (thiz, "MSDK VPP srcpad uses DMABuf memory");
1986 thiz->use_dmabuf = TRUE;
1987 }
1988
1989 num_buffers = gst_msdkenc_maximum_delayed_frames (thiz) + 1;
1990 pool = gst_msdkenc_create_buffer_pool (thiz, caps, num_buffers, TRUE);
1991
1992 gst_query_add_allocation_pool (query, pool, GST_VIDEO_INFO_SIZE (&info),
1993 num_buffers, 0);
1994 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1995
1996 if (pool) {
1997 GstStructure *config;
1998 GstAllocationParams params = { 0, 31, 0, 0, };
1999
2000 config = gst_buffer_pool_get_config (GST_BUFFER_POOL_CAST (pool));
2001
2002 if (gst_buffer_pool_config_get_allocator (config, &allocator, NULL))
2003 gst_query_add_allocation_param (query, allocator, ¶ms);
2004 gst_structure_free (config);
2005 }
2006
2007 gst_object_unref (pool);
2008
2009 return GST_VIDEO_ENCODER_CLASS (parent_class)->propose_allocation (encoder,
2010 query);
2011 }
2012
2013 static void
gst_msdkenc_dispose(GObject * object)2014 gst_msdkenc_dispose (GObject * object)
2015 {
2016 GstMsdkEnc *thiz = GST_MSDKENC (object);
2017
2018 if (thiz->input_state)
2019 gst_video_codec_state_unref (thiz->input_state);
2020 thiz->input_state = NULL;
2021
2022 gst_clear_object (&thiz->msdk_pool);
2023 gst_clear_object (&thiz->msdk_converted_pool);
2024 gst_clear_object (&thiz->old_context);
2025
2026 gst_clear_structure (&thiz->ext_coding_props);
2027
2028 G_OBJECT_CLASS (parent_class)->dispose (object);
2029 }
2030
2031 static gboolean
gst_msdkenc_need_conversion(GstMsdkEnc * encoder,GstVideoInfo * info,GstVideoFormat * out_format)2032 gst_msdkenc_need_conversion (GstMsdkEnc * encoder, GstVideoInfo * info,
2033 GstVideoFormat * out_format)
2034 {
2035 switch (GST_VIDEO_INFO_FORMAT (info)) {
2036 case GST_VIDEO_FORMAT_NV12:
2037 case GST_VIDEO_FORMAT_P010_10LE:
2038 case GST_VIDEO_FORMAT_VUYA:
2039 #if (MFX_VERSION >= 1027)
2040 case GST_VIDEO_FORMAT_Y410:
2041 case GST_VIDEO_FORMAT_Y210:
2042 #endif
2043 return FALSE;
2044
2045 default:
2046 if (GST_VIDEO_INFO_COMP_DEPTH (info, 0) == 10)
2047 *out_format = GST_VIDEO_FORMAT_P010_10LE;
2048 else
2049 *out_format = GST_VIDEO_FORMAT_NV12;
2050 return TRUE;
2051 }
2052 }
2053
2054 static gboolean
gst_msdkenc_need_reconfig(GstMsdkEnc * encoder,GstVideoCodecFrame * frame)2055 gst_msdkenc_need_reconfig (GstMsdkEnc * encoder, GstVideoCodecFrame * frame)
2056 {
2057 return FALSE;
2058 }
2059
2060 static void
gst_msdkenc_set_extra_params(GstMsdkEnc * encoder,GstVideoCodecFrame * frame)2061 gst_msdkenc_set_extra_params (GstMsdkEnc * encoder, GstVideoCodecFrame * frame)
2062 {
2063 /* Do nothing */
2064 }
2065
2066 static void
gst_msdkenc_class_init(GstMsdkEncClass * klass)2067 gst_msdkenc_class_init (GstMsdkEncClass * klass)
2068 {
2069 GObjectClass *gobject_class;
2070 GstElementClass *element_class;
2071 GstVideoEncoderClass *gstencoder_class;
2072
2073 gobject_class = G_OBJECT_CLASS (klass);
2074 element_class = GST_ELEMENT_CLASS (klass);
2075 gstencoder_class = GST_VIDEO_ENCODER_CLASS (klass);
2076
2077 klass->need_conversion = gst_msdkenc_need_conversion;
2078 klass->need_reconfig = gst_msdkenc_need_reconfig;
2079 klass->set_extra_params = gst_msdkenc_set_extra_params;
2080 klass->qp_max = 51;
2081 klass->qp_min = 0;
2082
2083 gobject_class->dispose = gst_msdkenc_dispose;
2084
2085 element_class->set_context = gst_msdkenc_set_context;
2086
2087 gstencoder_class->set_format = GST_DEBUG_FUNCPTR (gst_msdkenc_set_format);
2088 gstencoder_class->handle_frame = GST_DEBUG_FUNCPTR (gst_msdkenc_handle_frame);
2089 gstencoder_class->start = GST_DEBUG_FUNCPTR (gst_msdkenc_start);
2090 gstencoder_class->stop = GST_DEBUG_FUNCPTR (gst_msdkenc_stop);
2091 gstencoder_class->flush = GST_DEBUG_FUNCPTR (gst_msdkenc_flush);
2092 gstencoder_class->finish = GST_DEBUG_FUNCPTR (gst_msdkenc_finish);
2093 gstencoder_class->propose_allocation =
2094 GST_DEBUG_FUNCPTR (gst_msdkenc_propose_allocation);
2095
2096 gst_element_class_add_static_pad_template (element_class, &sink_factory);
2097 }
2098
2099 static void
gst_msdkenc_init(GstMsdkEnc * thiz)2100 gst_msdkenc_init (GstMsdkEnc * thiz)
2101 {
2102 thiz->hardware = PROP_HARDWARE_DEFAULT;
2103 thiz->async_depth = PROP_ASYNC_DEPTH_DEFAULT;
2104 thiz->target_usage = PROP_TARGET_USAGE_DEFAULT;
2105 thiz->rate_control = PROP_RATE_CONTROL_DEFAULT;
2106 thiz->bitrate = PROP_BITRATE_DEFAULT;
2107 thiz->max_frame_size = PROP_MAX_FRAME_SIZE_DEFAULT;
2108 thiz->max_vbv_bitrate = PROP_MAX_VBV_BITRATE_DEFAULT;
2109 thiz->accuracy = PROP_AVBR_ACCURACY_DEFAULT;
2110 thiz->convergence = PROP_AVBR_ACCURACY_DEFAULT;
2111 thiz->lookahead_depth = PROP_RC_LOOKAHEAD_DEPTH_DEFAULT;
2112 thiz->qpi = PROP_QPI_DEFAULT;
2113 thiz->qpp = PROP_QPP_DEFAULT;
2114 thiz->qpb = PROP_QPB_DEFAULT;
2115 thiz->gop_size = PROP_GOP_SIZE_DEFAULT;
2116 thiz->ref_frames = PROP_REF_FRAMES_DEFAULT;
2117 thiz->i_frames = PROP_I_FRAMES_DEFAULT;
2118 thiz->b_frames = PROP_B_FRAMES_DEFAULT;
2119 thiz->num_slices = PROP_NUM_SLICES_DEFAULT;
2120 thiz->mbbrc = PROP_MBBRC_DEFAULT;
2121 thiz->adaptive_i = PROP_ADAPTIVE_I_DEFAULT;
2122 thiz->adaptive_b = PROP_ADAPTIVE_B_DEFAULT;
2123
2124 thiz->ext_coding_props = gst_structure_new (EC_PROPS_STRUCT_NAME,
2125 EC_PROPS_EXTBRC, G_TYPE_STRING, "off", NULL);
2126 }
2127
2128 /* gst_msdkenc_set_common_property:
2129 *
2130 * This is a helper function to set the common property
2131 * of base encoder from subclass implementation.
2132 */
2133 gboolean
gst_msdkenc_set_common_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)2134 gst_msdkenc_set_common_property (GObject * object, guint prop_id,
2135 const GValue * value, GParamSpec * pspec)
2136 {
2137 GstMsdkEnc *thiz = GST_MSDKENC (object);
2138 GstState state;
2139 gboolean ret = TRUE;
2140
2141 GST_OBJECT_LOCK (thiz);
2142
2143 state = GST_STATE (thiz);
2144 if ((state != GST_STATE_READY && state != GST_STATE_NULL) &&
2145 !(pspec->flags & GST_PARAM_MUTABLE_PLAYING)) {
2146 ret = FALSE;
2147 goto wrong_state;
2148 }
2149
2150 switch (prop_id) {
2151 case GST_MSDKENC_PROP_HARDWARE:
2152 thiz->hardware = g_value_get_boolean (value);
2153 break;
2154 case GST_MSDKENC_PROP_ASYNC_DEPTH:
2155 thiz->async_depth = g_value_get_uint (value);
2156 break;
2157 case GST_MSDKENC_PROP_TARGET_USAGE:
2158 thiz->target_usage = g_value_get_uint (value);
2159 break;
2160 case GST_MSDKENC_PROP_RATE_CONTROL:
2161 thiz->rate_control = g_value_get_enum (value);
2162 break;
2163 case GST_MSDKENC_PROP_BITRATE:
2164 {
2165 guint bitrate = g_value_get_uint (value);
2166 /* Ensure that bitrate changed before triggering a reconfig */
2167 if (bitrate != thiz->bitrate) {
2168 thiz->bitrate = bitrate;
2169 thiz->reconfig = TRUE;
2170 GST_DEBUG_OBJECT (thiz, "changed bitrate to %u", bitrate);
2171 }
2172 break;
2173 }
2174 case GST_MSDKENC_PROP_MAX_FRAME_SIZE:
2175 thiz->max_frame_size = g_value_get_uint (value);
2176 break;
2177 case GST_MSDKENC_PROP_MAX_VBV_BITRATE:
2178 thiz->max_vbv_bitrate = g_value_get_uint (value);
2179 break;
2180 case GST_MSDKENC_PROP_AVBR_ACCURACY:
2181 thiz->accuracy = g_value_get_uint (value);
2182 break;
2183 case GST_MSDKENC_PROP_AVBR_CONVERGENCE:
2184 thiz->convergence = g_value_get_uint (value);
2185 break;
2186 case GST_MSDKENC_PROP_RC_LOOKAHEAD_DEPTH:
2187 thiz->lookahead_depth = g_value_get_uint (value);
2188 break;
2189 case GST_MSDKENC_PROP_QPI:
2190 thiz->qpi = g_value_get_uint (value);
2191 break;
2192 case GST_MSDKENC_PROP_QPP:
2193 thiz->qpp = g_value_get_uint (value);
2194 break;
2195 case GST_MSDKENC_PROP_QPB:
2196 thiz->qpb = g_value_get_uint (value);
2197 break;
2198 case GST_MSDKENC_PROP_GOP_SIZE:
2199 thiz->gop_size = g_value_get_uint (value);
2200 break;
2201 case GST_MSDKENC_PROP_REF_FRAMES:
2202 thiz->ref_frames = g_value_get_uint (value);
2203 break;
2204 case GST_MSDKENC_PROP_I_FRAMES:
2205 thiz->i_frames = g_value_get_uint (value);
2206 break;
2207 case GST_MSDKENC_PROP_B_FRAMES:
2208 thiz->b_frames = g_value_get_uint (value);
2209 break;
2210 case GST_MSDKENC_PROP_NUM_SLICES:
2211 thiz->num_slices = g_value_get_uint (value);
2212 break;
2213 case GST_MSDKENC_PROP_MBBRC:
2214 thiz->mbbrc = g_value_get_enum (value);
2215 break;
2216 case GST_MSDKENC_PROP_ADAPTIVE_I:
2217 thiz->adaptive_i = g_value_get_enum (value);
2218 break;
2219 case GST_MSDKENC_PROP_ADAPTIVE_B:
2220 thiz->adaptive_b = g_value_get_enum (value);
2221 break;
2222 case GST_MSDKENC_PROP_EXT_CODING_PROPS:
2223 {
2224 const GstStructure *s = gst_value_get_structure (value);
2225 const gchar *name = gst_structure_get_name (s);
2226 gst_structure_set_name (thiz->ext_coding_props, name);
2227 if (!structure_transform (s, thiz->ext_coding_props)) {
2228 GST_ERROR_OBJECT (thiz, "failed to transform structure");
2229 }
2230 break;
2231 }
2232 default:
2233 ret = FALSE;
2234 break;
2235 }
2236 GST_OBJECT_UNLOCK (thiz);
2237 return ret;
2238
2239 /* ERROR */
2240 wrong_state:
2241 {
2242 GST_WARNING_OBJECT (thiz, "setting property in wrong state");
2243 GST_OBJECT_UNLOCK (thiz);
2244 return ret;
2245 }
2246 }
2247
2248 /* gst_msdkenc_get_common_property:
2249 *
2250 * This is a helper function to get the common property
2251 * of base encoder from subclass implementation.
2252 */
2253 gboolean
gst_msdkenc_get_common_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)2254 gst_msdkenc_get_common_property (GObject * object, guint prop_id,
2255 GValue * value, GParamSpec * pspec)
2256 {
2257 GstMsdkEnc *thiz = GST_MSDKENC (object);
2258 gboolean ret = TRUE;
2259
2260 GST_OBJECT_LOCK (thiz);
2261 switch (prop_id) {
2262 case GST_MSDKENC_PROP_HARDWARE:
2263 g_value_set_boolean (value, thiz->hardware);
2264 break;
2265 case GST_MSDKENC_PROP_ASYNC_DEPTH:
2266 g_value_set_uint (value, thiz->async_depth);
2267 break;
2268 case GST_MSDKENC_PROP_TARGET_USAGE:
2269 g_value_set_uint (value, thiz->target_usage);
2270 break;
2271 case GST_MSDKENC_PROP_RATE_CONTROL:
2272 g_value_set_enum (value, thiz->rate_control);
2273 break;
2274 case GST_MSDKENC_PROP_BITRATE:
2275 g_value_set_uint (value, thiz->bitrate);
2276 break;
2277 case GST_MSDKENC_PROP_MAX_FRAME_SIZE:
2278 g_value_set_uint (value, thiz->max_frame_size);
2279 break;
2280 case GST_MSDKENC_PROP_MAX_VBV_BITRATE:
2281 g_value_set_uint (value, thiz->max_vbv_bitrate);
2282 break;
2283 case GST_MSDKENC_PROP_AVBR_ACCURACY:
2284 g_value_set_uint (value, thiz->accuracy);
2285 break;
2286 case GST_MSDKENC_PROP_AVBR_CONVERGENCE:
2287 g_value_set_uint (value, thiz->convergence);
2288 break;
2289 case GST_MSDKENC_PROP_RC_LOOKAHEAD_DEPTH:
2290 g_value_set_uint (value, thiz->lookahead_depth);
2291 break;
2292 case GST_MSDKENC_PROP_QPI:
2293 g_value_set_uint (value, thiz->qpi);
2294 break;
2295 case GST_MSDKENC_PROP_QPP:
2296 g_value_set_uint (value, thiz->qpp);
2297 break;
2298 case GST_MSDKENC_PROP_QPB:
2299 g_value_set_uint (value, thiz->qpb);
2300 break;
2301 case GST_MSDKENC_PROP_GOP_SIZE:
2302 g_value_set_uint (value, thiz->gop_size);
2303 break;
2304 case GST_MSDKENC_PROP_REF_FRAMES:
2305 g_value_set_uint (value, thiz->ref_frames);
2306 break;
2307 case GST_MSDKENC_PROP_I_FRAMES:
2308 g_value_set_uint (value, thiz->i_frames);
2309 break;
2310 case GST_MSDKENC_PROP_B_FRAMES:
2311 g_value_set_uint (value, thiz->b_frames);
2312 break;
2313 case GST_MSDKENC_PROP_NUM_SLICES:
2314 g_value_set_uint (value, thiz->num_slices);
2315 break;
2316 case GST_MSDKENC_PROP_MBBRC:
2317 g_value_set_enum (value, thiz->mbbrc);
2318 break;
2319 case GST_MSDKENC_PROP_ADAPTIVE_I:
2320 g_value_set_enum (value, thiz->adaptive_i);
2321 break;
2322 case GST_MSDKENC_PROP_ADAPTIVE_B:
2323 g_value_set_enum (value, thiz->adaptive_b);
2324 break;
2325 case GST_MSDKENC_PROP_EXT_CODING_PROPS:
2326 gst_value_set_structure (value, thiz->ext_coding_props);
2327 break;
2328 default:
2329 ret = FALSE;
2330 break;
2331 }
2332 GST_OBJECT_UNLOCK (thiz);
2333 return ret;
2334 }
2335
2336 /* gst_msdkenc_install_common_properties:
2337 * @thiz: a #GstMsdkEnc
2338 *
2339 * This is a helper function to install common properties
2340 * of base encoder from subclass implementation.
2341 * Encoders like jpeg do't require all the common properties
2342 * and they can avoid installing it into base gobject.
2343 */
2344 void
gst_msdkenc_install_common_properties(GstMsdkEncClass * klass)2345 gst_msdkenc_install_common_properties (GstMsdkEncClass * klass)
2346 {
2347 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
2348 GParamSpec *obj_properties[GST_MSDKENC_PROP_MAX] = { NULL, };
2349 guint qp_range_max = klass->qp_max;
2350 guint qp_range_min = klass->qp_min;
2351
2352 obj_properties[GST_MSDKENC_PROP_HARDWARE] =
2353 g_param_spec_boolean ("hardware", "Hardware", "Enable hardware encoders",
2354 PROP_HARDWARE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2355
2356 obj_properties[GST_MSDKENC_PROP_ASYNC_DEPTH] =
2357 g_param_spec_uint ("async-depth", "Async Depth",
2358 "Depth of asynchronous pipeline",
2359 1, 20, PROP_ASYNC_DEPTH_DEFAULT,
2360 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2361
2362 obj_properties[GST_MSDKENC_PROP_TARGET_USAGE] =
2363 g_param_spec_uint ("target-usage", "Target Usage",
2364 "1: Best quality, 4: Balanced, 7: Best speed",
2365 1, 7, PROP_TARGET_USAGE_DEFAULT,
2366 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2367
2368 obj_properties[GST_MSDKENC_PROP_RATE_CONTROL] =
2369 g_param_spec_enum ("rate-control", "Rate Control",
2370 "Rate control method", gst_msdkenc_rate_control_get_type (),
2371 PROP_RATE_CONTROL_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2372
2373 obj_properties[GST_MSDKENC_PROP_BITRATE] =
2374 g_param_spec_uint ("bitrate", "Bitrate", "Bitrate in kbit/sec", 1,
2375 2000 * 1024, PROP_BITRATE_DEFAULT,
2376 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_PLAYING);
2377
2378 obj_properties[GST_MSDKENC_PROP_MAX_FRAME_SIZE] =
2379 g_param_spec_uint ("max-frame-size", "Max Frame Size",
2380 "Maximum possible size (in kbyte) of any compressed frames (0: auto-calculate)",
2381 0, G_MAXUINT16, PROP_MAX_FRAME_SIZE_DEFAULT,
2382 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2383
2384 /* Set the same upper bound with bitrate */
2385 obj_properties[GST_MSDKENC_PROP_MAX_VBV_BITRATE] =
2386 g_param_spec_uint ("max-vbv-bitrate", "Max VBV Bitrate",
2387 "Maximum bitrate(kbit/sec) at which data enters Video Buffering Verifier (0: auto-calculate)",
2388 0, 2000 * 1024, PROP_MAX_VBV_BITRATE_DEFAULT,
2389 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2390
2391 obj_properties[GST_MSDKENC_PROP_AVBR_ACCURACY] =
2392 g_param_spec_uint ("accuracy", "Accuracy", "The AVBR Accuracy in "
2393 "the unit of tenth of percent", 0, G_MAXUINT16,
2394 PROP_AVBR_ACCURACY_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2395
2396 obj_properties[GST_MSDKENC_PROP_AVBR_CONVERGENCE] =
2397 g_param_spec_uint ("convergence", "Convergence",
2398 "The AVBR Convergence in the unit of 100 frames", 0, G_MAXUINT16,
2399 PROP_AVBR_CONVERGENCE_DEFAULT,
2400 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2401
2402 obj_properties[GST_MSDKENC_PROP_RC_LOOKAHEAD_DEPTH] =
2403 g_param_spec_uint ("rc-lookahead", "Look-ahead depth",
2404 "Number of frames to look ahead for Rate control", 10, 100,
2405 PROP_RC_LOOKAHEAD_DEPTH_DEFAULT,
2406 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2407
2408 obj_properties[GST_MSDKENC_PROP_QPI] =
2409 g_param_spec_uint ("qpi", "QPI",
2410 "Constant quantizer for I frames (0 unlimited). Also used as "
2411 "ICQQuality or QVBRQuality for different RateControl methods",
2412 qp_range_min, qp_range_max, PROP_QPI_DEFAULT,
2413 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2414
2415 obj_properties[GST_MSDKENC_PROP_QPP] =
2416 g_param_spec_uint ("qpp", "QPP",
2417 "Constant quantizer for P frames (0 unlimited)",
2418 qp_range_min, qp_range_max, PROP_QPP_DEFAULT,
2419 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2420
2421 obj_properties[GST_MSDKENC_PROP_QPB] =
2422 g_param_spec_uint ("qpb", "QPB",
2423 "Constant quantizer for B frames (0 unlimited)",
2424 qp_range_min, qp_range_max, PROP_QPB_DEFAULT,
2425 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2426
2427 obj_properties[GST_MSDKENC_PROP_GOP_SIZE] =
2428 g_param_spec_uint ("gop-size", "GOP Size", "GOP Size", 0,
2429 G_MAXINT, PROP_GOP_SIZE_DEFAULT,
2430 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2431
2432 obj_properties[GST_MSDKENC_PROP_REF_FRAMES] =
2433 g_param_spec_uint ("ref-frames", "Reference Frames",
2434 "Number of reference frames",
2435 0, G_MAXINT, PROP_REF_FRAMES_DEFAULT,
2436 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2437
2438 obj_properties[GST_MSDKENC_PROP_I_FRAMES] =
2439 g_param_spec_uint ("i-frames", "I Frames",
2440 "Number of I frames between IDR frames",
2441 0, G_MAXINT, PROP_I_FRAMES_DEFAULT,
2442 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2443
2444 obj_properties[GST_MSDKENC_PROP_B_FRAMES] =
2445 g_param_spec_uint ("b-frames", "B Frames",
2446 "Number of B frames between I and P frames",
2447 0, G_MAXINT, PROP_B_FRAMES_DEFAULT,
2448 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2449
2450 obj_properties[GST_MSDKENC_PROP_NUM_SLICES] =
2451 g_param_spec_uint ("num-slices", "Number of Slices",
2452 "Number of slices per frame, Zero tells the encoder to "
2453 "choose any slice partitioning allowed by the codec standard",
2454 0, G_MAXINT, PROP_NUM_SLICES_DEFAULT,
2455 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2456
2457 obj_properties[GST_MSDKENC_PROP_MBBRC] =
2458 g_param_spec_enum ("mbbrc", "MB level bitrate control",
2459 "Macroblock level bitrate control",
2460 gst_msdkenc_mbbrc_get_type (),
2461 PROP_MBBRC_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2462
2463 obj_properties[GST_MSDKENC_PROP_ADAPTIVE_I] =
2464 g_param_spec_enum ("i-adapt", "Adaptive I-Frame Insertion",
2465 "Adaptive I-Frame Insertion control",
2466 gst_msdkenc_adaptive_i_get_type (),
2467 PROP_ADAPTIVE_I_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2468
2469 obj_properties[GST_MSDKENC_PROP_ADAPTIVE_B] =
2470 g_param_spec_enum ("b-adapt", "Adaptive B-Frame Insertion",
2471 "Adaptive B-Frame Insertion control",
2472 gst_msdkenc_adaptive_b_get_type (),
2473 PROP_ADAPTIVE_B_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2474
2475 /**
2476 * GstMsdkEnc:ext-coding-props
2477 *
2478 * The properties for the external coding.
2479 *
2480 * Supported properties:
2481 * ```
2482 * extbrc : External bitrate control
2483 * String. Range: { auto, on, off } Default: off
2484 * ```
2485 *
2486 * Example:
2487 * ```
2488 * ext-coding-props="props,extbrc=on"
2489 * ```
2490 *
2491 * Since: 1.20
2492 *
2493 */
2494 obj_properties[GST_MSDKENC_PROP_EXT_CODING_PROPS] =
2495 g_param_spec_boxed ("ext-coding-props", "External coding properties",
2496 "The properties for the external coding, refer to the hotdoc for the "
2497 "supported properties",
2498 GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2499
2500 g_object_class_install_properties (gobject_class,
2501 GST_MSDKENC_PROP_MAX, obj_properties);
2502 }
2503