gstmsdkenc.c 58.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/* GStreamer Intel MSDK plugin
 * Copyright (c) 2016, Oblong Industries, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/* TODO:
 *  - Add support for interlaced content
 *  - Add support for MVC AVC
 *  - Wrap more configuration options and maybe move properties to derived
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif
41
42
43
#ifdef _WIN32
#  include <malloc.h>
#endif
44
45
46
47

#include <stdlib.h>

#include "gstmsdkenc.h"
Hyunjun Ko's avatar
Hyunjun Ko committed
48
49
50
#include "gstmsdkbufferpool.h"
#include "gstmsdkvideomemory.h"
#include "gstmsdksystemmemory.h"
51
#include "gstmsdkcontextutil.h"
52

53
54
55
56
#ifndef _WIN32
#include "gstmsdkallocator_libva.h"
#endif

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
static inline void *
_aligned_alloc (size_t alignment, size_t size)
{
#ifdef _WIN32
  return _aligned_malloc (size, alignment);
#else
  void *out;
  if (posix_memalign (&out, alignment, size) != 0)
    out = NULL;
  return out;
#endif
}

#ifndef _WIN32
#define _aligned_free free
#endif

74
75
76
77
78
79
80
81
82
static void gst_msdkenc_close_encoder (GstMsdkEnc * thiz);

GST_DEBUG_CATEGORY_EXTERN (gst_msdkenc_debug);
#define GST_CAT_DEFAULT gst_msdkenc_debug

static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-raw, "
83
        "format = (string) { NV12, I420, YV12, YUY2, UYVY, BGRA }, "
84
85
        "framerate = (fraction) [0, MAX], "
        "width = (int) [ 16, MAX ], height = (int) [ 16, MAX ],"
86
87
88
        "interlace-mode = (string) progressive" ";"
        GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_DMABUF,
            "{ NV12 }")));
89
90
91
92
93
94
95
96
97
98
99
100
101

#define PROP_HARDWARE_DEFAULT            TRUE
#define PROP_ASYNC_DEPTH_DEFAULT         4
#define PROP_TARGET_USAGE_DEFAULT        (MFX_TARGETUSAGE_BALANCED)
#define PROP_RATE_CONTROL_DEFAULT        (MFX_RATECONTROL_CBR)
#define PROP_BITRATE_DEFAULT             (2 * 1024)
#define PROP_QPI_DEFAULT                 0
#define PROP_QPP_DEFAULT                 0
#define PROP_QPB_DEFAULT                 0
#define PROP_GOP_SIZE_DEFAULT            256
#define PROP_REF_FRAMES_DEFAULT          1
#define PROP_I_FRAMES_DEFAULT            0
#define PROP_B_FRAMES_DEFAULT            0
102
#define PROP_NUM_SLICES_DEFAULT          0
103
104
105
106
107
#define PROP_AVBR_ACCURACY_DEFAULT       0
#define PROP_AVBR_CONVERGENCE_DEFAULT    0
#define PROP_RC_LOOKAHEAD_DEPTH_DEFAULT  10
#define PROP_MAX_VBV_BITRATE_DEFAULT     0
#define PROP_MAX_FRAME_SIZE_DEFAULT      0
108
109
110
#define PROP_MBBRC_DEFAULT               MFX_CODINGOPTION_OFF
#define PROP_ADAPTIVE_I_DEFAULT          MFX_CODINGOPTION_OFF
#define PROP_ADAPTIVE_B_DEFAULT          MFX_CODINGOPTION_OFF
111
112
113
114

#define gst_msdkenc_parent_class parent_class
G_DEFINE_TYPE (GstMsdkEnc, gst_msdkenc, GST_TYPE_VIDEO_ENCODER);

Hyunjun Ko's avatar
Hyunjun Ko committed
115
116
117
118
119
120
typedef struct
{
  mfxFrameSurface1 *surface;
  GstBuffer *buf;
} MsdkSurface;

121
122
123
124
125
126
127
128
129
void
gst_msdkenc_add_extra_param (GstMsdkEnc * thiz, mfxExtBuffer * param)
{
  if (thiz->num_extra_params < MAX_EXTRA_PARAMS) {
    thiz->extra_params[thiz->num_extra_params] = param;
    thiz->num_extra_params++;
  }
}

130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
static void
gst_msdkenc_set_context (GstElement * element, GstContext * context)
{
  GstMsdkContext *msdk_context = NULL;
  GstMsdkEnc *thiz = GST_MSDKENC (element);

  if (gst_msdk_context_get_context (context, &msdk_context)) {
    gst_object_replace ((GstObject **) & thiz->context,
        (GstObject *) msdk_context);
    gst_object_unref (msdk_context);
  }

  GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
}

145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
static void
ensure_bitrate_control (GstMsdkEnc * thiz)
{
  mfxInfoMFX *mfx = &thiz->param.mfx;
  mfxExtCodingOption2 *option2 = &thiz->option2;
  mfxExtCodingOption3 *option3 = &thiz->option3;

  mfx->RateControlMethod = thiz->rate_control;
  /* No effect in CQP varient algorithms */
  mfx->TargetKbps = thiz->bitrate;
  mfx->MaxKbps = thiz->max_vbv_bitrate;

  switch (mfx->RateControlMethod) {
    case MFX_RATECONTROL_CQP:
      mfx->QPI = thiz->qpi;
      mfx->QPP = thiz->qpp;
      mfx->QPB = thiz->qpb;
      break;

    case MFX_RATECONTROL_LA_ICQ:
      option2->LookAheadDepth = thiz->lookahead_depth;
    case MFX_RATECONTROL_ICQ:
      mfx->ICQQuality = CLAMP (thiz->qpi, 1, 51);
      break;

    case MFX_RATECONTROL_LA:   /* VBR with LA. Only supported in H264?? */
    case MFX_RATECONTROL_LA_HRD:       /* VBR with LA, HRD compliant */
      option2->LookAheadDepth = thiz->lookahead_depth;
      break;

    case MFX_RATECONTROL_QVBR:
      option3->QVBRQuality = CLAMP (thiz->qpi, 1, 51);
      thiz->enable_extopt3 = TRUE;
      break;

    case MFX_RATECONTROL_AVBR:
      mfx->Accuracy = thiz->accuracy;
      mfx->Convergence = thiz->convergence;
Haihao Xiang's avatar
Haihao Xiang committed
183
      break;
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202

    case MFX_RATECONTROL_VBR:
      option2->MaxFrameSize = thiz->max_frame_size * 1000;
      break;

    case MFX_RATECONTROL_VCM:
      /*Non HRD compliant mode with no B-frame and interlaced support */
      thiz->param.mfx.GopRefDist = 0;
      break;

    case MFX_RATECONTROL_CBR:
      break;

    default:
      GST_ERROR ("Unsupported RateControl!");
      break;
  }
}

203
204
void
gst_msdkenc_ensure_extended_coding_options (GstMsdkEnc * thiz)
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
{
  mfxExtCodingOption2 *option2 = &thiz->option2;
  mfxExtCodingOption3 *option3 = &thiz->option3;

  /* Fill ExtendedCodingOption2, set non-zero defaults too */
  option2->Header.BufferId = MFX_EXTBUFF_CODING_OPTION2;
  option2->Header.BufferSz = sizeof (thiz->option2);
  option2->MBBRC = thiz->mbbrc;
  option2->AdaptiveI = thiz->adaptive_i;
  option2->AdaptiveB = thiz->adaptive_b;
  option2->BitrateLimit = MFX_CODINGOPTION_OFF;
  option2->EnableMAD = MFX_CODINGOPTION_OFF;
  option2->UseRawRef = MFX_CODINGOPTION_OFF;
  gst_msdkenc_add_extra_param (thiz, (mfxExtBuffer *) option2);

  if (thiz->enable_extopt3) {
    option3->Header.BufferId = MFX_EXTBUFF_CODING_OPTION3;
    option3->Header.BufferSz = sizeof (thiz->option3);
    gst_msdkenc_add_extra_param (thiz, (mfxExtBuffer *) option3);
  }
}

227
228
229
230
231
232
233
static gboolean
gst_msdkenc_init_encoder (GstMsdkEnc * thiz)
{
  GstMsdkEncClass *klass = GST_MSDKENC_GET_CLASS (thiz);
  GstVideoInfo *info;
  mfxSession session;
  mfxStatus status;
234
  mfxFrameAllocRequest request[2];
235
236
  guint i;

237
238
239
240
241
  if (thiz->initialized)
    return TRUE;

  if (!thiz->context) {
    GST_WARNING_OBJECT (thiz, "No MSDK Context");
242
243
244
    return FALSE;
  }

245
246
  if (!thiz->input_state) {
    GST_DEBUG_OBJECT (thiz, "Have no input state yet");
247
248
    return FALSE;
  }
249
  info = &thiz->input_state->info;
250
251

  GST_OBJECT_LOCK (thiz);
Hyunjun Ko's avatar
Hyunjun Ko committed
252
  session = gst_msdk_context_get_session (thiz->context);
253
254

  thiz->has_vpp = FALSE;
Hyunjun Ko's avatar
Hyunjun Ko committed
255
256
257
  if (thiz->use_video_memory)
    gst_msdk_set_frame_allocator (thiz->context);

258
259
260
261
262
263
264
265
  /* Check 10bit input */
  if (GST_VIDEO_INFO_COMP_DEPTH (info, 0) == 10) {
    if (GST_VIDEO_INFO_FORMAT (info) != GST_VIDEO_FORMAT_P010_10LE) {
      GST_WARNING_OBJECT (thiz,
          "P010_10LE is the only supported 10bit format\n");
      goto failed;
    }
  } else if (GST_VIDEO_INFO_FORMAT (info) != GST_VIDEO_FORMAT_NV12) {
Hyunjun Ko's avatar
Hyunjun Ko committed
266
267
268
269
270
271
    if (thiz->use_video_memory)
      thiz->vpp_param.IOPattern =
          MFX_IOPATTERN_IN_VIDEO_MEMORY | MFX_IOPATTERN_OUT_VIDEO_MEMORY;
    else
      thiz->vpp_param.IOPattern =
          MFX_IOPATTERN_IN_SYSTEM_MEMORY | MFX_IOPATTERN_OUT_SYSTEM_MEMORY;
272

273
    thiz->vpp_param.vpp.In.Width = GST_ROUND_UP_16 (info->width);
274
275
276
277
278
279
280
281
    thiz->vpp_param.vpp.In.Height = GST_ROUND_UP_32 (info->height);
    thiz->vpp_param.vpp.In.CropW = info->width;
    thiz->vpp_param.vpp.In.CropH = info->height;
    thiz->vpp_param.vpp.In.FrameRateExtN = info->fps_n;
    thiz->vpp_param.vpp.In.FrameRateExtD = info->fps_d;
    thiz->vpp_param.vpp.In.AspectRatioW = info->par_n;
    thiz->vpp_param.vpp.In.AspectRatioH = info->par_d;
    thiz->vpp_param.vpp.In.PicStruct = MFX_PICSTRUCT_PROGRESSIVE;
282
    switch (GST_VIDEO_INFO_FORMAT (info)) {
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
      case GST_VIDEO_FORMAT_NV12:
        thiz->vpp_param.vpp.In.FourCC = MFX_FOURCC_NV12;
        thiz->vpp_param.vpp.In.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
        break;
      case GST_VIDEO_FORMAT_YV12:
      case GST_VIDEO_FORMAT_I420:
        thiz->vpp_param.vpp.In.FourCC = MFX_FOURCC_YV12;
        thiz->vpp_param.vpp.In.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
        break;
      case GST_VIDEO_FORMAT_YUY2:
        thiz->vpp_param.vpp.In.FourCC = MFX_FOURCC_YUY2;
        thiz->vpp_param.vpp.In.ChromaFormat = MFX_CHROMAFORMAT_YUV422;
        break;
      case GST_VIDEO_FORMAT_UYVY:
        thiz->vpp_param.vpp.In.FourCC = MFX_FOURCC_UYVY;
        thiz->vpp_param.vpp.In.ChromaFormat = MFX_CHROMAFORMAT_YUV422;
        break;
      case GST_VIDEO_FORMAT_BGRA:
        thiz->vpp_param.vpp.In.FourCC = MFX_FOURCC_RGB4;
        thiz->vpp_param.vpp.In.ChromaFormat = MFX_CHROMAFORMAT_YUV444;
        break;
      default:
        g_assert_not_reached ();
        break;
    }
    thiz->vpp_param.vpp.Out = thiz->vpp_param.vpp.In;
    thiz->vpp_param.vpp.Out.FourCC = MFX_FOURCC_NV12;
    thiz->vpp_param.vpp.Out.ChromaFormat = MFX_CHROMAFORMAT_YUV420;

    /* validate parameters and allow the Media SDK to make adjustments */
    status = MFXVideoVPP_Query (session, &thiz->vpp_param, &thiz->vpp_param);
    if (status < MFX_ERR_NONE) {
      GST_ERROR_OBJECT (thiz, "Video VPP Query failed (%s)",
          msdk_status_to_string (status));
      goto no_vpp;
    } else if (status > MFX_ERR_NONE) {
      GST_WARNING_OBJECT (thiz, "Video VPP Query returned: %s",
          msdk_status_to_string (status));
    }

    status = MFXVideoVPP_QueryIOSurf (session, &thiz->vpp_param, request);
    if (status < MFX_ERR_NONE) {
      GST_ERROR_OBJECT (thiz, "VPP Query IO surfaces failed (%s)",
          msdk_status_to_string (status));
      goto no_vpp;
    } else if (status > MFX_ERR_NONE) {
      GST_WARNING_OBJECT (thiz, "VPP Query IO surfaces returned: %s",
          msdk_status_to_string (status));
    }

333
334
335
    if (thiz->use_video_memory)
      request[0].NumFrameSuggested +=
          gst_msdk_context_get_shared_async_depth (thiz->context);
336
    thiz->num_vpp_surfaces = request[0].NumFrameSuggested;
Hyunjun Ko's avatar
Hyunjun Ko committed
337
338
339
340

    if (thiz->use_video_memory)
      gst_msdk_frame_alloc (thiz->context, &(request[0]),
          &thiz->vpp_alloc_resp);
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364

    status = MFXVideoVPP_Init (session, &thiz->vpp_param);
    if (status < MFX_ERR_NONE) {
      GST_ERROR_OBJECT (thiz, "Init failed (%s)",
          msdk_status_to_string (status));
      goto no_vpp;
    } else if (status > MFX_ERR_NONE) {
      GST_WARNING_OBJECT (thiz, "Init returned: %s",
          msdk_status_to_string (status));
    }

    status = MFXVideoVPP_GetVideoParam (session, &thiz->vpp_param);
    if (status < MFX_ERR_NONE) {
      GST_ERROR_OBJECT (thiz, "Get VPP Parameters failed (%s)",
          msdk_status_to_string (status));
      MFXVideoVPP_Close (session);
      goto no_vpp;
    } else if (status > MFX_ERR_NONE) {
      GST_WARNING_OBJECT (thiz, "Get VPP Parameters returned: %s",
          msdk_status_to_string (status));
    }

    thiz->has_vpp = TRUE;
  }
365
366

  thiz->param.AsyncDepth = thiz->async_depth;
Hyunjun Ko's avatar
Hyunjun Ko committed
367
368
369
370
  if (thiz->use_video_memory)
    thiz->param.IOPattern = MFX_IOPATTERN_IN_VIDEO_MEMORY;
  else
    thiz->param.IOPattern = MFX_IOPATTERN_IN_SYSTEM_MEMORY;
371
372
373
374
375

  thiz->param.mfx.TargetUsage = thiz->target_usage;
  thiz->param.mfx.GopPicSize = thiz->gop_size;
  thiz->param.mfx.GopRefDist = thiz->b_frames + 1;
  thiz->param.mfx.IdrInterval = thiz->i_frames;
376
  thiz->param.mfx.NumSlice = thiz->num_slices;
377
378
379
  thiz->param.mfx.NumRefFrame = thiz->ref_frames;
  thiz->param.mfx.EncodedOrder = 0;     /* Take input frames in display order */

380
  thiz->param.mfx.FrameInfo.Width = GST_ROUND_UP_16 (info->width);
381
  thiz->param.mfx.FrameInfo.Height = GST_ROUND_UP_32 (info->height);
382
383
384
385
386
387
388
389
390
  thiz->param.mfx.FrameInfo.CropW = info->width;
  thiz->param.mfx.FrameInfo.CropH = info->height;
  thiz->param.mfx.FrameInfo.FrameRateExtN = info->fps_n;
  thiz->param.mfx.FrameInfo.FrameRateExtD = info->fps_d;
  thiz->param.mfx.FrameInfo.AspectRatioW = info->par_n;
  thiz->param.mfx.FrameInfo.AspectRatioH = info->par_d;
  thiz->param.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE;
  thiz->param.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420;

391
392
393
394
395
396
397
398
399
400
401
  if (GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_P010_10LE) {
    thiz->param.mfx.FrameInfo.FourCC = MFX_FOURCC_P010;
    thiz->param.mfx.FrameInfo.BitDepthLuma = 10;
    thiz->param.mfx.FrameInfo.BitDepthChroma = 10;
    thiz->param.mfx.FrameInfo.Shift = 1;
  } else {
    thiz->param.mfx.FrameInfo.FourCC = MFX_FOURCC_NV12;
    thiz->param.mfx.FrameInfo.BitDepthLuma = 8;
    thiz->param.mfx.FrameInfo.BitDepthChroma = 8;
  }

402
403
404
  /* ensure bitrate control parameters */
  ensure_bitrate_control (thiz);

405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
  /* allow subclass configure further */
  if (klass->configure) {
    if (!klass->configure (thiz))
      goto failed;
  }

  if (thiz->num_extra_params) {
    thiz->param.NumExtParam = thiz->num_extra_params;
    thiz->param.ExtParam = thiz->extra_params;
  }

  /* validate parameters and allow the Media SDK to make adjustments */
  status = MFXVideoENCODE_Query (session, &thiz->param, &thiz->param);
  if (status < MFX_ERR_NONE) {
    GST_ERROR_OBJECT (thiz, "Video Encode Query failed (%s)",
        msdk_status_to_string (status));
    goto failed;
  } else if (status > MFX_ERR_NONE) {
    GST_WARNING_OBJECT (thiz, "Video Encode Query returned: %s",
        msdk_status_to_string (status));
  }

427
  status = MFXVideoENCODE_QueryIOSurf (session, &thiz->param, request);
428
  if (status < MFX_ERR_NONE) {
429
    GST_ERROR_OBJECT (thiz, "Encode Query IO surfaces failed (%s)",
430
431
432
        msdk_status_to_string (status));
    goto failed;
  } else if (status > MFX_ERR_NONE) {
433
    GST_WARNING_OBJECT (thiz, "Encode Query IO surfaces returned: %s",
434
435
436
        msdk_status_to_string (status));
  }

437
438
439
  if (thiz->has_vpp)
    request[0].NumFrameSuggested += thiz->num_vpp_surfaces + 1 - 4;

440
441
442
  if (thiz->use_video_memory) {
    if (thiz->use_dmabuf && !thiz->has_vpp)
      request[0].Type |= MFX_MEMTYPE_EXPORT_FRAME;
Hyunjun Ko's avatar
Hyunjun Ko committed
443
    gst_msdk_frame_alloc (thiz->context, &(request[0]), &thiz->alloc_resp);
444
  }
Hyunjun Ko's avatar
Hyunjun Ko committed
445

446
447
448
449
450
  /* Maximum of VPP output and encoder input, if using VPP */
  if (thiz->has_vpp)
    request[0].NumFrameSuggested =
        MAX (request[0].NumFrameSuggested, request[1].NumFrameSuggested);
  if (request[0].NumFrameSuggested < thiz->param.AsyncDepth) {
451
    GST_ERROR_OBJECT (thiz, "Required %d surfaces (%d suggested), async %d",
452
453
        request[0].NumFrameMin, request[0].NumFrameSuggested,
        thiz->param.AsyncDepth);
454
455
456
    goto failed;
  }

Hyunjun Ko's avatar
Hyunjun Ko committed
457
  /* This is VPP output (if any) and encoder input */
458
  thiz->num_surfaces = request[0].NumFrameSuggested;
459
460

  GST_DEBUG_OBJECT (thiz, "Required %d surfaces (%d suggested), allocated %d",
461
      request[0].NumFrameMin, request[0].NumFrameSuggested, thiz->num_surfaces);
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484

  status = MFXVideoENCODE_Init (session, &thiz->param);
  if (status < MFX_ERR_NONE) {
    GST_ERROR_OBJECT (thiz, "Init failed (%s)", msdk_status_to_string (status));
    goto failed;
  } else if (status > MFX_ERR_NONE) {
    GST_WARNING_OBJECT (thiz, "Init returned: %s",
        msdk_status_to_string (status));
  }

  status = MFXVideoENCODE_GetVideoParam (session, &thiz->param);
  if (status < MFX_ERR_NONE) {
    GST_ERROR_OBJECT (thiz, "Get Video Parameters failed (%s)",
        msdk_status_to_string (status));
    goto failed;
  } else if (status > MFX_ERR_NONE) {
    GST_WARNING_OBJECT (thiz, "Get Video Parameters returned: %s",
        msdk_status_to_string (status));
  }

  thiz->num_tasks = thiz->param.AsyncDepth;
  thiz->tasks = g_new0 (MsdkEncTask, thiz->num_tasks);
  for (i = 0; i < thiz->num_tasks; i++) {
485
486
487
    thiz->tasks[i].output_bitstream.Data = _aligned_alloc (32,
        thiz->param.mfx.BufferSizeInKB * 1024);
    if (!thiz->tasks[i].output_bitstream.Data) {
488
489
490
491
492
493
494
495
496
      GST_ERROR_OBJECT (thiz, "Memory allocation failed");
      goto failed;
    }
    thiz->tasks[i].output_bitstream.MaxLength =
        thiz->param.mfx.BufferSizeInKB * 1024;
  }
  thiz->next_task = 0;

  thiz->reconfig = FALSE;
497
  thiz->initialized = TRUE;
498
499
500
501
502

  GST_OBJECT_UNLOCK (thiz);

  return TRUE;

503
no_vpp:
504
505
506
507
508
509
510
511
512
513
514
failed:
  GST_OBJECT_UNLOCK (thiz);
  return FALSE;
}

static void
gst_msdkenc_close_encoder (GstMsdkEnc * thiz)
{
  guint i;
  mfxStatus status;

515
  if (!thiz->context || !thiz->initialized)
516
517
    return;

518
519
  GST_DEBUG_OBJECT (thiz, "Closing encoder with context %" GST_PTR_FORMAT,
      thiz->context);
520

Hyunjun Ko's avatar
Hyunjun Ko committed
521
522
523
524
525
526
  gst_object_replace ((GstObject **) & thiz->msdk_pool, NULL);
  gst_object_replace ((GstObject **) & thiz->msdk_converted_pool, NULL);

  if (thiz->use_video_memory)
    gst_msdk_frame_free (thiz->context, &thiz->alloc_resp);

Hyunjun Ko's avatar
Hyunjun Ko committed
527
  status = MFXVideoENCODE_Close (gst_msdk_context_get_session (thiz->context));
528
529
530
531
532
533
534
535
536
  if (status != MFX_ERR_NONE && status != MFX_ERR_NOT_INITIALIZED) {
    GST_WARNING_OBJECT (thiz, "Encoder close failed (%s)",
        msdk_status_to_string (status));
  }

  if (thiz->tasks) {
    for (i = 0; i < thiz->num_tasks; i++) {
      MsdkEncTask *task = &thiz->tasks[i];
      if (task->output_bitstream.Data) {
537
        _aligned_free (task->output_bitstream.Data);
538
539
540
541
542
543
      }
    }
  }
  g_free (thiz->tasks);
  thiz->tasks = NULL;

544
545
546
  /* Close VPP before freeing the surfaces. They are shared between encoder
   * and VPP */
  if (thiz->has_vpp) {
Hyunjun Ko's avatar
Hyunjun Ko committed
547
548
549
    if (thiz->use_video_memory)
      gst_msdk_frame_free (thiz->context, &thiz->vpp_alloc_resp);

Hyunjun Ko's avatar
Hyunjun Ko committed
550
    status = MFXVideoVPP_Close (gst_msdk_context_get_session (thiz->context));
551
552
553
554
555
556
    if (status != MFX_ERR_NONE && status != MFX_ERR_NOT_INITIALIZED) {
      GST_WARNING_OBJECT (thiz, "VPP close failed (%s)",
          msdk_status_to_string (status));
    }
  }

557
558
  memset (&thiz->param, 0, sizeof (thiz->param));
  thiz->num_extra_params = 0;
559
  thiz->initialized = FALSE;
560
561
562
563
564
}

typedef struct
{
  GstVideoCodecFrame *frame;
Hyunjun Ko's avatar
Hyunjun Ko committed
565
566
  MsdkSurface *frame_surface;
  MsdkSurface *converted_surface;
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
} FrameData;

static FrameData *
gst_msdkenc_queue_frame (GstMsdkEnc * thiz, GstVideoCodecFrame * frame,
    GstVideoInfo * info)
{
  FrameData *fdata;

  fdata = g_slice_new (FrameData);
  fdata->frame = gst_video_codec_frame_ref (frame);

  thiz->pending_frames = g_list_prepend (thiz->pending_frames, fdata);

  return fdata;
}

Hyunjun Ko's avatar
Hyunjun Ko committed
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
static MsdkSurface *
gst_msdkenc_create_surface (mfxFrameSurface1 * surface, GstBuffer * buf)
{
  MsdkSurface *msdk_surface;
  msdk_surface = g_slice_new0 (MsdkSurface);
  msdk_surface->surface = surface;
  msdk_surface->buf = buf;

  return msdk_surface;
}

static void
gst_msdkenc_free_surface (MsdkSurface * surface)
{
  if (surface->buf)
    gst_buffer_unref (surface->buf);

  g_slice_free (MsdkSurface, surface);
}

static void
gst_msdkenc_free_frame_data (GstMsdkEnc * thiz, FrameData * fdata)
{
  if (fdata->frame_surface)
    gst_msdkenc_free_surface (fdata->frame_surface);
  if (thiz->has_vpp)
    gst_msdkenc_free_surface (fdata->converted_surface);

  gst_video_codec_frame_unref (fdata->frame);
  g_slice_free (FrameData, fdata);
}

615
616
617
618
619
620
621
622
623
624
625
static void
gst_msdkenc_dequeue_frame (GstMsdkEnc * thiz, GstVideoCodecFrame * frame)
{
  GList *l;

  for (l = thiz->pending_frames; l; l = l->next) {
    FrameData *fdata = l->data;

    if (fdata->frame != frame)
      continue;

Hyunjun Ko's avatar
Hyunjun Ko committed
626
    gst_msdkenc_free_frame_data (thiz, fdata);
627
628
629
630
631
632
633
634
635
636
637
638
639
640

    thiz->pending_frames = g_list_delete_link (thiz->pending_frames, l);
    return;
  }
}

static void
gst_msdkenc_dequeue_all_frames (GstMsdkEnc * thiz)
{
  GList *l;

  for (l = thiz->pending_frames; l; l = l->next) {
    FrameData *fdata = l->data;

Hyunjun Ko's avatar
Hyunjun Ko committed
641
    gst_msdkenc_free_frame_data (thiz, fdata);
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
  }
  g_list_free (thiz->pending_frames);
  thiz->pending_frames = NULL;
}

static MsdkEncTask *
gst_msdkenc_get_free_task (GstMsdkEnc * thiz)
{
  MsdkEncTask *tasks = thiz->tasks;
  guint size = thiz->num_tasks;
  guint start = thiz->next_task;
  guint i;

  if (tasks) {
    for (i = 0; i < size; i++) {
      guint t = (start + i) % size;
      if (tasks[t].sync_point == NULL)
        return &tasks[t];
    }
  }
  return NULL;
}

static void
gst_msdkenc_reset_task (MsdkEncTask * task)
{
  task->output_bitstream.DataLength = 0;
  task->sync_point = NULL;
}

static GstFlowReturn
gst_msdkenc_finish_frame (GstMsdkEnc * thiz, MsdkEncTask * task,
    gboolean discard)
{
676
  GstVideoCodecFrame *frame;
677

678
  if (!task->sync_point)
679
    return GST_FLOW_OK;
680
681
682
683
684
685

  frame = gst_video_encoder_get_oldest_frame (GST_VIDEO_ENCODER (thiz));

  if (!frame) {
    GST_ERROR_OBJECT (thiz, "failed to get a frame");
    return GST_FLOW_ERROR;
686
687
  }

688
689
690
691
  /* Wait for encoding operation to complete, the magic number 300000 below
   * is used in MSDK samples
   * #define MSDK_ENC_WAIT_INTERVAL 300000
   */
Hyunjun Ko's avatar
Hyunjun Ko committed
692
  MFXVideoCORE_SyncOperation (gst_msdk_context_get_session (thiz->context),
693
      task->sync_point, 300000);
694
695
696
697
698
699
700
701
  if (!discard && task->output_bitstream.DataLength) {
    GstBuffer *out_buf = NULL;
    guint8 *data =
        task->output_bitstream.Data + task->output_bitstream.DataOffset;
    gsize size = task->output_bitstream.DataLength;
    out_buf = gst_buffer_new_allocate (NULL, size, NULL);
    gst_buffer_fill (out_buf, 0, data, size);
    frame->output_buffer = out_buf;
702
703
704
705
706
707
708
709
710
    frame->pts =
        gst_util_uint64_scale (task->output_bitstream.TimeStamp, GST_SECOND,
        90000);
    frame->dts =
        gst_util_uint64_scale (task->output_bitstream.DecodeTimeStamp,
        GST_SECOND, 90000);

    if ((task->output_bitstream.FrameType & MFX_FRAMETYPE_IDR) != 0 ||
        (task->output_bitstream.FrameType & MFX_FRAMETYPE_xIDR) != 0) {
711
712
713
714
715
716
717
      GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
    }

    /* Mark task as available */
    gst_msdkenc_reset_task (task);
  }

718
  gst_video_codec_frame_unref (frame);
719
  gst_msdkenc_dequeue_frame (thiz, frame);
720

721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
  return gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (thiz), frame);
}

static GstFlowReturn
gst_msdkenc_encode_frame (GstMsdkEnc * thiz, mfxFrameSurface1 * surface,
    GstVideoCodecFrame * input_frame)
{
  mfxSession session;
  MsdkEncTask *task;
  mfxStatus status;

  if (G_UNLIKELY (thiz->context == NULL)) {
    gst_msdkenc_dequeue_frame (thiz, input_frame);
    gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (thiz), input_frame);
    return GST_FLOW_NOT_NEGOTIATED;
  }
Hyunjun Ko's avatar
Hyunjun Ko committed
737
  session = gst_msdk_context_get_session (thiz->context);
738
739
740
741

  task = gst_msdkenc_get_free_task (thiz);

  for (;;) {
742
743
744
745
746
747
748
749
750
    /* Force key-frame if needed */
    if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (input_frame))
      thiz->enc_cntrl.FrameType =
          MFX_FRAMETYPE_I | MFX_FRAMETYPE_IDR | MFX_FRAMETYPE_REF;
    else
      thiz->enc_cntrl.FrameType = MFX_FRAMETYPE_UNKNOWN;

    status =
        MFXVideoENCODE_EncodeFrameAsync (session, &thiz->enc_cntrl, surface,
751
752
753
754
755
756
757
758
759
        &task->output_bitstream, &task->sync_point);
    if (status != MFX_WRN_DEVICE_BUSY)
      break;
    /* If device is busy, wait 1ms and retry, as per MSDK's recomendation */
    g_usleep (1000);
  };

  if (status != MFX_ERR_NONE && status != MFX_ERR_MORE_DATA) {
    GST_ELEMENT_ERROR (thiz, STREAM, ENCODE, ("Encode frame failed."),
760
        ("MSDK encode error (%s)", msdk_status_to_string (status)));
761
762
763
764
765
    gst_msdkenc_dequeue_frame (thiz, input_frame);
    gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (thiz), input_frame);
    return GST_FLOW_ERROR;
  }

766
767
768
  if (task->sync_point) {
    thiz->next_task = ((task - thiz->tasks) + 1) % thiz->num_tasks;
  } else if (status == MFX_ERR_MORE_DATA) {
769
    gst_msdkenc_dequeue_frame (thiz, input_frame);
770
771
  }

772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
  /* Ensure that next task is available */
  task = thiz->tasks + thiz->next_task;
  return gst_msdkenc_finish_frame (thiz, task, FALSE);
}

static guint
gst_msdkenc_maximum_delayed_frames (GstMsdkEnc * thiz)
{
  return thiz->num_tasks;
}

static void
gst_msdkenc_set_latency (GstMsdkEnc * thiz)
{
  GstVideoInfo *info = &thiz->input_state->info;
  gint max_delayed_frames;
  GstClockTime latency;

  max_delayed_frames = gst_msdkenc_maximum_delayed_frames (thiz);

  if (info->fps_n) {
    latency = gst_util_uint64_scale_ceil (GST_SECOND * info->fps_d,
        max_delayed_frames, info->fps_n);
  } else {
    /* FIXME: Assume 25fps. This is better than reporting no latency at
     * all and then later failing in live pipelines
     */
    latency = gst_util_uint64_scale_ceil (GST_SECOND * 1,
        max_delayed_frames, 25);
  }

  GST_INFO_OBJECT (thiz,
      "Updating latency to %" GST_TIME_FORMAT " (%d frames)",
      GST_TIME_ARGS (latency), max_delayed_frames);

  gst_video_encoder_set_latency (GST_VIDEO_ENCODER (thiz), latency, latency);
}

static void
gst_msdkenc_flush_frames (GstMsdkEnc * thiz, gboolean discard)
{
813
814
815
816
  mfxStatus status;
  mfxSession session;
  MsdkEncTask *task;
  guint i, t;
817
818
819
820

  if (!thiz->tasks)
    return;

821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
  session = gst_msdk_context_get_session (thiz->context);

  for (;;) {
    task = thiz->tasks + thiz->next_task;
    gst_msdkenc_finish_frame (thiz, task, FALSE);

    status = MFXVideoENCODE_EncodeFrameAsync (session, NULL, NULL,
        &task->output_bitstream, &task->sync_point);

    if (status != MFX_ERR_NONE && status != MFX_ERR_MORE_DATA) {
      GST_ELEMENT_ERROR (thiz, STREAM, ENCODE, ("Encode frame failed."),
          ("MSDK encode error (%s)", msdk_status_to_string (status)));
    }

    if (task->sync_point) {
      thiz->next_task = ((task - thiz->tasks) + 1) % thiz->num_tasks;
    } else if (status == MFX_ERR_MORE_DATA) {
      break;
    }
  };

  t = thiz->next_task;
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
  for (i = 0; i < thiz->num_tasks; i++) {
    gst_msdkenc_finish_frame (thiz, &thiz->tasks[t], discard);
    t = (t + 1) % thiz->num_tasks;
  }
}

static gboolean
gst_msdkenc_set_src_caps (GstMsdkEnc * thiz)
{
  GstMsdkEncClass *klass = GST_MSDKENC_GET_CLASS (thiz);
  GstCaps *outcaps = NULL;
  GstVideoCodecState *state;
  GstTagList *tags;

  if (klass->set_src_caps)
    outcaps = klass->set_src_caps (thiz);

  if (!outcaps)
    return FALSE;

  state = gst_video_encoder_set_output_state (GST_VIDEO_ENCODER (thiz),
      outcaps, thiz->input_state);
  GST_DEBUG_OBJECT (thiz, "output caps: %" GST_PTR_FORMAT, state->caps);

  gst_video_codec_state_unref (state);

  tags = gst_tag_list_new_empty ();
  gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, "msdkenc",
      GST_TAG_MAXIMUM_BITRATE, thiz->bitrate * 1024,
      GST_TAG_NOMINAL_BITRATE, thiz->bitrate * 1024, NULL);
  gst_video_encoder_merge_tags (GST_VIDEO_ENCODER (thiz), tags,
      GST_TAG_MERGE_REPLACE);
  gst_tag_list_unref (tags);

  return TRUE;
}

Hyunjun Ko's avatar
Hyunjun Ko committed
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
static GstBufferPool *
gst_msdkenc_create_buffer_pool (GstMsdkEnc * thiz, GstCaps * caps,
    guint num_buffers, gboolean set_align)
{
  GstBufferPool *pool = NULL;
  GstStructure *config;
  GstAllocator *allocator = NULL;
  GstVideoInfo info;
  GstVideoAlignment align;
  GstAllocationParams params = { 0, 31, 0, 0, };
  mfxFrameAllocResponse *alloc_resp = NULL;

  if (thiz->has_vpp)
    alloc_resp = set_align ? &thiz->vpp_alloc_resp : &thiz->alloc_resp;
  else
    alloc_resp = &thiz->alloc_resp;

  pool = gst_msdk_buffer_pool_new (thiz->context, alloc_resp);
  if (!pool)
    goto error_no_pool;

  if (!gst_video_info_from_caps (&info, caps)) {
    GST_INFO_OBJECT (thiz, "failed to get video info");
    return FALSE;
  }

  gst_msdk_set_video_alignment (&info, &align);
  gst_video_info_align (&info, &align);

909
910
911
912
  if (thiz->use_dmabuf)
    allocator =
        gst_msdk_dmabuf_allocator_new (thiz->context, &info, alloc_resp);
  else if (thiz->use_video_memory)
Hyunjun Ko's avatar
Hyunjun Ko committed
913
914
915
916
917
918
919
920
921
922
923
924
925
    allocator = gst_msdk_video_allocator_new (thiz->context, &info, alloc_resp);
  else
    allocator = gst_msdk_system_allocator_new (&info);

  if (!allocator)
    goto error_no_allocator;

  config = gst_buffer_pool_get_config (GST_BUFFER_POOL_CAST (pool));
  gst_buffer_pool_config_set_params (config, caps, info.size, num_buffers, 0);
  gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
  gst_buffer_pool_config_add_option (config,
      GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);

926
  if (thiz->use_video_memory) {
Hyunjun Ko's avatar
Hyunjun Ko committed
927
928
    gst_buffer_pool_config_add_option (config,
        GST_BUFFER_POOL_OPTION_MSDK_USE_VIDEO_MEMORY);
929
930
931
932
    if (thiz->use_dmabuf)
      gst_buffer_pool_config_add_option (config,
          GST_BUFFER_POOL_OPTION_MSDK_USE_DMABUF);
  }
Hyunjun Ko's avatar
Hyunjun Ko committed
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962

  gst_buffer_pool_config_set_video_alignment (config, &align);
  gst_buffer_pool_config_set_allocator (config, allocator, &params);
  gst_object_unref (allocator);

  if (!gst_buffer_pool_set_config (pool, config))
    goto error_pool_config;

  if (set_align)
    thiz->aligned_info = info;

  return pool;

error_no_pool:
  {
    GST_INFO_OBJECT (thiz, "failed to create bufferpool");
    return FALSE;
  }
error_no_allocator:
  {
    GST_INFO_OBJECT (thiz, "failed to create allocator");
    return FALSE;
  }
error_pool_config:
  {
    GST_INFO_OBJECT (thiz, "failed to set config");
    return FALSE;
  }
}

963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
/* Fixme: Common routine used by all msdk elements, should be
 * moved to a common util file */
static gboolean
_gst_caps_has_feature (const GstCaps * caps, const gchar * feature)
{
  guint i;

  for (i = 0; i < gst_caps_get_size (caps); i++) {
    GstCapsFeatures *const features = gst_caps_get_features (caps, i);
    /* Skip ANY features, we need an exact match for correct evaluation */
    if (gst_caps_features_is_any (features))
      continue;
    if (gst_caps_features_contains (features, feature))
      return TRUE;
  }
  return FALSE;
}

static gboolean
sinkpad_can_dmabuf (GstMsdkEnc * thiz)
{
  gboolean ret = FALSE;
  GstCaps *caps, *allowed_caps;
  GstPad *sinkpad;

  sinkpad = GST_VIDEO_ENCODER_SINK_PAD (thiz);
  caps = gst_pad_get_pad_template_caps (sinkpad);

  allowed_caps = gst_pad_peer_query_caps (sinkpad, caps);
  if (!allowed_caps)
    goto done;
  if (gst_caps_is_any (allowed_caps) || gst_caps_is_empty (allowed_caps)
      || allowed_caps == caps)
    goto done;

  if (_gst_caps_has_feature (allowed_caps, GST_CAPS_FEATURE_MEMORY_DMABUF))
    ret = TRUE;

done:
  if (caps)
    gst_caps_unref (caps);
  if (allowed_caps)
    gst_caps_unref (allowed_caps);
  return ret;
}

1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
static gboolean
gst_msdkenc_set_format (GstVideoEncoder * encoder, GstVideoCodecState * state)
{
  GstMsdkEnc *thiz = GST_MSDKENC (encoder);
  GstMsdkEncClass *klass = GST_MSDKENC_GET_CLASS (thiz);

  if (state) {
    if (thiz->input_state)
      gst_video_codec_state_unref (thiz->input_state);
    thiz->input_state = gst_video_codec_state_ref (state);
  }

Hyunjun Ko's avatar
Hyunjun Ko committed
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
  /* TODO: Currently d3d allocator is not implemented.
   * So encoder uses system memory by default on Windows.
   */
#ifndef _WIN32
  thiz->use_video_memory = TRUE;
#else
  thiz->use_video_memory = FALSE;
#endif

  GST_INFO_OBJECT (encoder, "This MSDK encoder uses %s memory",
      thiz->use_video_memory ? "video" : "system");

1033
1034
1035
1036
1037
  if (klass->set_format) {
    if (!klass->set_format (thiz))
      return FALSE;
  }

1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
  /* If upstream supports DMABufCapsfeatures, then we request for the dmabuf
   * based pipeline usage. Ideally we should have dmabuf support even with
   * raw-caps negotiation, but we don't have dmabuf-import support in msdk
   * plugin yet */
  if (sinkpad_can_dmabuf (thiz)) {
    thiz->input_state->caps = gst_caps_make_writable (thiz->input_state->caps);
    gst_caps_set_features (thiz->input_state->caps, 0,
        gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_DMABUF, NULL));
    thiz->use_dmabuf = TRUE;
  }

1049
1050
1051
1052
1053
1054
1055
1056
  if (!gst_msdkenc_init_encoder (thiz))
    return FALSE;

  if (!gst_msdkenc_set_src_caps (thiz)) {
    gst_msdkenc_close_encoder (thiz);
    return FALSE;
  }

Hyunjun Ko's avatar
Hyunjun Ko committed
1057
1058
1059
1060
1061
1062
1063
  if (!thiz->msdk_pool) {
    guint num_buffers = gst_msdkenc_maximum_delayed_frames (thiz) + 1;
    thiz->msdk_pool =
        gst_msdkenc_create_buffer_pool (thiz, thiz->input_state->caps,
        num_buffers, TRUE);
  }

1064
1065
  gst_msdkenc_set_latency (thiz);

Hyunjun Ko's avatar
Hyunjun Ko committed
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
  /* Create another bufferpool if VPP requires */
  if (thiz->has_vpp) {
    GstVideoInfo *info = &thiz->input_state->info;
    GstVideoInfo nv12_info;
    GstCaps *caps;
    GstBufferPool *pool = NULL;

    gst_video_info_init (&nv12_info);
    gst_video_info_set_format (&nv12_info, GST_VIDEO_FORMAT_NV12, info->width,
        info->height);
    caps = gst_video_info_to_caps (&nv12_info);

    pool =
        gst_msdkenc_create_buffer_pool (thiz, caps, thiz->num_surfaces, FALSE);

    thiz->msdk_converted_pool = pool;
    gst_caps_unref (caps);
  }

1085
1086
1087
  return TRUE;
}

Hyunjun Ko's avatar
Hyunjun Ko committed
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
static MsdkSurface *
gst_msdkenc_get_surface_from_pool (GstMsdkEnc * thiz, GstBufferPool * pool,
    GstBufferPoolAcquireParams * params)
{
  GstBuffer *new_buffer;
  mfxFrameSurface1 *new_surface;
  MsdkSurface *msdk_surface;

  if (!gst_buffer_pool_is_active (pool) &&
      !gst_buffer_pool_set_active (pool, TRUE)) {
    GST_ERROR_OBJECT (pool, "failed to activate buffer pool");
    return NULL;
  }

  if (gst_buffer_pool_acquire_buffer (pool, &new_buffer, params) != GST_FLOW_OK) {
    GST_ERROR_OBJECT (pool, "failed to acquire a buffer from pool");
    return NULL;
  }

  if (gst_msdk_is_msdk_buffer (new_buffer))
    new_surface = gst_msdk_get_surface_from_buffer (new_buffer);
  else {
    GST_ERROR_OBJECT (pool, "the acquired memory is not MSDK memory");
    return NULL;
  }

  msdk_surface = gst_msdkenc_create_surface (new_surface, new_buffer);

  return msdk_surface;
}

Seungha Yang's avatar
Seungha Yang committed
1119
#ifndef _WIN32
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
static gboolean
import_dmabuf_to_msdk_surface (GstMsdkEnc * thiz, GstBuffer * buf,
    MsdkSurface * msdk_surface)
{
  GstMemory *mem = NULL;
  GstVideoInfo vinfo;
  GstVideoMeta *vmeta;
  GstMsdkMemoryID *msdk_mid = NULL;
  mfxFrameSurface1 *mfx_surface = NULL;
  gint fd, i;
  mem = gst_buffer_peek_memory (buf, 0);
  fd = gst_dmabuf_memory_get_fd (mem);
  if (fd < 0)
    return FALSE;

  vinfo = thiz->input_state->info;
  /* Update offset/stride/size if there is VideoMeta attached to
   * the buffer */
  vmeta = gst_buffer_get_video_meta (buf);
  if (vmeta) {
    if (GST_VIDEO_INFO_FORMAT (&vinfo) != vmeta->format ||
        GST_VIDEO_INFO_WIDTH (&vinfo) != vmeta->width ||
        GST_VIDEO_INFO_HEIGHT (&vinfo) != vmeta->height ||
        GST_VIDEO_INFO_N_PLANES (&vinfo) != vmeta->n_planes) {
      GST_ERROR_OBJECT (thiz, "VideoMeta attached to buffer is not matching"
          "the negotiated width/height/format");
      return FALSE;
    }
    for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&vinfo); ++i) {
      GST_VIDEO_INFO_PLANE_OFFSET (&vinfo, i) = vmeta->offset[i];
      GST_VIDEO_INFO_PLANE_STRIDE (&vinfo, i) = vmeta->stride[i];
    }
    GST_VIDEO_INFO_SIZE (&vinfo) = gst_buffer_get_size (buf);
  }

  /* Upstream neither accepted the msdk pool nor the msdk buffer size restrictions.
   * Current media-driver and GMMLib will fail due to strict memory size restrictions.
   * Ideally, media-driver should accept what ever memory coming from other drivers
   * in case of dmabuf-import and this is how the intel-vaapi-driver works.
   * For now, in order to avoid any crash we check the buffer size and fallback
   * to copy frame method.
   *
   * See this: https://github.com/intel/media-driver/issues/169
   * */
  if (GST_VIDEO_INFO_SIZE (&vinfo) < GST_VIDEO_INFO_SIZE (&thiz->aligned_info))
    return FALSE;

  mfx_surface = msdk_surface->surface;
  msdk_mid = (GstMsdkMemoryID *) mfx_surface->Data.MemId;

  /* release the internal memory storage of associated mfxSurface */
  gst_msdk_replace_mfx_memid (thiz->context, mfx_surface, VA_INVALID_ID);

  /* export dmabuf to vasurface */
  if (!gst_msdk_export_dmabuf_to_vasurface (thiz->context, &vinfo, fd,
          msdk_mid->surface))
    return FALSE;

  return TRUE;
}
Seungha Yang's avatar
Seungha Yang committed
1180
#endif
1181

Hyunjun Ko's avatar
Hyunjun Ko committed
1182
1183
1184
1185
1186
1187
1188
static MsdkSurface *
gst_msdkenc_get_surface_from_frame (GstMsdkEnc * thiz,
    GstVideoCodecFrame * frame)
{
  GstVideoFrame src_frame, out_frame;
  MsdkSurface *msdk_surface;
  GstBuffer *inbuf;
1189
  GstMemory *mem = NULL;
Hyunjun Ko's avatar
Hyunjun Ko committed
1190
1191
1192
1193
1194
1195
1196
1197
1198

  inbuf = frame->input_buffer;
  if (gst_msdk_is_msdk_buffer (inbuf)) {
    msdk_surface = g_slice_new0 (MsdkSurface);
    msdk_surface->surface = gst_msdk_get_surface_from_buffer (inbuf);
    return msdk_surface;
  }

  /* If upstream hasn't accpeted the proposed msdk bufferpool,
1199
   * just copy frame (if not dmabuf backed )to msdk buffer and take a surface from it.
Hyunjun Ko's avatar
Hyunjun Ko committed
1200
1201
1202
1203
1204
   */
  if (!(msdk_surface =
          gst_msdkenc_get_surface_from_pool (thiz, thiz->msdk_pool, NULL)))
    goto error;

1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
#ifndef _WIN32
  /************ dmabuf-import ************* */
  /* if upstream provided a dmabuf backed memory, but not an msdk
   * buffer, we could try to export the dmabuf to underlined vasurface */
  mem = gst_buffer_peek_memory (inbuf, 0);
  if (gst_is_dmabuf_memory (mem)) {
    if (import_dmabuf_to_msdk_surface (thiz, inbuf, msdk_surface))
      return msdk_surface;
    else
      GST_INFO_OBJECT (thiz, "Upstream dmabuf-backed memory is not imported"
          "to the msdk surface, fall back to the copy input frame method");
  }
#endif

Hyunjun Ko's avatar
Hyunjun Ko committed
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
  if (!gst_video_frame_map (&src_frame, &thiz->input_state->info, inbuf,
          GST_MAP_READ)) {
    GST_ERROR_OBJECT (thiz, "failed to map the frame for source");
    goto error;
  }

  if (!gst_video_frame_map (&out_frame, &thiz->aligned_info, msdk_surface->buf,
          GST_MAP_WRITE)) {
    GST_ERROR_OBJECT (thiz, "failed to map the frame for destination");
    gst_video_frame_unmap (&src_frame);
    goto error;
  }

  if (!gst_video_frame_copy (&out_frame, &src_frame)) {
    GST_ERROR_OBJECT (thiz, "failed to copy frame");
    gst_video_frame_unmap (&out_frame);
    gst_video_frame_unmap (&src_frame);
    goto error;
  }

  gst_video_frame_unmap (&out_frame);
  gst_video_frame_unmap (&src_frame);

  gst_buffer_replace (&frame->input_buffer, msdk_surface->buf);
  gst_buffer_unref (msdk_surface->buf);
  msdk_surface->buf = NULL;

  return msdk_surface;

error:
  if (msdk_surface) {
    if (msdk_surface->buf)
      gst_buffer_unref (msdk_surface->buf);
    g_slice_free (MsdkSurface, msdk_surface);
  }
  return NULL;
}

1257
1258
1259
1260
1261
1262
static GstFlowReturn
gst_msdkenc_handle_frame (GstVideoEncoder * encoder, GstVideoCodecFrame * frame)
{
  GstMsdkEnc *thiz = GST_MSDKENC (encoder);
  GstVideoInfo *info = &thiz->input_state->info;
  FrameData *fdata;
Hyunjun Ko's avatar
Hyunjun Ko committed
1263
  MsdkSurface *surface;
1264
1265
1266
1267
1268
1269
1270
1271
1272

  if (thiz->reconfig) {
    gst_msdkenc_flush_frames (thiz, FALSE);
    gst_msdkenc_set_format (encoder, NULL);
  }

  if (G_UNLIKELY (thiz->context == NULL))
    goto not_inited;

1273
  if (thiz->has_vpp) {
Hyunjun Ko's avatar
Hyunjun Ko committed
1274
    MsdkSurface *vpp_surface;
1275
1276
1277
1278
1279
    GstVideoFrame vframe;
    mfxSession session;
    mfxSyncPoint vpp_sync_point = NULL;
    mfxStatus status;

Hyunjun Ko's avatar
Hyunjun Ko committed
1280
    vpp_surface = gst_msdkenc_get_surface_from_frame (thiz, frame);
1281
1282
    if (!vpp_surface)
      goto invalid_surface;
Hyunjun Ko's avatar
Hyunjun Ko committed
1283
1284
1285
    surface =
        gst_msdkenc_get_surface_from_pool (thiz, thiz->msdk_converted_pool,
        NULL);
1286
1287
1288
1289
1290
1291
1292
    if (!surface)
      goto invalid_surface;

    if (!gst_video_frame_map (&vframe, info, frame->input_buffer, GST_MAP_READ))
      goto invalid_frame;

    if (frame->pts != GST_CLOCK_TIME_NONE) {
Hyunjun Ko's avatar
Hyunjun Ko committed
1293
      vpp_surface->surface->Data.TimeStamp =
1294
          gst_util_uint64_scale (frame->pts, 90000, GST_SECOND);
Hyunjun Ko's avatar
Hyunjun Ko committed
1295
      surface->surface->Data.TimeStamp =
1296
1297
          gst_util_uint64_scale (frame->pts, 90000, GST_SECOND);
    } else {
Hyunjun Ko's avatar
Hyunjun Ko committed
1298
1299
      vpp_surface->surface->Data.TimeStamp = MFX_TIMESTAMP_UNKNOWN;
      surface->surface->Data.TimeStamp = MFX_TIMESTAMP_UNKNOWN;
1300
1301
    }

Hyunjun Ko's avatar
Hyunjun Ko committed
1302
    session = gst_msdk_context_get_session (thiz->context);
1303
1304
    for (;;) {
      status =
Hyunjun Ko's avatar
Hyunjun Ko committed
1305
1306
          MFXVideoVPP_RunFrameVPPAsync (session, vpp_surface->surface,
          surface->surface, NULL, &vpp_sync_point);
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
      if (status != MFX_WRN_DEVICE_BUSY)
        break;
      /* If device is busy, wait 1ms and retry, as per MSDK's recomendation */
      g_usleep (1000);
    };

    gst_video_frame_unmap (&vframe);

    if (status != MFX_ERR_NONE && status != MFX_ERR_MORE_DATA) {
      GST_ELEMENT_ERROR (thiz, STREAM, ENCODE, ("Converting frame failed."),
          ("MSDK VPP error (%s)", msdk_status_to_string (status)));
      gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (thiz), frame);
      return GST_FLOW_ERROR;
    }
1321

1322
1323
    fdata = g_slice_new0 (FrameData);
    fdata->frame = gst_video_codec_frame_ref (frame);
Hyunjun Ko's avatar
Hyunjun Ko committed
1324
1325
    fdata->frame_surface = vpp_surface;
    fdata->converted_surface = surface;
1326

1327
    thiz->pending_frames = g_list_prepend (thiz->pending_frames, fdata);
1328
  } else {
Hyunjun Ko's avatar
Hyunjun Ko committed
1329
    surface = gst_msdkenc_get_surface_from_frame (thiz, frame);
1330
1331
1332
1333
1334
1335
1336
    if (!surface)
      goto invalid_surface;

    fdata = gst_msdkenc_queue_frame (thiz, frame, info);
    if (!fdata)
      goto invalid_frame;

Hyunjun Ko's avatar
Hyunjun Ko committed
1337
1338
    fdata->frame_surface = surface;

1339
    if (frame->pts != GST_CLOCK_TIME_NONE) {
Hyunjun Ko's avatar
Hyunjun Ko committed
1340
      surface->surface->Data.TimeStamp =
1341
1342
          gst_util_uint64_scale (frame->pts, 90000, GST_SECOND);
    } else {
Hyunjun Ko's avatar
Hyunjun Ko committed
1343
      surface->surface->Data.TimeStamp = MFX_TIMESTAMP_UNKNOWN;
1344
    }
1345
1346
  }

Hyunjun Ko's avatar
Hyunjun Ko committed
1347
  return gst_msdkenc_encode_frame (thiz, surface->surface, frame);
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369

/* ERRORS */
not_inited:
  {
    GST_WARNING_OBJECT (encoder, "Got buffer before set_caps was called");
    return GST_FLOW_NOT_NEGOTIATED;
  }
invalid_surface:
  {
    GST_ERROR_OBJECT (encoder, "Surface pool is full");
    return GST_FLOW_ERROR;
  }
invalid_frame:
  {
    GST_WARNING_OBJECT (encoder, "Failed to map frame");
    return GST_FLOW_OK;
  }
}

static gboolean
gst_msdkenc_start (GstVideoEncoder * encoder)
{
1370
1371
1372
1373
1374
1375
1376
  GstMsdkEnc *thiz = GST_MSDKENC (encoder);

  if (gst_msdk_context_prepare (GST_ELEMENT_CAST (thiz), &thiz->context)) {
    GST_INFO_OBJECT (thiz, "Found context %" GST_PTR_FORMAT " from neighbour",
        thiz->context);

    if (gst_msdk_context_get_job_type (thiz->context) & GST_MSDK_JOB_ENCODER) {
1377
      GstMsdkContext *parent_context, *msdk_context;
1378
1379

      parent_context = thiz->context;
1380
1381
1382
1383
1384
1385
1386
1387
      msdk_context = gst_msdk_context_new_with_parent (parent_context);

      if (!msdk_context) {
        GST_ERROR_OBJECT (thiz, "Context creation failed");
        return FALSE;
      }

      thiz->context = msdk_context;
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
      gst_object_unref (parent_context);

      GST_INFO_OBJECT (thiz,
          "Creating new context %" GST_PTR_FORMAT " with joined session",
          thiz->context);
    } else {
      gst_msdk_context_add_job_type (thiz->context, GST_MSDK_JOB_ENCODER);
    }
  } else {
    gst_msdk_context_ensure_context (GST_ELEMENT_CAST (thiz), thiz->hardware,
        GST_MSDK_JOB_ENCODER);

    GST_INFO_OBJECT (thiz, "Creating new context %" GST_PTR_FORMAT,
        thiz->context);
  }

1404
1405
  gst_msdk_context_add_shared_async_depth (thiz->context, thiz->async_depth);

1406
1407
1408
1409
1410
  /* Set the minimum pts to some huge value (1000 hours). This keeps
     the dts at the start of the stream from needing to be
     negative. */
  gst_video_encoder_set_min_pts (encoder, GST_SECOND * 60 * 60 * 1000);

1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
  return TRUE;
}

static gboolean
gst_msdkenc_stop (GstVideoEncoder * encoder)
{
  GstMsdkEnc *thiz = GST_MSDKENC (encoder);

  gst_msdkenc_flush_frames (thiz, TRUE);
  gst_msdkenc_close_encoder (thiz);
  gst_msdkenc_dequeue_all_frames (thiz);

  if (thiz->input_state)
    gst_video_codec_state_unref (thiz->input_state);
  thiz->input_state = NULL;

1427
1428
  gst_object_replace ((GstObject **) & thiz->context, NULL);

1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
  return TRUE;
}

static gboolean
gst_msdkenc_flush (GstVideoEncoder * encoder)
{
  GstMsdkEnc *thiz = GST_MSDKENC (encoder);

  gst_msdkenc_flush_frames (thiz, TRUE);
  gst_msdkenc_close_encoder (thiz);
  gst_msdkenc_dequeue_all_frames (thiz);

  gst_msdkenc_init_encoder (thiz);

  return TRUE;
}

static GstFlowReturn
gst_msdkenc_finish (GstVideoEncoder * encoder)
{
  GstMsdkEnc *thiz = GST_MSDKENC (encoder);

  gst_msdkenc_flush_frames (thiz, FALSE);

  return GST_FLOW_OK;
}

1456

1457
1458
1459
1460
static gboolean
gst_msdkenc_propose_allocation (GstVideoEncoder * encoder, GstQuery * query)
{
  GstMsdkEnc *thiz = GST_MSDKENC (encoder);
Hyunjun Ko's avatar
Hyunjun Ko committed
1461
1462
1463
1464
  GstVideoInfo info;
  GstBufferPool *pool = NULL;
  GstAllocator *allocator = NULL;
  GstCaps *caps;
1465
1466
1467
1468
1469
  guint num_buffers;

  if (!thiz->input_state)
    return FALSE;

Hyunjun Ko's avatar
Hyunjun Ko committed
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
  gst_query_parse_allocation (query, &caps, NULL);

  if (!caps) {
    GST_INFO_OBJECT (encoder, "failed to get caps");
    return FALSE;
  }

  if (!gst_video_info_from_caps (&info, caps)) {
    GST_INFO_OBJECT (encoder, "failed to get video info");
    return FALSE;
  }

1482
1483
1484
1485
1486
1487
1488
  /* if upstream allocation query supports dmabuf-capsfeatures,
   *  we do allocate dmabuf backed memory */
  if (_gst_caps_has_feature (caps, GST_CAPS_FEATURE_MEMORY_DMABUF)) {
    GST_INFO_OBJECT (thiz, "MSDK VPP srcpad uses DMABuf memory");
    thiz->use_dmabuf = TRUE;
  }

1489
  num_buffers = gst_msdkenc_maximum_delayed_frames (thiz) + 1;
Hyunjun Ko's avatar
Hyunjun Ko committed
1490
1491
1492
1493
1494
1495
1496
1497
1498
  pool = gst_msdkenc_create_buffer_pool (thiz, caps, num_buffers, TRUE);

  gst_query_add_allocation_pool (query, pool, GST_VIDEO_INFO_SIZE (&info),
      num_buffers, 0);
  gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);

  if (pool) {
    GstStructure *config;
    GstAllocationParams params = { 0, 31, 0, 0, };
1499

Hyunjun Ko's avatar
Hyunjun Ko committed
1500
1501
1502
1503
1504
1505
1506
1507
    config = gst_buffer_pool_get_config (GST_BUFFER_POOL_CAST (pool));

    if (gst_buffer_pool_config_get_allocator (config, &allocator, NULL))
      gst_query_add_allocation_param (query, allocator, &params);
    gst_structure_free (config);
  }

  gst_object_unref (pool);
1508
1509
1510
1511
1512

  return GST_VIDEO_ENCODER_CLASS (parent_class)->propose_allocation (encoder,
      query);
}

1513

1514
static void
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
gst_msdkenc_finalize (GObject * object)
{
  GstMsdkEnc *thiz = GST_MSDKENC (object);

  if (thiz->input_state)
    gst_video_codec_state_unref (thiz->input_state);
  thiz->input_state = NULL;

  gst_object_replace ((GstObject **) & thiz->msdk_pool, NULL);
  gst_object_replace ((GstObject **) & thiz->msdk_converted_pool, NULL);

  G_OBJECT_CLASS (parent_class)->finalize (object);
}

static void
gst_msdkenc_class_init (GstMsdkEncClass * klass)
{
  GObjectClass *gobject_class;
  GstElementClass *element_class;
  GstVideoEncoderClass *gstencoder_class;

  gobject_class = G_OBJECT_CLASS (klass);
  element_class = GST_ELEMENT_CLASS (klass);
  gstencoder_class = GST_VIDEO_ENCODER_CLASS (klass);

  gobject_class->finalize = gst_msdkenc_finalize;

  element_class->set_context = gst_msdkenc_set_context;

  gstencoder_class->set_format = GST_DEBUG_FUNCPTR (gst_msdkenc_set_format);
  gstencoder_class->handle_frame = GST_DEBUG_FUNCPTR (gst_msdkenc_handle_frame);
  gstencoder_class->start = GST_DEBUG_FUNCPTR (gst_msdkenc_start);
  gstencoder_class->stop = GST_DEBUG_FUNCPTR (gst_msdkenc_stop);
  gstencoder_class->flush = GST_DEBUG_FUNCPTR (gst_msdkenc_flush);
  gstencoder_class->finish = GST_DEBUG_FUNCPTR (gst_msdkenc_finish);
  gstencoder_class->propose_allocation =
      GST_DEBUG_FUNCPTR (gst_msdkenc_propose_allocation);

  gst_element_class_add_static_pad_template (element_class, &sink_factory);
}

static void
gst_msdkenc_init (GstMsdkEnc * thiz)
{
  thiz->hardware = PROP_HARDWARE_DEFAULT;
  thiz->async_depth = PROP_ASYNC_DEPTH_DEFAULT;
  thiz->target_usage = PROP_TARGET_USAGE_DEFAULT;
  thiz->rate_control = PROP_RATE_CONTROL_DEFAULT;
  thiz->bitrate = PROP_BITRATE_DEFAULT;
1564
1565
1566
1567
1568
  thiz->max_frame_size = PROP_MAX_FRAME_SIZE_DEFAULT;
  thiz->max_vbv_bitrate = PROP_MAX_VBV_BITRATE_DEFAULT;
  thiz->accuracy = PROP_AVBR_ACCURACY_DEFAULT;
  thiz->convergence = PROP_AVBR_ACCURACY_DEFAULT;
  thiz->lookahead_depth = PROP_RC_LOOKAHEAD_DEPTH_DEFAULT;
1569
1570
1571
1572
1573
1574
1575
  thiz->qpi = PROP_QPI_DEFAULT;
  thiz->qpp = PROP_QPP_DEFAULT;
  thiz->qpb = PROP_QPB_DEFAULT;
  thiz->gop_size = PROP_GOP_SIZE_DEFAULT;
  thiz->ref_frames = PROP_REF_FRAMES_DEFAULT;
  thiz->i_frames = PROP_I_FRAMES_DEFAULT;
  thiz->b_frames = PROP_B_FRAMES_DEFAULT;
1576
  thiz->num_slices = PROP_NUM_SLICES_DEFAULT;
1577
1578
1579
  thiz->mbbrc = PROP_MBBRC_DEFAULT;
  thiz->adaptive_i = PROP_ADAPTIVE_I_DEFAULT;
  thiz->adaptive_b = PROP_ADAPTIVE_B_DEFAULT;
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
}

/* gst_msdkenc_set_common_property:
 *
 * This is a helper function to set the common property
 * of base encoder from subclass implementation.
 */
gboolean
gst_msdkenc_set_common_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
1590
1591
1592
{
  GstMsdkEnc *thiz = GST_MSDKENC (object);
  GstState state;
1593
  gboolean ret = TRUE;
1594
1595
1596
1597
1598

  GST_OBJECT_LOCK (thiz);

  state = GST_STATE (thiz);
  if ((state != GST_STATE_READY && state != GST_STATE_NULL) &&
1599
1600
      !(pspec->flags & GST_PARAM_MUTABLE_PLAYING)) {
    ret = FALSE;
1601
    goto wrong_state;
1602
  }
1603
1604

  switch (prop_id) {
1605
    case GST_MSDKENC_PROP_HARDWARE:
1606
1607
      thiz->hardware = g_value_get_boolean (value);
      break;
1608
    case GST_MSDKENC_PROP_ASYNC_DEPTH:
1609
1610
      thiz->async_depth = g_value_get_uint (value);
      break;
1611
    case GST_MSDKENC_PROP_TARGET_USAGE:
1612
1613
      thiz->target_usage = g_value_get_uint (value);
      break;
1614
    case GST_MSDKENC_PROP_RATE_CONTROL:
1615
1616
      thiz->rate_control = g_value_get_enum (value);
      break;
1617
    case GST_MSDKENC_PROP_BITRATE:
1618
1619
1620
      thiz->bitrate = g_value_get_uint (value);
      thiz->reconfig = TRUE;
      break;
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
    case GST_MSDKENC_PROP_MAX_FRAME_SIZE:
      thiz->max_frame_size = g_value_get_uint (value);
      break;
    case GST_MSDKENC_PROP_MAX_VBV_BITRATE:
      thiz->max_vbv_bitrate = g_value_get_uint (value);
      break;
    case GST_MSDKENC_PROP_AVBR_ACCURACY:
      thiz->accuracy = g_value_get_uint (value);
      break;
    case GST_MSDKENC_PROP_AVBR_CONVERGENCE:
      thiz->convergence = g_value_get_uint (value);
      break;
    case GST_MSDKENC_PROP_RC_LOOKAHEAD_DEPTH:
      thiz->lookahead_depth = g_value_get_uint (value);
      break;
    case GST_MSDKENC_PROP_QPI:
1637
1638
      thiz->qpi = g_value_get_uint (value);
      break;
1639
    case GST_MSDKENC_PROP_QPP:
1640
1641
      thiz->qpp = g_value_get_uint (value);
      break;
1642
    case GST_MSDKENC_PROP_QPB:
1643
1644
      thiz->qpb = g_value_get_uint (value);
      break;
1645
    case GST_MSDKENC_PROP_GOP_SIZE:
1646
1647
      thiz->gop_size = g_value_get_uint (value);
      break;
1648
    case GST_MSDKENC_PROP_REF_FRAMES:
1649
1650
      thiz->ref_frames = g_value_get_uint (value);
      break;
1651
    case GST_MSDKENC_PROP_I_FRAMES:
1652
1653
      thiz->i_frames = g_value_get_uint (value);
      break;
1654
    case GST_MSDKENC_PROP_B_FRAMES:
1655
1656
      thiz->b_frames = g_value_get_uint (value);
      break;
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
    case GST_MSDKENC_PROP_NUM_SLICES:
      thiz->num_slices = g_value_get_uint (value);
      break;
    case GST_MSDKENC_PROP_MBBRC:
      thiz->mbbrc = g_value_get_enum (value);
      break;
    case GST_MSDKENC_PROP_ADAPTIVE_I:
      thiz->adaptive_i = g_value_get_enum (value);
      break;
    case GST_MSDKENC_PROP_ADAPTIVE_B:
      thiz->adaptive_b = g_value_get_enum (value);
      break;
1669
    default:
1670
      ret = FALSE;
1671
1672
1673
      break;
  }
  GST_OBJECT_UNLOCK (thiz);
1674
  return ret;
1675
1676
1677
1678
1679
1680

  /* ERROR */
wrong_state:
  {
    GST_WARNING_OBJECT (thiz, "setting property in wrong state");
    GST_OBJECT_UNLOCK (thiz);
1681
    return ret;
1682
1683
1684
  }
}

1685
1686
1687
1688
1689
1690
1691
1692
/* gst_msdkenc_get_common_property:
 *
 * This is a helper function to get the common property
 * of base encoder from subclass implementation.
 */
gboolean
gst_msdkenc_get_common_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
1693
1694
{
  GstMsdkEnc *thiz = GST_MSDKENC (object);
1695
  gboolean ret = TRUE;
1696
1697
1698

  GST_OBJECT_LOCK (thiz);
  switch (prop_id) {
1699
    case GST_MSDKENC_PROP_HARDWARE:
1700
1701
      g_value_set_boolean (value, thiz->hardware);
      break;
1702
    case GST_MSDKENC_PROP_ASYNC_DEPTH:
1703
1704
      g_value_set_uint (value, thiz->async_depth);
      break;
1705
    case GST_MSDKENC_PROP_TARGET_USAGE:
1706
1707
      g_value_set_uint (value, thiz->target_usage);
      break;
Sreerenj Balachandran's avatar