2 * Copyright (C) 2011, Hewlett-Packard Development Company, L.P.
3 * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation
8 * version 2.1 of the License.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27 #include "gstomxh264enc.h"
29 #ifdef USE_OMX_TARGET_RPI
30 #include <OMX_Broadcom.h>
31 #include <OMX_Index.h>
34 GST_DEBUG_CATEGORY_STATIC (gst_omx_h264_enc_debug_category);
35 #define GST_CAT_DEFAULT gst_omx_h264_enc_debug_category
38 static gboolean gst_omx_h264_enc_set_format (GstOMXVideoEnc * enc,
39 GstOMXPort * port, GstVideoCodecState * state);
40 static GstCaps *gst_omx_h264_enc_get_caps (GstOMXVideoEnc * enc,
41 GstOMXPort * port, GstVideoCodecState * state);
42 static GstFlowReturn gst_omx_h264_enc_handle_output_frame (GstOMXVideoEnc *
43 self, GstOMXPort * port, GstOMXBuffer * buf, GstVideoCodecFrame * frame);
44 static gboolean gst_omx_h264_enc_flush (GstVideoEncoder * enc);
45 static gboolean gst_omx_h264_enc_stop (GstVideoEncoder * enc);
46 static void gst_omx_h264_enc_set_property (GObject * object, guint prop_id,
47 const GValue * value, GParamSpec * pspec);
48 static void gst_omx_h264_enc_get_property (GObject * object, guint prop_id,
49 GValue * value, GParamSpec * pspec);
54 #ifdef USE_OMX_TARGET_RPI
55 PROP_INLINESPSPPSHEADERS,
57 PROP_PERIODICITYOFIDRFRAMES,
58 PROP_INTERVALOFCODINGINTRAFRAMES
61 #ifdef USE_OMX_TARGET_RPI
62 #define GST_OMX_H264_VIDEO_ENC_INLINE_SPS_PPS_HEADERS_DEFAULT TRUE
64 #define GST_OMX_H264_VIDEO_ENC_PERIODICITY_OF_IDR_FRAMES_DEFAULT (0xffffffff)
65 #define GST_OMX_H264_VIDEO_ENC_INTERVAL_OF_CODING_INTRA_FRAMES_DEFAULT (0xffffffff)
68 /* class initialization */
71 GST_DEBUG_CATEGORY_INIT (gst_omx_h264_enc_debug_category, "omxh264enc", 0, \
72 "debug category for gst-omx video encoder base class");
74 #define parent_class gst_omx_h264_enc_parent_class
75 G_DEFINE_TYPE_WITH_CODE (GstOMXH264Enc, gst_omx_h264_enc,
76 GST_TYPE_OMX_VIDEO_ENC, DEBUG_INIT);
79 gst_omx_h264_enc_class_init (GstOMXH264EncClass * klass)
81 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
82 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
83 GstVideoEncoderClass *basevideoenc_class = GST_VIDEO_ENCODER_CLASS (klass);
84 GstOMXVideoEncClass *videoenc_class = GST_OMX_VIDEO_ENC_CLASS (klass);
86 videoenc_class->set_format = GST_DEBUG_FUNCPTR (gst_omx_h264_enc_set_format);
87 videoenc_class->get_caps = GST_DEBUG_FUNCPTR (gst_omx_h264_enc_get_caps);
89 gobject_class->set_property = gst_omx_h264_enc_set_property;
90 gobject_class->get_property = gst_omx_h264_enc_get_property;
92 #ifdef USE_OMX_TARGET_RPI
93 g_object_class_install_property (gobject_class, PROP_INLINESPSPPSHEADERS,
94 g_param_spec_boolean ("inline-header",
95 "Inline SPS/PPS headers before IDR",
96 "Inline SPS/PPS header before IDR",
97 GST_OMX_H264_VIDEO_ENC_INLINE_SPS_PPS_HEADERS_DEFAULT,
98 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
99 GST_PARAM_MUTABLE_READY));
102 g_object_class_install_property (gobject_class, PROP_PERIODICITYOFIDRFRAMES,
103 g_param_spec_uint ("periodicty-idr", "Target Bitrate",
104 "Periodicity of IDR frames (0xffffffff=component default)",
106 GST_OMX_H264_VIDEO_ENC_PERIODICITY_OF_IDR_FRAMES_DEFAULT,
107 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
108 GST_PARAM_MUTABLE_READY));
110 g_object_class_install_property (gobject_class,
111 PROP_INTERVALOFCODINGINTRAFRAMES,
112 g_param_spec_uint ("interval-intraframes",
113 "Interval of coding Intra frames",
114 "Interval of coding Intra frames (0xffffffff=component default)", 0,
116 GST_OMX_H264_VIDEO_ENC_INTERVAL_OF_CODING_INTRA_FRAMES_DEFAULT,
117 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
118 GST_PARAM_MUTABLE_READY));
120 basevideoenc_class->flush = gst_omx_h264_enc_flush;
121 basevideoenc_class->stop = gst_omx_h264_enc_stop;
123 videoenc_class->cdata.default_src_template_caps = "video/x-h264, "
124 "width=(int) [ 16, 4096 ], " "height=(int) [ 16, 4096 ]";
125 videoenc_class->handle_output_frame =
126 GST_DEBUG_FUNCPTR (gst_omx_h264_enc_handle_output_frame);
128 gst_element_class_set_static_metadata (element_class,
129 "OpenMAX H.264 Video Encoder",
130 "Codec/Encoder/Video",
131 "Encode H.264 video streams",
132 "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
134 gst_omx_set_default_role (&videoenc_class->cdata, "video_encoder.avc");
138 gst_omx_h264_enc_set_property (GObject * object, guint prop_id,
139 const GValue * value, GParamSpec * pspec)
141 GstOMXH264Enc *self = GST_OMX_H264_ENC (object);
144 #ifdef USE_OMX_TARGET_RPI
145 case PROP_INLINESPSPPSHEADERS:
146 self->inline_sps_pps_headers = g_value_get_boolean (value);
149 case PROP_PERIODICITYOFIDRFRAMES:
150 self->periodicty_idr = g_value_get_uint (value);
152 case PROP_INTERVALOFCODINGINTRAFRAMES:
153 self->interval_intraframes = g_value_get_uint (value);
156 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
162 gst_omx_h264_enc_get_property (GObject * object, guint prop_id, GValue * value,
165 GstOMXH264Enc *self = GST_OMX_H264_ENC (object);
168 #ifdef USE_OMX_TARGET_RPI
169 case PROP_INLINESPSPPSHEADERS:
170 g_value_set_boolean (value, self->inline_sps_pps_headers);
173 case PROP_PERIODICITYOFIDRFRAMES:
174 g_value_set_uint (value, self->periodicty_idr);
176 case PROP_INTERVALOFCODINGINTRAFRAMES:
177 g_value_set_uint (value, self->interval_intraframes);
180 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
186 gst_omx_h264_enc_init (GstOMXH264Enc * self)
188 #ifdef USE_OMX_TARGET_RPI
189 self->inline_sps_pps_headers =
190 GST_OMX_H264_VIDEO_ENC_INLINE_SPS_PPS_HEADERS_DEFAULT;
192 self->periodicty_idr =
193 GST_OMX_H264_VIDEO_ENC_PERIODICITY_OF_IDR_FRAMES_DEFAULT;
194 self->interval_intraframes =
195 GST_OMX_H264_VIDEO_ENC_INTERVAL_OF_CODING_INTRA_FRAMES_DEFAULT;
199 gst_omx_h264_enc_flush (GstVideoEncoder * enc)
201 GstOMXH264Enc *self = GST_OMX_H264_ENC (enc);
203 g_list_free_full (self->headers, (GDestroyNotify) gst_buffer_unref);
204 self->headers = NULL;
206 return GST_VIDEO_ENCODER_CLASS (parent_class)->flush (enc);
210 gst_omx_h264_enc_stop (GstVideoEncoder * enc)
212 GstOMXH264Enc *self = GST_OMX_H264_ENC (enc);
214 g_list_free_full (self->headers, (GDestroyNotify) gst_buffer_unref);
215 self->headers = NULL;
217 return GST_VIDEO_ENCODER_CLASS (parent_class)->stop (enc);
221 gst_omx_h264_enc_set_format (GstOMXVideoEnc * enc, GstOMXPort * port,
222 GstVideoCodecState * state)
224 GstOMXH264Enc *self = GST_OMX_H264_ENC (enc);
226 OMX_PARAM_PORTDEFINITIONTYPE port_def;
227 OMX_VIDEO_PARAM_PROFILELEVELTYPE param;
228 OMX_VIDEO_CONFIG_AVCINTRAPERIOD config_avcintraperiod;
229 #ifdef USE_OMX_TARGET_RPI
230 OMX_CONFIG_PORTBOOLEANTYPE config_inline_header;
233 const gchar *profile_string, *level_string;
235 #ifdef USE_OMX_TARGET_RPI
236 GST_OMX_INIT_STRUCT (&config_inline_header);
237 config_inline_header.nPortIndex =
238 GST_OMX_VIDEO_ENC (self)->enc_out_port->index;
240 gst_omx_component_get_parameter (GST_OMX_VIDEO_ENC (self)->enc,
241 OMX_IndexParamBrcmVideoAVCInlineHeaderEnable, &config_inline_header);
242 if (err != OMX_ErrorNone) {
243 GST_ERROR_OBJECT (self,
244 "can't get OMX_IndexParamBrcmVideoAVCInlineHeaderEnable %s (0x%08x)",
245 gst_omx_error_to_string (err), err);
249 if (self->inline_sps_pps_headers) {
250 config_inline_header.bEnabled = OMX_TRUE;
252 config_inline_header.bEnabled = OMX_FALSE;
256 gst_omx_component_set_parameter (GST_OMX_VIDEO_ENC (self)->enc,
257 OMX_IndexParamBrcmVideoAVCInlineHeaderEnable, &config_inline_header);
258 if (err != OMX_ErrorNone) {
259 GST_ERROR_OBJECT (self,
260 "can't set OMX_IndexParamBrcmVideoAVCInlineHeaderEnable %s (0x%08x)",
261 gst_omx_error_to_string (err), err);
266 if (self->periodicty_idr !=
267 GST_OMX_H264_VIDEO_ENC_PERIODICITY_OF_IDR_FRAMES_DEFAULT
268 || self->interval_intraframes !=
269 GST_OMX_H264_VIDEO_ENC_INTERVAL_OF_CODING_INTRA_FRAMES_DEFAULT) {
272 GST_OMX_INIT_STRUCT (&config_avcintraperiod);
273 config_avcintraperiod.nPortIndex =
274 GST_OMX_VIDEO_ENC (self)->enc_out_port->index;
276 gst_omx_component_get_parameter (GST_OMX_VIDEO_ENC (self)->enc,
277 OMX_IndexConfigVideoAVCIntraPeriod, &config_avcintraperiod);
278 if (err != OMX_ErrorNone) {
279 GST_ERROR_OBJECT (self,
280 "can't get OMX_IndexConfigVideoAVCIntraPeriod %s (0x%08x)",
281 gst_omx_error_to_string (err), err);
285 GST_DEBUG_OBJECT (self, "default nPFrames:%u, nIDRPeriod:%u",
286 (guint) config_avcintraperiod.nPFrames,
287 (guint) config_avcintraperiod.nIDRPeriod);
289 if (self->periodicty_idr !=
290 GST_OMX_H264_VIDEO_ENC_PERIODICITY_OF_IDR_FRAMES_DEFAULT) {
291 config_avcintraperiod.nIDRPeriod = self->periodicty_idr;
294 if (self->interval_intraframes !=
295 GST_OMX_H264_VIDEO_ENC_INTERVAL_OF_CODING_INTRA_FRAMES_DEFAULT) {
296 config_avcintraperiod.nPFrames = self->interval_intraframes;
300 gst_omx_component_set_parameter (GST_OMX_VIDEO_ENC (self)->enc,
301 OMX_IndexConfigVideoAVCIntraPeriod, &config_avcintraperiod);
302 if (err != OMX_ErrorNone) {
303 GST_ERROR_OBJECT (self,
304 "can't set OMX_IndexConfigVideoAVCIntraPeriod %s (0x%08x)",
305 gst_omx_error_to_string (err), err);
310 gst_omx_port_get_port_definition (GST_OMX_VIDEO_ENC (self)->enc_out_port,
312 port_def.format.video.eCompressionFormat = OMX_VIDEO_CodingAVC;
314 gst_omx_port_update_port_definition (GST_OMX_VIDEO_ENC
315 (self)->enc_out_port, &port_def);
316 if (err != OMX_ErrorNone)
319 GST_OMX_INIT_STRUCT (¶m);
320 param.nPortIndex = GST_OMX_VIDEO_ENC (self)->enc_out_port->index;
323 gst_omx_component_get_parameter (GST_OMX_VIDEO_ENC (self)->enc,
324 OMX_IndexParamVideoProfileLevelCurrent, ¶m);
325 if (err != OMX_ErrorNone) {
326 GST_WARNING_OBJECT (self,
327 "Setting profile/level not supported by component");
331 peercaps = gst_pad_peer_query_caps (GST_VIDEO_ENCODER_SRC_PAD (enc),
332 gst_pad_get_pad_template_caps (GST_VIDEO_ENCODER_SRC_PAD (enc)));
336 if (gst_caps_is_empty (peercaps)) {
337 gst_caps_unref (peercaps);
338 GST_ERROR_OBJECT (self, "Empty caps");
342 s = gst_caps_get_structure (peercaps, 0);
343 profile_string = gst_structure_get_string (s, "profile");
344 if (profile_string) {
345 if (g_str_equal (profile_string, "baseline")) {
346 param.eProfile = OMX_VIDEO_AVCProfileBaseline;
347 } else if (g_str_equal (profile_string, "main")) {
348 param.eProfile = OMX_VIDEO_AVCProfileMain;
349 } else if (g_str_equal (profile_string, "extended")) {
350 param.eProfile = OMX_VIDEO_AVCProfileExtended;
351 } else if (g_str_equal (profile_string, "high")) {
352 param.eProfile = OMX_VIDEO_AVCProfileHigh;
353 } else if (g_str_equal (profile_string, "high-10")) {
354 param.eProfile = OMX_VIDEO_AVCProfileHigh10;
355 } else if (g_str_equal (profile_string, "high-4:2:2")) {
356 param.eProfile = OMX_VIDEO_AVCProfileHigh422;
357 } else if (g_str_equal (profile_string, "high-4:4:4")) {
358 param.eProfile = OMX_VIDEO_AVCProfileHigh444;
360 goto unsupported_profile;
363 level_string = gst_structure_get_string (s, "level");
365 if (g_str_equal (level_string, "1")) {
366 param.eLevel = OMX_VIDEO_AVCLevel1;
367 } else if (g_str_equal (level_string, "1b")) {
368 param.eLevel = OMX_VIDEO_AVCLevel1b;
369 } else if (g_str_equal (level_string, "1.1")) {
370 param.eLevel = OMX_VIDEO_AVCLevel11;
371 } else if (g_str_equal (level_string, "1.2")) {
372 param.eLevel = OMX_VIDEO_AVCLevel12;
373 } else if (g_str_equal (level_string, "1.3")) {
374 param.eLevel = OMX_VIDEO_AVCLevel13;
375 } else if (g_str_equal (level_string, "2")) {
376 param.eLevel = OMX_VIDEO_AVCLevel2;
377 } else if (g_str_equal (level_string, "2.1")) {
378 param.eLevel = OMX_VIDEO_AVCLevel21;
379 } else if (g_str_equal (level_string, "2.2")) {
380 param.eLevel = OMX_VIDEO_AVCLevel22;
381 } else if (g_str_equal (level_string, "3")) {
382 param.eLevel = OMX_VIDEO_AVCLevel3;
383 } else if (g_str_equal (level_string, "3.1")) {
384 param.eLevel = OMX_VIDEO_AVCLevel31;
385 } else if (g_str_equal (level_string, "3.2")) {
386 param.eLevel = OMX_VIDEO_AVCLevel32;
387 } else if (g_str_equal (level_string, "4")) {
388 param.eLevel = OMX_VIDEO_AVCLevel4;
389 } else if (g_str_equal (level_string, "4.1")) {
390 param.eLevel = OMX_VIDEO_AVCLevel41;
391 } else if (g_str_equal (level_string, "4.2")) {
392 param.eLevel = OMX_VIDEO_AVCLevel42;
393 } else if (g_str_equal (level_string, "5")) {
394 param.eLevel = OMX_VIDEO_AVCLevel5;
395 } else if (g_str_equal (level_string, "5.1")) {
396 param.eLevel = OMX_VIDEO_AVCLevel51;
398 goto unsupported_level;
401 gst_caps_unref (peercaps);
405 gst_omx_component_set_parameter (GST_OMX_VIDEO_ENC (self)->enc,
406 OMX_IndexParamVideoProfileLevelCurrent, ¶m);
407 if (err == OMX_ErrorUnsupportedIndex) {
408 GST_WARNING_OBJECT (self,
409 "Setting profile/level not supported by component");
410 } else if (err != OMX_ErrorNone) {
411 GST_ERROR_OBJECT (self,
412 "Error setting profile %u and level %u: %s (0x%08x)",
413 (guint) param.eProfile, (guint) param.eLevel,
414 gst_omx_error_to_string (err), err);
421 GST_ERROR_OBJECT (self, "Unsupported profile %s", profile_string);
422 gst_caps_unref (peercaps);
426 GST_ERROR_OBJECT (self, "Unsupported level %s", level_string);
427 gst_caps_unref (peercaps);
432 gst_omx_h264_enc_get_caps (GstOMXVideoEnc * enc, GstOMXPort * port,
433 GstVideoCodecState * state)
435 GstOMXH264Enc *self = GST_OMX_H264_ENC (enc);
438 OMX_VIDEO_PARAM_PROFILELEVELTYPE param;
439 const gchar *profile, *level;
441 caps = gst_caps_new_simple ("video/x-h264",
442 "stream-format", G_TYPE_STRING, "byte-stream",
443 "alignment", G_TYPE_STRING, "au", NULL);
445 GST_OMX_INIT_STRUCT (¶m);
446 param.nPortIndex = GST_OMX_VIDEO_ENC (self)->enc_out_port->index;
449 gst_omx_component_get_parameter (GST_OMX_VIDEO_ENC (self)->enc,
450 OMX_IndexParamVideoProfileLevelCurrent, ¶m);
451 if (err != OMX_ErrorNone && err != OMX_ErrorUnsupportedIndex)
454 if (err == OMX_ErrorNone) {
455 switch (param.eProfile) {
456 case OMX_VIDEO_AVCProfileBaseline:
457 profile = "baseline";
459 case OMX_VIDEO_AVCProfileMain:
462 case OMX_VIDEO_AVCProfileExtended:
463 profile = "extended";
465 case OMX_VIDEO_AVCProfileHigh:
468 case OMX_VIDEO_AVCProfileHigh10:
471 case OMX_VIDEO_AVCProfileHigh422:
472 profile = "high-4:2:2";
474 case OMX_VIDEO_AVCProfileHigh444:
475 profile = "high-4:4:4";
478 g_assert_not_reached ();
482 switch (param.eLevel) {
483 case OMX_VIDEO_AVCLevel1:
486 case OMX_VIDEO_AVCLevel1b:
489 case OMX_VIDEO_AVCLevel11:
492 case OMX_VIDEO_AVCLevel12:
495 case OMX_VIDEO_AVCLevel13:
498 case OMX_VIDEO_AVCLevel2:
501 case OMX_VIDEO_AVCLevel21:
504 case OMX_VIDEO_AVCLevel22:
507 case OMX_VIDEO_AVCLevel3:
510 case OMX_VIDEO_AVCLevel31:
513 case OMX_VIDEO_AVCLevel32:
516 case OMX_VIDEO_AVCLevel4:
519 case OMX_VIDEO_AVCLevel41:
522 case OMX_VIDEO_AVCLevel42:
525 case OMX_VIDEO_AVCLevel5:
528 case OMX_VIDEO_AVCLevel51:
532 g_assert_not_reached ();
535 gst_caps_set_simple (caps,
536 "profile", G_TYPE_STRING, profile, "level", G_TYPE_STRING, level, NULL);
543 gst_omx_h264_enc_handle_output_frame (GstOMXVideoEnc * enc, GstOMXPort * port,
544 GstOMXBuffer * buf, GstVideoCodecFrame * frame)
546 GstOMXH264Enc *self = GST_OMX_H264_ENC (enc);
548 if (buf->omx_buf->nFlags & OMX_BUFFERFLAG_CODECCONFIG) {
549 /* The codec data is SPS/PPS with a startcode => bytestream stream format
550 * For bytestream stream format the SPS/PPS is only in-stream and not
553 if (buf->omx_buf->nFilledLen >= 4 &&
554 GST_READ_UINT32_BE (buf->omx_buf->pBuffer +
555 buf->omx_buf->nOffset) == 0x00000001) {
557 GstMapInfo map = GST_MAP_INFO_INIT;
559 GST_DEBUG_OBJECT (self, "got codecconfig in byte-stream format");
561 hdrs = gst_buffer_new_and_alloc (buf->omx_buf->nFilledLen);
563 gst_buffer_map (hdrs, &map, GST_MAP_WRITE);
565 buf->omx_buf->pBuffer + buf->omx_buf->nOffset,
566 buf->omx_buf->nFilledLen);
567 gst_buffer_unmap (hdrs, &map);
568 self->headers = g_list_append (self->headers, hdrs);
571 gst_video_codec_frame_unref (frame);
575 } else if (self->headers) {
576 gst_video_encoder_set_headers (GST_VIDEO_ENCODER (self), self->headers);
577 self->headers = NULL;
581 GST_OMX_VIDEO_ENC_CLASS
582 (gst_omx_h264_enc_parent_class)->handle_output_frame (enc, port, buf,