diff options
Diffstat (limited to 'omx/gstomxh264enc.c')
-rw-r--r-- | omx/gstomxh264enc.c | 406 |
1 files changed, 335 insertions, 71 deletions
diff --git a/omx/gstomxh264enc.c b/omx/gstomxh264enc.c index 109e173..4af175a 100644 --- a/omx/gstomxh264enc.c +++ b/omx/gstomxh264enc.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2011, Hewlett-Packard Development Company, L.P. * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd. + * Copyright (c) 2013-2015, NVIDIA CORPORATION. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -36,10 +37,15 @@ static GstCaps *gst_omx_h264_enc_get_caps (GstOMXVideoEnc * enc, GstOMXPort * port, GstVideoCodecState * state); static GstFlowReturn gst_omx_h264_enc_handle_output_frame (GstOMXVideoEnc * self, GstOMXPort * port, GstOMXBuffer * buf, GstVideoCodecFrame * frame); +static void gst_omx_h264_enc_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_omx_h264_enc_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec); enum { - PROP_0 + PROP_0, + PROP_INSERT_SPS_PPS }; /* class initialization */ @@ -56,12 +62,16 @@ gst_omx_h264_enc_class_init (GstOMXH264EncClass * klass) { GstElementClass *element_class = GST_ELEMENT_CLASS (klass); GstOMXVideoEncClass *videoenc_class = GST_OMX_VIDEO_ENC_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); videoenc_class->set_format = GST_DEBUG_FUNCPTR (gst_omx_h264_enc_set_format); videoenc_class->get_caps = GST_DEBUG_FUNCPTR (gst_omx_h264_enc_get_caps); + gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_omx_h264_enc_set_property); + gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_omx_h264_enc_get_property); videoenc_class->cdata.default_src_template_caps = "video/x-h264, " - "width=(int) [ 16, 4096 ], " "height=(int) [ 16, 4096 ]"; + "width=(int) [ 16, 4096 ], " "height=(int) [ 16, 4096 ], " + "stream-format=(string) { byte-stream, avc }, " "alignment=(string) au "; videoenc_class->handle_output_frame = GST_DEBUG_FUNCPTR (gst_omx_h264_enc_handle_output_frame); @@ -72,13 +82,81 @@ gst_omx_h264_enc_class_init (GstOMXH264EncClass * klass) "Sebastian Dröge <sebastian.droege@collabora.co.uk>"); gst_omx_set_default_role (&videoenc_class->cdata, "video_encoder.avc"); + + g_object_class_install_property (gobject_class, PROP_INSERT_SPS_PPS, + g_param_spec_boolean ("insert-sps-pps", + "Insert H.264 SPS, PPS", + "Insert H.264 SPS, PPS at every IDR frame", + FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } static void gst_omx_h264_enc_init (GstOMXH264Enc * self) { + self->insert_sps_pps = FALSE; +} + +static OMX_ERRORTYPE +ifi_setup (GstOMXVideoEnc * enc) +{ + GstOMXH264Enc *self = GST_OMX_H264_ENC (enc); + OMX_ERRORTYPE err = OMX_ErrorNone; + + if (enc->iframeinterval != 0xffffffff) { + OMX_VIDEO_CONFIG_AVCINTRAPERIOD oIntraPeriod; + GST_OMX_INIT_STRUCT (&oIntraPeriod); + oIntraPeriod.nPortIndex = enc->enc_out_port->index; + + err = + gst_omx_component_get_config (GST_OMX_VIDEO_ENC (self)->enc, + OMX_IndexConfigVideoAVCIntraPeriod, + &oIntraPeriod); + + if (err == OMX_ErrorNone) { + if (enc->iframeinterval != 0) { + oIntraPeriod.nIDRPeriod = enc->iframeinterval; + oIntraPeriod.nPFrames = enc->iframeinterval - 1; + } + + err = + gst_omx_component_set_config (GST_OMX_VIDEO_ENC (self)->enc, + OMX_IndexConfigVideoAVCIntraPeriod, + &oIntraPeriod); + } + } + return err; } +static OMX_ERRORTYPE +gst_omx_h264_enc_set_insert_sps_pps (GstOMXVideoEnc * enc) +{ + OMX_INDEXTYPE eIndex; + OMX_ERRORTYPE eError = OMX_ErrorNone; + NVX_PARAM_VIDENCPROPERTY oEncodeProp; + GstOMXH264Enc *self = GST_OMX_H264_ENC (enc); + + if (self->insert_sps_pps) { + GST_OMX_INIT_STRUCT (&oEncodeProp); + oEncodeProp.nPortIndex = enc->enc_out_port->index; + + eError = gst_omx_component_get_index (GST_OMX_VIDEO_ENC (self)->enc, + (gpointer) NVX_INDEX_PARAM_VIDEO_ENCODE_PROPERTY, &eIndex); + + if (eError == OMX_ErrorNone) { + eError = + gst_omx_component_get_parameter (GST_OMX_VIDEO_ENC (self)->enc, + eIndex, &oEncodeProp); + if (eError == OMX_ErrorNone) { + oEncodeProp.bInsertSPSPPSAtIDR = self->insert_sps_pps; + + eError = + gst_omx_component_set_parameter (GST_OMX_VIDEO_ENC (self)->enc, eIndex, &oEncodeProp); + } + } + } + return eError; + } + static gboolean gst_omx_h264_enc_set_format (GstOMXVideoEnc * enc, GstOMXPort * port, GstVideoCodecState * state) @@ -87,8 +165,10 @@ gst_omx_h264_enc_set_format (GstOMXVideoEnc * enc, GstOMXPort * port, GstCaps *peercaps; OMX_PARAM_PORTDEFINITIONTYPE port_def; OMX_VIDEO_PARAM_PROFILELEVELTYPE param; + OMX_VIDEO_CONFIG_NALSIZE OMXNalSize; OMX_ERRORTYPE err; const gchar *profile_string, *level_string; + const gchar *out_format = NULL; gst_omx_port_get_port_definition (GST_OMX_VIDEO_ENC (self)->enc_out_port, &port_def); @@ -108,7 +188,6 @@ gst_omx_h264_enc_set_format (GstOMXVideoEnc * enc, GstOMXPort * port, if (err != OMX_ErrorNone) { GST_WARNING_OBJECT (self, "Setting profile/level not supported by component"); - return TRUE; } peercaps = gst_pad_peer_query_caps (GST_VIDEO_ENCODER_SRC_PAD (enc), @@ -123,79 +202,131 @@ gst_omx_h264_enc_set_format (GstOMXVideoEnc * enc, GstOMXPort * port, } s = gst_caps_get_structure (peercaps, 0); - profile_string = gst_structure_get_string (s, "profile"); - if (profile_string) { - if (g_str_equal (profile_string, "baseline")) { - param.eProfile = OMX_VIDEO_AVCProfileBaseline; - } else if (g_str_equal (profile_string, "main")) { - param.eProfile = OMX_VIDEO_AVCProfileMain; - } else if (g_str_equal (profile_string, "extended")) { - param.eProfile = OMX_VIDEO_AVCProfileExtended; - } else if (g_str_equal (profile_string, "high")) { - param.eProfile = OMX_VIDEO_AVCProfileHigh; - } else if (g_str_equal (profile_string, "high-10")) { - param.eProfile = OMX_VIDEO_AVCProfileHigh10; - } else if (g_str_equal (profile_string, "high-4:2:2")) { - param.eProfile = OMX_VIDEO_AVCProfileHigh422; - } else if (g_str_equal (profile_string, "high-4:4:4")) { - param.eProfile = OMX_VIDEO_AVCProfileHigh444; - } else { - goto unsupported_profile; + + if (err == OMX_ErrorNone) { + profile_string = gst_structure_get_string (s, "profile"); + if (profile_string) { + if (g_str_equal (profile_string, "baseline")) { + param.eProfile = OMX_VIDEO_AVCProfileBaseline; + } else if (g_str_equal (profile_string, "main")) { + param.eProfile = OMX_VIDEO_AVCProfileMain; + } else if (g_str_equal (profile_string, "extended")) { + param.eProfile = OMX_VIDEO_AVCProfileExtended; + } else if (g_str_equal (profile_string, "high")) { + param.eProfile = OMX_VIDEO_AVCProfileHigh; + } else if (g_str_equal (profile_string, "high-10")) { + param.eProfile = OMX_VIDEO_AVCProfileHigh10; + } else if (g_str_equal (profile_string, "high-4:2:2")) { + param.eProfile = OMX_VIDEO_AVCProfileHigh422; + } else if (g_str_equal (profile_string, "high-4:4:4")) { + param.eProfile = OMX_VIDEO_AVCProfileHigh444; + } else { + goto unsupported_profile; + } + } + level_string = gst_structure_get_string (s, "level"); + if (level_string) { + if (g_str_equal (level_string, "1")) { + param.eLevel = OMX_VIDEO_AVCLevel1; + } else if (g_str_equal (level_string, "1b")) { + param.eLevel = OMX_VIDEO_AVCLevel1b; + } else if (g_str_equal (level_string, "1.1")) { + param.eLevel = OMX_VIDEO_AVCLevel11; + } else if (g_str_equal (level_string, "1.2")) { + param.eLevel = OMX_VIDEO_AVCLevel12; + } else if (g_str_equal (level_string, "1.3")) { + param.eLevel = OMX_VIDEO_AVCLevel13; + } else if (g_str_equal (level_string, "2")) { + param.eLevel = OMX_VIDEO_AVCLevel2; + } else if (g_str_equal (level_string, "2.1")) { + param.eLevel = OMX_VIDEO_AVCLevel21; + } else if (g_str_equal (level_string, "2.2")) { + param.eLevel = OMX_VIDEO_AVCLevel22; + } else if (g_str_equal (level_string, "3")) { + param.eLevel = OMX_VIDEO_AVCLevel3; + } else if (g_str_equal (level_string, "3.1")) { + param.eLevel = OMX_VIDEO_AVCLevel31; + } else if (g_str_equal (level_string, "3.2")) { + param.eLevel = OMX_VIDEO_AVCLevel32; + } else if (g_str_equal (level_string, "4")) { + param.eLevel = OMX_VIDEO_AVCLevel4; + } else if (g_str_equal (level_string, "4.1")) { + param.eLevel = OMX_VIDEO_AVCLevel41; + } else if (g_str_equal (level_string, "4.2")) { + param.eLevel = OMX_VIDEO_AVCLevel42; + } else if (g_str_equal (level_string, "5")) { + param.eLevel = OMX_VIDEO_AVCLevel5; + } else if (g_str_equal (level_string, "5.1")) { + param.eLevel = OMX_VIDEO_AVCLevel51; + } else { + goto unsupported_level; + } + } + + err = + gst_omx_component_set_parameter (GST_OMX_VIDEO_ENC (self)->enc, + OMX_IndexParamVideoProfileLevelCurrent, ¶m); + if (err == OMX_ErrorUnsupportedIndex) { + GST_WARNING_OBJECT (self, + "Setting profile/level not supported by component"); + } else if (err != OMX_ErrorNone) { + GST_ERROR_OBJECT (self, + "Error setting profile %u and level %u: %s (0x%08x)", + (guint) param.eProfile, (guint) param.eLevel, + gst_omx_error_to_string (err), err); + return FALSE; } } - level_string = gst_structure_get_string (s, "level"); - if (level_string) { - if (g_str_equal (level_string, "1")) { - param.eLevel = OMX_VIDEO_AVCLevel1; - } else if (g_str_equal (level_string, "1b")) { - param.eLevel = OMX_VIDEO_AVCLevel1b; - } else if (g_str_equal (level_string, "1.1")) { - param.eLevel = OMX_VIDEO_AVCLevel11; - } else if (g_str_equal (level_string, "1.2")) { - param.eLevel = OMX_VIDEO_AVCLevel12; - } else if (g_str_equal (level_string, "1.3")) { - param.eLevel = OMX_VIDEO_AVCLevel13; - } else if (g_str_equal (level_string, "2")) { - param.eLevel = OMX_VIDEO_AVCLevel2; - } else if (g_str_equal (level_string, "2.1")) { - param.eLevel = OMX_VIDEO_AVCLevel21; - } else if (g_str_equal (level_string, "2.2")) { - param.eLevel = OMX_VIDEO_AVCLevel22; - } else if (g_str_equal (level_string, "3")) { - param.eLevel = OMX_VIDEO_AVCLevel3; - } else if (g_str_equal (level_string, "3.1")) { - param.eLevel = OMX_VIDEO_AVCLevel31; - } else if (g_str_equal (level_string, "3.2")) { - param.eLevel = OMX_VIDEO_AVCLevel32; - } else if (g_str_equal (level_string, "4")) { - param.eLevel = OMX_VIDEO_AVCLevel4; - } else if (g_str_equal (level_string, "4.1")) { - param.eLevel = OMX_VIDEO_AVCLevel41; - } else if (g_str_equal (level_string, "4.2")) { - param.eLevel = OMX_VIDEO_AVCLevel42; - } else if (g_str_equal (level_string, "5")) { - param.eLevel = OMX_VIDEO_AVCLevel5; - } else if (g_str_equal (level_string, "5.1")) { - param.eLevel = OMX_VIDEO_AVCLevel51; + + out_format = gst_structure_get_string (s, "stream-format"); + if (out_format) { + if (g_str_equal (out_format, "avc")) { + self->stream_format = H264_AVC; + } else if (g_str_equal (out_format, "byte-stream")) { + self->stream_format = H264_BTS; } else { - goto unsupported_level; + goto unsupported_out_format; } } gst_caps_unref (peercaps); } - err = - gst_omx_component_set_parameter (GST_OMX_VIDEO_ENC (self)->enc, - OMX_IndexParamVideoProfileLevelCurrent, ¶m); - if (err == OMX_ErrorUnsupportedIndex) { - GST_WARNING_OBJECT (self, - "Setting profile/level not supported by component"); - } else if (err != OMX_ErrorNone) { - GST_ERROR_OBJECT (self, - "Error setting profile %u and level %u: %s (0x%08x)", - (guint) param.eProfile, (guint) param.eLevel, - gst_omx_error_to_string (err), err); - return FALSE; + if (self->stream_format == H264_AVC) { + GST_OMX_INIT_STRUCT (&OMXNalSize); + err = + gst_omx_component_get_config (GST_OMX_VIDEO_ENC (self)->enc, + OMX_IndexConfigVideoNalSize, &OMXNalSize); + if (err == OMX_ErrorNone) { + OMXNalSize.nNaluBytes = 4; + OMXNalSize.nPortIndex = GST_OMX_VIDEO_ENC (self)->enc_out_port->index; + err = + gst_omx_component_set_config (GST_OMX_VIDEO_ENC (self)->enc, + OMX_IndexConfigVideoNalSize, &OMXNalSize); + if (err != OMX_ErrorNone) { + return FALSE; + } + } + } + + if (enc->iframeinterval != 0xffffffff) { + err = ifi_setup (enc); + if (err != OMX_ErrorNone) { + GST_WARNING_OBJECT (self, + "Error setting iframeinterval %u : %s (0x%08x)", + (guint) enc->iframeinterval, + gst_omx_error_to_string (err), err); + return FALSE; + } + } + + if (self->insert_sps_pps) { + err = gst_omx_h264_enc_set_insert_sps_pps (enc); + if (err != OMX_ErrorNone) { + GST_WARNING_OBJECT (self, + "Error setting insert sps pps: %s (0x%08x)", + gst_omx_error_to_string (err), err); + return FALSE; + } } return TRUE; @@ -209,6 +340,11 @@ unsupported_level: GST_ERROR_OBJECT (self, "Unsupported level %s", level_string); gst_caps_unref (peercaps); return FALSE; + +unsupported_out_format: + GST_ERROR_OBJECT (self, "Unsupported stream-format %s", out_format); + gst_caps_unref (peercaps); + return FALSE; } static GstCaps * @@ -219,10 +355,9 @@ gst_omx_h264_enc_get_caps (GstOMXVideoEnc * enc, GstOMXPort * port, GstCaps *caps; OMX_ERRORTYPE err; OMX_VIDEO_PARAM_PROFILELEVELTYPE param; - const gchar *profile, *level; + const gchar *profile, *level, *out_format; caps = gst_caps_new_simple ("video/x-h264", - "stream-format", G_TYPE_STRING, "byte-stream", "alignment", G_TYPE_STRING, "au", NULL); GST_OMX_INIT_STRUCT (¶m); @@ -231,7 +366,8 @@ gst_omx_h264_enc_get_caps (GstOMXVideoEnc * enc, GstOMXPort * port, err = gst_omx_component_get_parameter (GST_OMX_VIDEO_ENC (self)->enc, OMX_IndexParamVideoProfileLevelCurrent, ¶m); - if (err != OMX_ErrorNone && err != OMX_ErrorUnsupportedIndex) + if (err != OMX_ErrorNone && err != OMX_ErrorUnsupportedIndex + && err != OMX_ErrorNotImplemented) return NULL; if (err == OMX_ErrorNone) { @@ -315,10 +451,25 @@ gst_omx_h264_enc_get_caps (GstOMXVideoEnc * enc, GstOMXPort * port, g_assert_not_reached (); return NULL; } + gst_caps_set_simple (caps, "profile", G_TYPE_STRING, profile, "level", G_TYPE_STRING, level, NULL); } + switch (self->stream_format) { + case H264_AVC: + out_format = "avc"; + break; + case H264_BTS: + out_format = "byte-stream"; + break; + default: + g_assert_not_reached (); + return NULL; + } + + gst_caps_set_simple (caps, "stream-format", G_TYPE_STRING, out_format, NULL); + return caps; } @@ -326,6 +477,8 @@ static GstFlowReturn gst_omx_h264_enc_handle_output_frame (GstOMXVideoEnc * self, GstOMXPort * port, GstOMXBuffer * buf, GstVideoCodecFrame * frame) { + GstOMXH264Enc *h264enc = GST_OMX_H264_ENC (self); + if (buf->omx_buf->nFlags & OMX_BUFFERFLAG_CODECCONFIG) { /* The codec data is SPS/PPS with a startcode => bytestream stream format * For bytestream stream format the SPS/PPS is only in-stream and not @@ -351,6 +504,85 @@ gst_omx_h264_enc_handle_output_frame (GstOMXVideoEnc * self, GstOMXPort * port, l = g_list_append (l, hdrs); gst_video_encoder_set_headers (GST_VIDEO_ENCODER (self), l); } +#ifdef USE_OMX_TARGET_TEGRA + else { + if (h264enc->stream_format == H264_AVC) { + gsize inbuf_size; + GstBuffer *codec_data_buf; + GstMapInfo cdmap = GST_MAP_INFO_INIT; + OMX_U32 length_to_allocate, offset; + OMX_U8 *str_ptr; + OMX_U8 *pts_ptr; + OMX_U8 data[6]; + OMX_U32 *codec_data; + OMX_U32 sps_length; + + GST_DEBUG_OBJECT (self, "create codec_data caps buffer"); + + str_ptr = (buf->omx_buf->pBuffer + buf->omx_buf->nOffset); + inbuf_size = buf->omx_buf->nFilledLen; + + codec_data = + (OMX_U32 *) (buf->omx_buf->pBuffer + buf->omx_buf->nOffset); + sps_length = (codec_data[0] >> 24); + + // Need additional 7 bytes of data to indicate no of sps,pps + //and how many bytes indicate nal_length,profile etc also we + //are going to strip down 2 bytes each for the sps and pps + //length hence reducing the size by 4 + + length_to_allocate = (inbuf_size + ADDITIONAL_LENGTH); + + codec_data_buf = gst_buffer_new_and_alloc (length_to_allocate); + gst_buffer_map (codec_data_buf, &cdmap, GST_MAP_WRITE); + + data[0] = 0x01; + data[1] = 0x42; // 66 Baseline profile + data[2] = 0x40; // constrained Baseline + data[3] = 0x15; // level2.1 + data[4] = 0x03; // nal length = 4 bytes + data[5] = 0x01; // 1SPS + + // append data of 6 bytes and then the sps length and sps data + memcpy (cdmap.data, data, HEADER_DATA_LENGTH); + + // Strip down the length indicating the NAL data size from 4 bytes to 2 bytes. + // This is because the down stream qtmux are expecting this to be only 2 bytes + + memcpy ((cdmap.data + HEADER_DATA_LENGTH), (str_ptr + 2), + DOWNSTREAM_NAL_LENGTH_INDICATOR); + + // Now copy the SPS data + memcpy ((cdmap.data + HEADER_DATA_LENGTH + 2), + (str_ptr + ENCODER_NAL_LENGTH_INDICATOR), sps_length); + + offset = + HEADER_DATA_LENGTH + DOWNSTREAM_NAL_LENGTH_INDICATOR + sps_length; + + pts_ptr = cdmap.data + offset; + pts_ptr[0] = 1; // 1 pps + + offset += 1; + + // Strip down the length indicating the NAL data size from 4 bytes to 2 bytes. + // This is because the down stream qtmux are expecting this to be only 2 bytes + memcpy ((cdmap.data + offset), + (str_ptr + (ENCODER_NAL_LENGTH_INDICATOR + sps_length) + 2), + DOWNSTREAM_NAL_LENGTH_INDICATOR); + + offset += 2; + + // Now copy pps data + memcpy ((cdmap.data + offset), + (str_ptr + ((ENCODER_NAL_LENGTH_INDICATOR << 1) + sps_length)), + (inbuf_size - ((ENCODER_NAL_LENGTH_INDICATOR << 1) + sps_length))); + + gst_buffer_unmap (codec_data_buf, &cdmap); + + self->codec_data = codec_data_buf; + } + } +#endif } return @@ -358,3 +590,35 @@ gst_omx_h264_enc_handle_output_frame (GstOMXVideoEnc * self, GstOMXPort * port, (gst_omx_h264_enc_parent_class)->handle_output_frame (self, port, buf, frame); } + +static void +gst_omx_h264_enc_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstOMXH264Enc *self = GST_OMX_H264_ENC (object); + + switch (prop_id) { + case PROP_INSERT_SPS_PPS: + self->insert_sps_pps = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_omx_h264_enc_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstOMXH264Enc *self = GST_OMX_H264_ENC (object); + + switch (prop_id) { + case PROP_INSERT_SPS_PPS: + g_value_set_boolean (value, self->insert_sps_pps); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} |