2e273596b47d117f93311d42976664c3602300df
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / qsv / gstqsvjpegenc.cpp
1 /* GStreamer
2  * Copyright (C) 2022 Seungha Yang <seungha@centricular.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 /**
21  * SECTION:element-qsvjpegenc
22  * @title: qsvvp9enc
23  *
24  * Intel Quick Sync JPEG encoder
25  *
26  * ## Example launch line
27  * ```
28  * gst-launch-1.0 videotestsrc ! qsvjpegenc ! qtmux ! filesink location=out.mp4
29  * ```
30  *
31  * Since: 1.22
32  */
33
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37
38 #include "gstqsvjpegenc.h"
39 #include <vector>
40 #include <string>
41 #include <set>
42 #include <string.h>
43
44 #ifdef G_OS_WIN32
45 #include <gst/d3d11/gstd3d11.h>
46 #else
47 #include <gst/va/gstva.h>
48 #endif
49
50 GST_DEBUG_CATEGORY_STATIC (gst_qsv_jpeg_enc_debug);
51 #define GST_CAT_DEFAULT gst_qsv_jpeg_enc_debug
52
53 enum
54 {
55   PROP_0,
56   PROP_QUALITY,
57 };
58
59 #define DEFAULT_JPEG_QUALITY 85
60
61 #define DOC_SINK_CAPS_COMM \
62     "format = (string) { NV12, BGRA }, " \
63     "width = (int) [ 16, 16384 ], height = (int) [ 16, 16384 ]"
64
65 #define DOC_SINK_CAPS \
66     "video/x-raw(memory:D3D11Memory), " DOC_SINK_CAPS_COMM "; " \
67     "video/x-raw(memory:VAMemory), " DOC_SINK_CAPS_COMM "; " \
68     "video/x-raw, " DOC_SINK_CAPS_COMM
69
70 #define DOC_SRC_CAPS \
71     "image/jpeg, width = (int) [ 16, 16384 ], height = (int) [ 16, 16384 ]"
72
73 typedef struct _GstQsvJpegEncClassData
74 {
75   GstCaps *sink_caps;
76   GstCaps *src_caps;
77   guint impl_index;
78   gint64 adapter_luid;
79   gchar *display_path;
80   gchar *description;
81   gboolean interlaved;
82 } GstQsvJpegEncClassData;
83
84 typedef struct _GstQsvJpegEnc
85 {
86   GstQsvEncoder parent;
87
88   GMutex prop_lock;
89   /* protected by prop_lock */
90   gboolean property_updated;
91
92   /* properties */
93   guint quality;
94 } GstQsvJpegEnc;
95
96 typedef struct _GstQsvJpegEncClass
97 {
98   GstQsvEncoderClass parent_class;
99   gboolean interlaved;
100 } GstQsvJpegEncClass;
101
102 static GstElementClass *parent_class = nullptr;
103
104 #define GST_QSV_JPEG_ENC(object) ((GstQsvJpegEnc *) (object))
105 #define GST_QSV_JPEG_ENC_GET_CLASS(object) \
106     (G_TYPE_INSTANCE_GET_CLASS ((object),G_TYPE_FROM_INSTANCE (object),GstQsvJpegEncClass))
107
108 static void gst_qsv_jpeg_enc_finalize (GObject * object);
109 static void gst_qsv_jpeg_enc_set_property (GObject * object, guint prop_id,
110     const GValue * value, GParamSpec * pspec);
111 static void gst_qsv_jpeg_enc_get_property (GObject * object, guint prop_id,
112     GValue * value, GParamSpec * pspec);
113
114 static gboolean gst_qsv_jpeg_enc_set_format (GstQsvEncoder * encoder,
115     GstVideoCodecState * state, mfxVideoParam * param,
116     GPtrArray * extra_params);
117 static gboolean gst_qsv_jpeg_enc_set_output_state (GstQsvEncoder * encoder,
118     GstVideoCodecState * state, mfxSession session);
119 static GstQsvEncoderReconfigure
120 gst_qsv_jpeg_enc_check_reconfigure (GstQsvEncoder * encoder, mfxSession session,
121     mfxVideoParam * param, GPtrArray * extra_params);
122
123 static void
124 gst_qsv_jpeg_enc_class_init (GstQsvJpegEncClass * klass, gpointer data)
125 {
126   GObjectClass *object_class = G_OBJECT_CLASS (klass);
127   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
128   GstQsvEncoderClass *qsvenc_class = GST_QSV_ENCODER_CLASS (klass);
129   GstQsvJpegEncClassData *cdata = (GstQsvJpegEncClassData *) data;
130   GstPadTemplate *pad_templ;
131   GstCaps *doc_caps;
132
133   qsvenc_class->codec_id = MFX_CODEC_JPEG;
134   qsvenc_class->impl_index = cdata->impl_index;
135   qsvenc_class->adapter_luid = cdata->adapter_luid;
136   qsvenc_class->display_path = cdata->display_path;
137
138   object_class->finalize = gst_qsv_jpeg_enc_finalize;
139   object_class->set_property = gst_qsv_jpeg_enc_set_property;
140   object_class->get_property = gst_qsv_jpeg_enc_get_property;
141
142   g_object_class_install_property (object_class, PROP_QUALITY,
143       g_param_spec_uint ("quality", "Quality",
144           "Encoding quality, 100 for best quality",
145           1, 100, DEFAULT_JPEG_QUALITY, (GParamFlags)
146           (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
147
148   parent_class = (GstElementClass *) g_type_class_peek_parent (klass);
149
150 #ifdef G_OS_WIN32
151   std::string long_name = "Intel Quick Sync Video " +
152       std::string (cdata->description) + " JPEG Encoder";
153
154   gst_element_class_set_metadata (element_class, long_name.c_str (),
155       "Codec/Encoder/Video/Hardware",
156       "Intel Quick Sync Video JPEG Encoder",
157       "Seungha Yang <seungha@centricular.com>");
158 #else
159   gst_element_class_set_static_metadata (element_class,
160       "Intel Quick Sync Video JPEG Encoder",
161       "Codec/Encoder/Video/Hardware",
162       "Intel Quick Sync Video JPEG Encoder",
163       "Seungha Yang <seungha@centricular.com>");
164 #endif
165
166   pad_templ = gst_pad_template_new ("sink",
167       GST_PAD_SINK, GST_PAD_ALWAYS, cdata->sink_caps);
168   doc_caps = gst_caps_from_string (DOC_SINK_CAPS);
169   gst_pad_template_set_documentation_caps (pad_templ, doc_caps);
170   gst_caps_unref (doc_caps);
171   gst_element_class_add_pad_template (element_class, pad_templ);
172
173   pad_templ = gst_pad_template_new ("src",
174       GST_PAD_SRC, GST_PAD_ALWAYS, cdata->src_caps);
175   doc_caps = gst_caps_from_string (DOC_SRC_CAPS);
176   gst_pad_template_set_documentation_caps (pad_templ, doc_caps);
177   gst_caps_unref (doc_caps);
178   gst_element_class_add_pad_template (element_class, pad_templ);
179
180   qsvenc_class->set_format = GST_DEBUG_FUNCPTR (gst_qsv_jpeg_enc_set_format);
181   qsvenc_class->set_output_state =
182       GST_DEBUG_FUNCPTR (gst_qsv_jpeg_enc_set_output_state);
183   qsvenc_class->check_reconfigure =
184       GST_DEBUG_FUNCPTR (gst_qsv_jpeg_enc_check_reconfigure);
185
186   klass->interlaved = cdata->interlaved;
187
188   gst_caps_unref (cdata->sink_caps);
189   gst_caps_unref (cdata->src_caps);
190   g_free (cdata->description);
191   g_free (cdata);
192 }
193
194 static void
195 gst_qsv_jpeg_enc_init (GstQsvJpegEnc * self)
196 {
197   self->quality = DEFAULT_JPEG_QUALITY;
198
199   g_mutex_init (&self->prop_lock);
200 }
201
202 static void
203 gst_qsv_jpeg_enc_finalize (GObject * object)
204 {
205   GstQsvJpegEnc *self = GST_QSV_JPEG_ENC (object);
206
207   g_mutex_clear (&self->prop_lock);
208
209   G_OBJECT_CLASS (parent_class)->finalize (object);
210 }
211
212 static void
213 gst_qsv_jpeg_enc_check_update_uint (GstQsvJpegEnc * self, guint * old_val,
214     guint new_val)
215 {
216   if (*old_val == new_val)
217     return;
218
219   g_mutex_lock (&self->prop_lock);
220   *old_val = new_val;
221   self->property_updated = TRUE;
222   g_mutex_unlock (&self->prop_lock);
223 }
224
225 static void
226 gst_qsv_jpeg_enc_set_property (GObject * object, guint prop_id,
227     const GValue * value, GParamSpec * pspec)
228 {
229   GstQsvJpegEnc *self = GST_QSV_JPEG_ENC (object);
230
231   switch (prop_id) {
232     case PROP_QUALITY:
233       gst_qsv_jpeg_enc_check_update_uint (self, &self->quality,
234           g_value_get_uint (value));
235       break;
236     default:
237       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
238       break;
239   }
240 }
241
242 static void
243 gst_qsv_jpeg_enc_get_property (GObject * object, guint prop_id, GValue * value,
244     GParamSpec * pspec)
245 {
246   GstQsvJpegEnc *self = GST_QSV_JPEG_ENC (object);
247
248   switch (prop_id) {
249     case PROP_QUALITY:
250       g_value_set_uint (value, self->quality);
251       break;
252     default:
253       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
254       break;
255   }
256 }
257
258 static gboolean
259 gst_qsv_jpeg_enc_set_format (GstQsvEncoder * encoder,
260     GstVideoCodecState * state, mfxVideoParam * param, GPtrArray * extra_params)
261 {
262   GstQsvJpegEnc *self = GST_QSV_JPEG_ENC (encoder);
263   GstQsvJpegEncClass *klass = GST_QSV_JPEG_ENC_GET_CLASS (self);
264   GstVideoInfo *info = &state->info;
265   mfxFrameInfo *frame_info;
266
267   frame_info = &param->mfx.FrameInfo;
268
269   frame_info->Width = frame_info->CropW = info->width;
270   frame_info->Height = frame_info->CropH = info->height;
271
272   frame_info->PicStruct = MFX_PICSTRUCT_PROGRESSIVE;
273
274   if (GST_VIDEO_INFO_FPS_N (info) > 0 && GST_VIDEO_INFO_FPS_D (info) > 0) {
275     frame_info->FrameRateExtN = GST_VIDEO_INFO_FPS_N (info);
276     frame_info->FrameRateExtD = GST_VIDEO_INFO_FPS_D (info);
277   } else {
278     /* HACK: Same as x264enc */
279     frame_info->FrameRateExtN = 25;
280     frame_info->FrameRateExtD = 1;
281   }
282
283   frame_info->AspectRatioW = GST_VIDEO_INFO_PAR_N (info);
284   frame_info->AspectRatioH = GST_VIDEO_INFO_PAR_D (info);
285
286   switch (GST_VIDEO_INFO_FORMAT (info)) {
287     case GST_VIDEO_FORMAT_NV12:
288       frame_info->ChromaFormat = MFX_CHROMAFORMAT_YUV420;
289       frame_info->FourCC = MFX_FOURCC_NV12;
290       frame_info->BitDepthLuma = 8;
291       frame_info->BitDepthChroma = 8;
292       break;
293     case GST_VIDEO_FORMAT_BGRA:
294       frame_info->ChromaFormat = MFX_CHROMAFORMAT_YUV444;
295       frame_info->FourCC = MFX_FOURCC_RGB4;
296       break;
297     default:
298       GST_ERROR_OBJECT (self, "Unexpected format %s",
299           gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (info)));
300       return FALSE;
301   }
302
303   g_mutex_lock (&self->prop_lock);
304   param->mfx.CodecId = MFX_CODEC_JPEG;
305   param->mfx.CodecProfile = MFX_PROFILE_JPEG_BASELINE;
306   param->mfx.Quality = self->quality;
307   if (klass->interlaved)
308     param->mfx.Interleaved = 1;
309   else
310     param->mfx.Interleaved = 0;
311   param->mfx.RestartInterval = 0;
312   param->ExtParam = (mfxExtBuffer **) extra_params->pdata;
313   param->NumExtParam = extra_params->len;
314
315   self->property_updated = FALSE;
316   g_mutex_unlock (&self->prop_lock);
317
318   return TRUE;
319 }
320
321 static gboolean
322 gst_qsv_jpeg_enc_set_output_state (GstQsvEncoder * encoder,
323     GstVideoCodecState * state, mfxSession session)
324 {
325   GstCaps *caps;
326   GstTagList *tags;
327   GstVideoCodecState *out_state;
328
329   caps = gst_caps_from_string ("image/jpeg");
330   out_state = gst_video_encoder_set_output_state (GST_VIDEO_ENCODER (encoder),
331       caps, state);
332   gst_video_codec_state_unref (out_state);
333
334   tags = gst_tag_list_new_empty ();
335   gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, "qsvjpegenc",
336       nullptr);
337
338   gst_video_encoder_merge_tags (GST_VIDEO_ENCODER (encoder),
339       tags, GST_TAG_MERGE_REPLACE);
340   gst_tag_list_unref (tags);
341
342   return TRUE;
343 }
344
345 static GstQsvEncoderReconfigure
346 gst_qsv_jpeg_enc_check_reconfigure (GstQsvEncoder * encoder, mfxSession session,
347     mfxVideoParam * param, GPtrArray * extra_params)
348 {
349   GstQsvJpegEnc *self = GST_QSV_JPEG_ENC (encoder);
350   GstQsvEncoderReconfigure ret = GST_QSV_ENCODER_RECONFIGURE_NONE;
351
352   g_mutex_lock (&self->prop_lock);
353   if (self->property_updated)
354     ret = GST_QSV_ENCODER_RECONFIGURE_FULL;
355
356   self->property_updated = FALSE;
357   g_mutex_unlock (&self->prop_lock);
358
359   return ret;
360 }
361
362 void
363 gst_qsv_jpeg_enc_register (GstPlugin * plugin, guint rank, guint impl_index,
364     GstObject * device, mfxSession session)
365 {
366   mfxVideoParam param;
367   mfxInfoMFX *mfx;
368   std::vector < std::string > supported_formats;
369   GstQsvResolution max_resolution;
370   mfxStatus status;
371   gboolean interlaved = TRUE;
372
373   GST_DEBUG_CATEGORY_INIT (gst_qsv_jpeg_enc_debug,
374       "qsvjpegenc", 0, "qsvjpegenc");
375
376   memset (&param, 0, sizeof (mfxVideoParam));
377   memset (&max_resolution, 0, sizeof (GstQsvResolution));
378
379   param.AsyncDepth = 4;
380   param.IOPattern = MFX_IOPATTERN_IN_VIDEO_MEMORY;
381
382   mfx = &param.mfx;
383   mfx->LowPower = MFX_CODINGOPTION_UNKNOWN;
384   mfx->CodecId = MFX_CODEC_JPEG;
385   mfx->CodecProfile = MFX_PROFILE_JPEG_BASELINE;
386   mfx->Quality = DEFAULT_JPEG_QUALITY;
387   mfx->Interleaved = 1;
388   mfx->RestartInterval = 0;
389
390   mfx->FrameInfo.Width = mfx->FrameInfo.CropW = GST_ROUND_UP_16 (320);
391   mfx->FrameInfo.Height = mfx->FrameInfo.CropH = GST_ROUND_UP_16 (240);
392
393   mfx->FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
394   mfx->FrameInfo.FourCC = MFX_FOURCC_NV12;
395   mfx->FrameInfo.FrameRateExtN = 30;
396   mfx->FrameInfo.FrameRateExtD = 1;
397   mfx->FrameInfo.AspectRatioW = 1;
398   mfx->FrameInfo.AspectRatioH = 1;
399   mfx->FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE;
400
401   status = MFXVideoENCODE_Query (session, &param, &param);
402   if (status == MFX_WRN_PARTIAL_ACCELERATION) {
403     mfx->Interleaved = 0;
404     interlaved = FALSE;
405
406     status = MFXVideoENCODE_Query (session, &param, &param);
407   }
408
409   if (status != MFX_ERR_NONE)
410     return;
411
412   supported_formats.push_back ("NV12");
413
414   mfx->FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV444;
415   mfx->FrameInfo.FourCC = MFX_FOURCC_RGB4;
416   status = MFXVideoENCODE_Query (session, &param, &param);
417
418   /* TODO: Add YUY2 support, d3d11 doesn't support the format yet */
419   if (status == MFX_ERR_NONE)
420     supported_formats.push_back ("BGRA");
421
422   mfx->FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
423   mfx->FrameInfo.FourCC = MFX_FOURCC_NV12;
424
425   /* Check max-resolution */
426   for (guint i = 0; i < G_N_ELEMENTS (gst_qsv_resolutions); i++) {
427     mfx->FrameInfo.Width = mfx->FrameInfo.CropW = gst_qsv_resolutions[i].width;
428     mfx->FrameInfo.Height = mfx->FrameInfo.CropH =
429         gst_qsv_resolutions[i].height;
430
431     if (MFXVideoENCODE_Query (session, &param, &param) != MFX_ERR_NONE)
432       break;
433
434     max_resolution.width = gst_qsv_resolutions[i].width;
435     max_resolution.height = gst_qsv_resolutions[i].height;
436   }
437
438   GST_INFO ("Maximum supported resolution: %dx%d",
439       max_resolution.width, max_resolution.height);
440
441   /* To cover both landscape and portrait,
442    * select max value (width in this case) */
443   guint resolution = MAX (max_resolution.width, max_resolution.height);
444   std::string sink_caps_str = "video/x-raw";
445
446   sink_caps_str += ", width=(int) [ 16, " + std::to_string (resolution) + " ]";
447   sink_caps_str += ", height=(int) [ 16, " + std::to_string (resolution) + " ]";
448
449   /* *INDENT-OFF* */
450   if (supported_formats.size () > 1) {
451     sink_caps_str += ", format=(string) { ";
452     bool first = true;
453     for (const auto &iter: supported_formats) {
454       if (!first) {
455         sink_caps_str += ", ";
456       }
457
458       sink_caps_str += iter;
459       first = false;
460     }
461     sink_caps_str += " }";
462   } else {
463     sink_caps_str += ", format=(string) " + supported_formats[0];
464   }
465   /* *INDENT-ON* */
466
467   GstCaps *sink_caps = gst_caps_from_string (sink_caps_str.c_str ());
468
469 #ifdef G_OS_WIN32
470   GstCaps *d3d11_caps = gst_caps_copy (sink_caps);
471   GstCapsFeatures *caps_features =
472       gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, nullptr);
473   gst_caps_set_features_simple (d3d11_caps, caps_features);
474   gst_caps_append (d3d11_caps, sink_caps);
475   sink_caps = d3d11_caps;
476 #else
477   GstCaps *va_caps = gst_caps_copy (sink_caps);
478   GstCapsFeatures *caps_features =
479       gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_VA, nullptr);
480   gst_caps_set_features_simple (va_caps, caps_features);
481   gst_caps_append (va_caps, sink_caps);
482   sink_caps = va_caps;
483 #endif
484
485   std::string src_caps_str = "image/jpeg";
486   src_caps_str += ", width=(int) [ 16, " + std::to_string (resolution) + " ]";
487   src_caps_str += ", height=(int) [ 16, " + std::to_string (resolution) + " ]";
488
489   GstCaps *src_caps = gst_caps_from_string (src_caps_str.c_str ());
490
491   GST_MINI_OBJECT_FLAG_SET (sink_caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
492   GST_MINI_OBJECT_FLAG_SET (src_caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
493
494   GstQsvJpegEncClassData *cdata = g_new0 (GstQsvJpegEncClassData, 1);
495   cdata->sink_caps = sink_caps;
496   cdata->src_caps = src_caps;
497   cdata->impl_index = impl_index;
498   cdata->interlaved = interlaved;
499
500 #ifdef G_OS_WIN32
501   g_object_get (device, "adapter-luid", &cdata->adapter_luid,
502       "description", &cdata->description, nullptr);
503 #else
504   g_object_get (device, "path", &cdata->display_path, nullptr);
505 #endif
506
507   GType type;
508   gchar *type_name;
509   gchar *feature_name;
510   GTypeInfo type_info = {
511     sizeof (GstQsvJpegEncClass),
512     nullptr,
513     nullptr,
514     (GClassInitFunc) gst_qsv_jpeg_enc_class_init,
515     nullptr,
516     cdata,
517     sizeof (GstQsvJpegEnc),
518     0,
519     (GInstanceInitFunc) gst_qsv_jpeg_enc_init,
520   };
521
522   type_name = g_strdup ("GstQsvJpegEnc");
523   feature_name = g_strdup ("qsvjpegenc");
524
525   gint index = 0;
526   while (g_type_from_name (type_name)) {
527     index++;
528     g_free (type_name);
529     g_free (feature_name);
530     type_name = g_strdup_printf ("GstQsvJpegDevice%dEnc", index);
531     feature_name = g_strdup_printf ("qsvjpegdevice%denc", index);
532   }
533
534   type = g_type_register_static (GST_TYPE_QSV_ENCODER, type_name, &type_info,
535       (GTypeFlags) 0);
536
537   if (rank > 0 && index != 0)
538     rank--;
539
540   if (index != 0)
541     gst_element_type_set_skip_documentation (type);
542
543   if (!gst_element_register (plugin, feature_name, rank, type))
544     GST_WARNING ("Failed to register plugin '%s'", type_name);
545
546   g_free (type_name);
547   g_free (feature_name);
548 }