+/*
+ * description : convert byte-stream format to packetized frame
+ * params : @self : GstOmxH264Enc, @buf: byte-stream buf, @sync: notify this buf is sync frame
+ * return : none
+ * comments :
+ */
+static void
+convert_to_packetized_frame (GstOmxH264Enc *self, GstBuffer **buf)
+{
+ unsigned char *data = GST_BUFFER_DATA (*buf);
+ unsigned int size = GST_BUFFER_SIZE(*buf);
+ int idx = 0;
+ gint start_idx = -1;
+ unsigned char *nalu_start = NULL;
+ GstOmxBaseFilter *omx_base = GST_OMX_BASE_FILTER(self);
+
+ GST_LOG_OBJECT (self, "convert_to_packtized format. size=%d sliceMode=%d",
+ GST_BUFFER_SIZE(*buf), self->slice_fmo.eSliceMode);
+
+ if (omx_base->gomx->component_vendor == GOMX_VENDOR_SLSI &&
+ self->slice_fmo.eSliceMode == OMX_VIDEO_SLICEMODE_AVCDefault) { /* 1 slice per frame */
+ GST_LOG_OBJECT (self, " handle single NALU per buffer");
+ while (idx < size - GSTOMX_H264_NAL_START_LEN) {
+ if (((data[idx] == 0x00) && (data[idx+1] == 0x00) && (data[idx+2] == 0x00)&& (data[idx+3] == 0x01)) ||
+ ((data[idx] == 0x00) && (data[idx+1] == 0x00) && (data[idx+2] == 0x01))) {
+ if (data[idx+2] == 0x01) {
+ GST_ERROR_OBJECT (self, "ERROR : NALU header is 3-bytes, yet to support !!");
+ } else {
+ GSTOMX_H264_WB32(data + idx, size - idx - GSTOMX_H264_NAL_START_LEN);
+ return;
+ }
+ }
+ idx++;
+ } /* while */
+ } else { /* handle multiple NALUs in one buffer */
+ GST_LOG_OBJECT (self, " handle multiple NALUs per buffer");
+ while (idx < size - GSTOMX_H264_NAL_START_LEN) {
+ if (((data[idx] == 0x00) && (data[idx+1] == 0x00) && (data[idx+2] == 0x00)&& (data[idx+3] == 0x01)) ||
+ ((data[idx] == 0x00) && (data[idx+1] == 0x00) && (data[idx+2] == 0x01))) {
+ if (data[idx+2] == 0x01) {
+ GST_ERROR_OBJECT (self, "ERROR : NALU header is 3-bytes, yet to support !!");
+ } else {
+ if (start_idx >= 0) {
+ if (idx <= start_idx) {
+ GST_ERROR_OBJECT (self, "ERROR : idx <= start_idx !!");
+ return;
+ }
+ GST_LOG_OBJECT (self, "size of current nal unit = %d", idx-start_idx);
+ GSTOMX_H264_WB32(nalu_start, idx - start_idx - GSTOMX_H264_NAL_START_LEN);
+ }
+ start_idx = idx;
+ nalu_start = data + idx;
+ idx++;
+ }
+ }
+ idx++;
+ } /* while */
+ idx += GSTOMX_H264_NAL_START_LEN;
+
+ /* converting last nal unit */
+ if (start_idx >= 0) {
+ GST_LOG_OBJECT (self, "size of current nal unit = %d", idx-start_idx);
+ GSTOMX_H264_WB32(nalu_start, idx - start_idx - GSTOMX_H264_NAL_START_LEN);
+ }
+ }
+}
+
+/*
+ * description : convert byte-stream codec data to packtized codec_data
+ * params : @self : GstOmxH264Enc, @inbuf: byte-stream codec data (omx buf), @outbuf: packetized codec_data
+ * return : true on successes / false on failure
+ * comments :
+ */
+static gboolean
+convert_to_packetized_dci (GstOmxH264Enc *self, unsigned char *nalu_dci, unsigned nalu_dci_len,
+ GstBuffer **packetized_dci, gint *out_sps_cnt, gint *out_pps_cnt)
+{
+ gint idx = 0;
+ gint sps_cnt = 0;
+ gint pps_cnt = 0;
+ GQueue *sps_queue = 0;
+ GQueue *pps_queue = 0;
+ unsigned char *packet_dci = NULL;
+ gint prev_nalu_start = 0;
+ gint prev_nalu_type = GSTOMX_H264_NUT_UNKNOWN;
+ gint sps_size = 0;
+ gint pps_size = 0;
+ GstBuffer *sps_data = NULL;
+ GstBuffer *pps_data = NULL;
+ GstBuffer *queue_data = NULL;
+ gint nal_type = GSTOMX_H264_NUT_UNKNOWN;
+ unsigned char profile = 0;
+ unsigned char level = 0;
+ unsigned char profile_comp = 0;
+ gboolean bret = TRUE;
+ gboolean single_sps_pps = FALSE; /* if there is only 1 sps, pps set, */
+
+ sps_queue = g_queue_new ();
+ pps_queue = g_queue_new ();
+
+ /* find no.of SPS & PPS units */
+ while (idx < nalu_dci_len) {
+ if ((nalu_dci[idx] == 0x00) && (nalu_dci[idx+1] == 0x00) && (nalu_dci[idx+2] == 0x01)) {
+ /* copy previous nal unit */
+ if (prev_nalu_start && prev_nalu_type == GSTOMX_H264_NUT_SPS) {
+ if (nalu_dci[idx -1] == 0x00) {
+ sps_size = idx -1 - prev_nalu_start;
+ } else {
+ sps_size = idx - prev_nalu_start;
+ }
+ sps_data = gst_buffer_new_and_alloc (sps_size + GSTOMX_H264_C_DCI_LEN);
+ if (!sps_data) {
+ GST_ERROR_OBJECT (self, "failed to allocate memory..");
+ bret = FALSE;
+ goto exit;
+ }
+ GSTOMX_H264_WB16(GST_BUFFER_DATA(sps_data), sps_size);
+ memcpy (GST_BUFFER_DATA(sps_data) + GSTOMX_H264_C_DCI_LEN, nalu_dci + prev_nalu_start, sps_size);
+ g_queue_push_tail (sps_queue, sps_data);
+ } else if (prev_nalu_start && prev_nalu_type == GSTOMX_H264_NUT_PPS) {
+ if (nalu_dci[idx -1] == 0x00) {
+ pps_size = idx -1 - prev_nalu_start;
+ } else {
+ pps_size = idx - prev_nalu_start;
+ }
+ pps_data = gst_buffer_new_and_alloc (pps_size + GSTOMX_H264_C_DCI_LEN);
+ if (!pps_data) {
+ GST_ERROR_OBJECT (self, "failed to allocate memory..");
+ bret = FALSE;
+ goto exit;
+ }
+ GSTOMX_H264_WB16(GST_BUFFER_DATA(pps_data), pps_size);
+ memcpy (GST_BUFFER_DATA(pps_data) + GSTOMX_H264_C_DCI_LEN, nalu_dci + prev_nalu_start, pps_size);
+ g_queue_push_tail (pps_queue, pps_data);
+ }
+ /* present nalu type */
+ nal_type = nalu_dci[idx+3] & 0x1f;
+
+ if (nal_type == GSTOMX_H264_NUT_SPS) {
+ sps_cnt++;
+ prev_nalu_start = idx + 3;
+ prev_nalu_type =GSTOMX_H264_NUT_SPS;
+ profile = nalu_dci[idx+4];
+ level = nalu_dci[idx+6];
+ GST_INFO_OBJECT (self, "Profile Number = %d and Level = %d...", nalu_dci[idx+4], level);
+ GST_INFO_OBJECT (self, "Profile is %s", (profile == 66) ? "BaseLine Profile": (profile == 77)? "Main Profile": profile==88 ?
+ "Extended Profile": profile==100 ? "High Profile": "Not Supported codec");
+ } else if ((nalu_dci[idx+3] & 0x1f) == GSTOMX_H264_NUT_PPS) {
+ pps_cnt++;
+ prev_nalu_start = idx + 3;
+ prev_nalu_type = GSTOMX_H264_NUT_PPS;
+ }
+ }
+ idx++;
+ }
+
+ /* copy previous nal unit */
+ if (prev_nalu_start && prev_nalu_type == GSTOMX_H264_NUT_SPS) {
+ sps_size = idx - prev_nalu_start;
+
+ sps_data = gst_buffer_new_and_alloc (sps_size + GSTOMX_H264_C_DCI_LEN);
+ if (!sps_data) {
+ GST_ERROR_OBJECT (self, "failed to allocate memory..");
+ bret = FALSE;
+ goto exit;
+ }
+
+ GSTOMX_H264_WB16(GST_BUFFER_DATA(sps_data), sps_size);
+ memcpy (GST_BUFFER_DATA(sps_data) + GSTOMX_H264_C_DCI_LEN, nalu_dci + prev_nalu_start, sps_size);
+ g_queue_push_tail (sps_queue, sps_data);
+
+ } else if (prev_nalu_start && prev_nalu_type == GSTOMX_H264_NUT_PPS) {
+ pps_size = idx - prev_nalu_start;
+
+ pps_data = gst_buffer_new_and_alloc (pps_size + GSTOMX_H264_C_DCI_LEN);
+ if (!pps_data) {
+ GST_ERROR_OBJECT (self, "failed to allocate memory..");
+ bret = FALSE;
+ goto exit;
+ }
+
+ GSTOMX_H264_WB16(GST_BUFFER_DATA(pps_data), pps_size);
+ memcpy (GST_BUFFER_DATA(pps_data) + GSTOMX_H264_C_DCI_LEN, nalu_dci + prev_nalu_start, pps_size);
+ g_queue_push_tail (pps_queue, pps_data);
+ }
+
+ /* make packetized codec data */
+ if (sps_cnt == 1 && pps_cnt == 1) {
+ single_sps_pps = TRUE;
+ *packetized_dci = gst_buffer_new_and_alloc(GSTOMX_H264_MDATA + GSTOMX_H264_C_DCI_LEN + sps_size + GSTOMX_H264_CNT_LEN + GSTOMX_H264_C_DCI_LEN + pps_size);
+ GST_LOG_OBJECT(self, "allocate packetized_dci in case of single sps, pps. (size=%d)", GST_BUFFER_SIZE(*packetized_dci));
+ } else {
+ *packetized_dci = gst_buffer_new_and_alloc(GSTOMX_H264_MDATA);
+ GST_LOG_OBJECT(self, "allocate packetized_dci in case of multi sps, pps");
+ }
+
+ if (*packetized_dci == NULL) {
+ GST_ERROR_OBJECT (self, "Failed to allocate memory..");
+ bret = FALSE;
+ goto exit;
+ }
+
+ packet_dci = GST_BUFFER_DATA(*packetized_dci);
+ packet_dci[0] = 0x01; /* configurationVersion */
+ packet_dci[1] = profile; /* AVCProfileIndication */
+ packet_dci[2] = profile_comp; /* profile_compatibility*/
+ packet_dci[3] = level; /* AVCLevelIndication */
+ packet_dci[4] = 0xff; /* Reserver (6bits.111111) + LengthSizeMinusOne (2bits). lengthoflength = 4 for present */
+ packet_dci[5] = 0xe0 | sps_cnt; /* Reserver (3bits. 111) + numOfSequenceParameterSets (5bits) */
+
+ /* copy SPS sets */
+ while (!g_queue_is_empty (sps_queue)) {
+ sps_data = g_queue_pop_head (sps_queue);
+
+ if (TRUE == single_sps_pps) {
+ memcpy(packet_dci + GSTOMX_H264_MDATA, GST_BUFFER_DATA(sps_data), GST_BUFFER_SIZE(sps_data));
+ gst_buffer_unref(sps_data);
+ sps_data = NULL;
+ } else {
+ *packetized_dci = gst_buffer_join(*packetized_dci, sps_data);
+ }
+ }
+
+ /* add number of PPS sets (1byte)*/
+ if (TRUE == single_sps_pps) {
+ packet_dci[GSTOMX_H264_MDATA + GSTOMX_H264_C_DCI_LEN + sps_size] = pps_cnt;
+ } else {
+ GstBuffer *next_data = gst_buffer_new_and_alloc(GSTOMX_H264_CNT_LEN);
+ if (!next_data) {
+ GST_ERROR_OBJECT (self, "Failed to allocate memory..");
+ bret = FALSE;
+ goto exit;
+ }
+ GST_BUFFER_DATA(next_data)[0] = pps_cnt;
+ *packetized_dci = gst_buffer_join(*packetized_dci, next_data);
+ }
+
+ /* copy PPS sets */
+ while (!g_queue_is_empty (pps_queue)) {
+ pps_data = g_queue_pop_head (pps_queue);
+
+ if (TRUE == single_sps_pps) {
+ memcpy(packet_dci + GSTOMX_H264_MDATA + GSTOMX_H264_C_DCI_LEN + sps_size + GSTOMX_H264_CNT_LEN,
+ GST_BUFFER_DATA(pps_data), GST_BUFFER_SIZE(pps_data));
+ gst_buffer_unref(pps_data);
+ pps_data = NULL;
+ } else {
+ *packetized_dci = gst_buffer_join(*packetized_dci, pps_data);
+ }
+ }
+
+exit:
+ while (!g_queue_is_empty (sps_queue)) {
+ queue_data = g_queue_pop_head (sps_queue);
+ gst_buffer_unref (queue_data);
+ queue_data = NULL;
+ }
+ g_queue_free (sps_queue);
+
+ while (!g_queue_is_empty (pps_queue)) {
+ queue_data = g_queue_pop_head (pps_queue);
+ gst_buffer_unref (queue_data);
+ queue_data = NULL;
+ }
+ g_queue_free (pps_queue);
+
+ *out_sps_cnt = sps_cnt;
+ *out_pps_cnt = sps_cnt;
+
+ return bret;
+}
+
+/*
+ * description : resotre packtized dci (codec_data)
+ * params : @self : GstOmxH264Enc, @inbuf: codec data, @outbuf: h264enc->dci
+ * return : none
+ * comments :
+ * from original packetized codec_data: METADATA(6) + SPS_CNT(0) + {SPS_SIZE(2)+SPS_DATA(sps_size)}*n +
+ PPS_CNT(1) + {PPS_SIZE(2)+PPS_DATA(pps_size)}*n
+ * to estore packetized dci: {SPS_SIZE(4)+SPS_DATA(sps_size)}*n + {PPS_SIZE(4)+PPS_DATA(pps_size)}*n
+ */
+static gboolean
+restore_packetized_dci (GstOmxH264Enc *self, GstBuffer *inbuf, GstBuffer **outbuf, gint sps_cnt, gint pps_cnt)
+{
+ unsigned char *indata = GST_BUFFER_DATA(inbuf);
+ guint sps_size =0;
+ guint pps_size =0;
+ gint i = 0;
+ GstBuffer *next_data = NULL;
+
+ GST_INFO_OBJECT (self, "restore_packetized_dci. sps_cnt=%d, pps_cnt=%d", sps_cnt, pps_cnt);
+
+ if (sps_cnt == 1 && pps_cnt == 1) {
+ sps_size = GSTOMX_H264_RB16(indata + GSTOMX_H264_MDATA);
+ pps_size = GSTOMX_H264_RB16(indata + GSTOMX_H264_MDATA + GSTOMX_H264_C_DCI_LEN + sps_size + GSTOMX_H264_CNT_LEN);
+
+ *outbuf = gst_buffer_new_and_alloc (GSTOMX_H264_A_DCI_LEN + sps_size + GSTOMX_H264_A_DCI_LEN + pps_size);
+ if (!*outbuf) {
+ GST_ERROR_OBJECT (self, "Failed to allocate memory in gst_buffer_new_and_alloc.");
+ goto error_exit;
+ }
+
+ GSTOMX_H264_WB32(GST_BUFFER_DATA(*outbuf), sps_size);
+ indata += GSTOMX_H264_MDATA + GSTOMX_H264_C_DCI_LEN;
+ memcpy (GST_BUFFER_DATA(*outbuf) + GSTOMX_H264_A_DCI_LEN, indata, sps_size);
+ indata += sps_size;
+
+ GSTOMX_H264_WB32(GST_BUFFER_DATA(*outbuf) + GSTOMX_H264_A_DCI_LEN + sps_size, pps_size);
+ indata += GSTOMX_H264_CNT_LEN + GSTOMX_H264_C_DCI_LEN;
+ memcpy (GST_BUFFER_DATA(*outbuf) + GSTOMX_H264_A_DCI_LEN + sps_size + GSTOMX_H264_A_DCI_LEN, indata, pps_size);
+ indata += pps_size;
+ } else {
+ /* in this case, dci has multi nalu. ex) sps + sps + sps + pps + pps */
+ indata += GSTOMX_H264_MDATA;
+ *outbuf = gst_buffer_new ();
+
+ for (i = 0; i < sps_cnt; i++) {
+ sps_size = GSTOMX_H264_RB16(indata); /* metadata(6) */
+
+ next_data = gst_buffer_new_and_alloc(GSTOMX_H264_A_DCI_LEN + sps_size);
+ if (!next_data) {
+ GST_ERROR_OBJECT (self, "Failed to allocate memory in gst_buffer_new_and_alloc.");
+ goto error_exit;
+ }
+ GSTOMX_H264_WB32(GST_BUFFER_DATA(next_data), sps_size);
+ indata += GSTOMX_H264_C_DCI_LEN; /* sps size field (2 byte) */
+ memcpy (GST_BUFFER_DATA(next_data) + GSTOMX_H264_A_DCI_LEN, indata, sps_size);
+
+ *outbuf = gst_buffer_join(*outbuf, next_data);
+ indata += sps_size;
+ }
+ indata += GSTOMX_H264_CNT_LEN; /* pps cnt field (1 byte) */
+
+ for (i = 0; i < pps_cnt; i++) {
+ pps_size = GSTOMX_H264_RB16(indata);
+
+ next_data = gst_buffer_new_and_alloc(GSTOMX_H264_A_DCI_LEN + pps_size);
+ if (!next_data) {
+ GST_ERROR_OBJECT (self, "Failed to allocate memory in gst_buffer_new_and_alloc.");
+ goto error_exit;
+ }
+ GSTOMX_H264_WB32(GST_BUFFER_DATA(next_data), pps_size);
+ indata += GSTOMX_H264_C_DCI_LEN;
+ memcpy (GST_BUFFER_DATA(next_data) + GSTOMX_H264_A_DCI_LEN, indata, pps_size);
+
+ *outbuf = gst_buffer_join(*outbuf, next_data);
+ indata += pps_size;
+ }
+ }
+
+ return TRUE;
+
+error_exit:
+ if (*outbuf) {
+ gst_buffer_unref(*outbuf);
+ *outbuf = NULL;
+ }
+ return FALSE;
+}
+
+/*
+ * description : set sync frame. if needed, convert output to packetized format, append dci every I frame.
+ * params : @self : GstOmxBaseFilter, @buf: encoder output frame, @omx_buffer: omx_buffer
+ * return : none
+ * comments :
+ */
+static void
+process_output_buf(GstOmxBaseFilter * omx_base, GstBuffer **buf, OMX_BUFFERHEADERTYPE *omx_buffer)
+{
+ GstOmxH264Enc *self;
+ self = GST_OMX_H264ENC (omx_base);
+
+ if (!self->byte_stream) { /* Packtized Format */
+ convert_to_packetized_frame (self, buf); /* convert byte stream to packetized stream */
+ GST_LOG_OBJECT (self, "output buffer is converted to Packtized format.");
+ } else {
+ GST_LOG_OBJECT (self, "output buffer is Byte-stream format.");
+ }
+
+ if ((self->first_frame) ||(self->append_dci && omx_buffer->nFlags & OMX_BUFFERFLAG_SYNCFRAME)) {
+ GstBuffer *newbuf = NULL;
+ GST_LOG_OBJECT (self, "append dci at %s by gst-openmax.", (self->first_frame == TRUE) ? "first frame": "every I frame");
+
+ if (self->dci == NULL) {
+ GST_ERROR_OBJECT (self, "dci is null. can not append dci.");
+ self->append_dci = FALSE;
+ } else {
+ newbuf = gst_buffer_merge (self->dci, *buf);
+ if (newbuf == NULL) {
+ GST_ERROR_OBJECT (self, "Failed to gst_buffer_merge.");
+ return;
+ }
+ gst_buffer_copy_metadata(newbuf, *buf, GST_BUFFER_COPY_ALL);
+ if (*buf)
+ gst_buffer_unref(*buf);
+ *buf = newbuf;
+ }
+ }
+
+ if (self->first_frame == TRUE)
+ self->first_frame = FALSE;
+
+ /* Set sync frame info while encoding */
+ if (omx_buffer->nFlags & OMX_BUFFERFLAG_SYNCFRAME) {
+ GST_BUFFER_FLAG_UNSET(*buf, GST_BUFFER_FLAG_DELTA_UNIT);
+ } else {
+ GST_BUFFER_FLAG_SET(*buf, GST_BUFFER_FLAG_DELTA_UNIT);
+ }
+}
+
+/*
+ * description : if needed, convert byte-stream codec_data to packetized format, save dci.
+ * params : @self : GstOmxBaseFilter, @omx_buffer: omx_buffer
+ * return : none
+ * comments :
+ */
+static void
+process_output_caps(GstOmxBaseFilter * base, OMX_BUFFERHEADERTYPE *omx_buffer)
+{
+ GstOmxH264Enc *h264enc;
+ gboolean ret = FALSE;
+
+ h264enc = GST_OMX_H264ENC (base);
+
+ if (!h264enc->byte_stream) { /* Packtized Format */
+ GstCaps *caps = NULL;
+ GstStructure *structure;
+ GstBuffer *codec_data = NULL;
+ gint sps_cnt = 0;
+ gint pps_cnt = 0;
+ GValue value = { 0, {{0}
+ }
+ };
+ g_value_init (&value, GST_TYPE_BUFFER);
+
+ GST_INFO_OBJECT (h264enc, "Packtized Format: set src caps with codec-data");
+
+ /* convert codec_data to packtized format.. (metadata(cnt) + sps_size + sps + cnt + pps_size + pps) */
+ ret = convert_to_packetized_dci (h264enc, omx_buffer->pBuffer + omx_buffer->nOffset, omx_buffer->nFilledLen,
+ &codec_data, &sps_cnt, &pps_cnt);
+
+ if (FALSE == ret) {
+ GST_ERROR_OBJECT (h264enc, "ERROR: convert to packetized dci");
+ if (codec_data) {
+ gst_buffer_unref(codec_data);
+ codec_data = NULL;
+ }
+ return;
+ }
+
+ /* restore packtized format sps, pps */
+ ret = restore_packetized_dci (h264enc, codec_data, &h264enc->dci, sps_cnt, pps_cnt);
+ if (ret == FALSE) {
+ GST_ERROR_OBJECT (h264enc, "ERROR: restore packetized dci");
+ return;
+ }
+
+ GST_DEBUG_OBJECT (h264enc, "adding codec_data in caps");
+ caps = gst_pad_get_negotiated_caps (base->srcpad);
+ caps = gst_caps_make_writable (caps);
+ structure = gst_caps_get_structure (caps, 0);
+
+ /* packetized format, set codec_data field in caps */
+ gst_value_set_buffer (&value,codec_data);
+ gst_buffer_unref (codec_data);
+ codec_data = NULL;
+ gst_structure_set_value (structure, "codec_data", &value);
+ g_value_unset (&value);
+
+ gst_pad_set_caps (base->srcpad, caps);
+ gst_caps_unref (caps);
+ } else {
+ /* byte-stream format */
+ GST_INFO_OBJECT (h264enc, "Byte-stream Format");
+ h264enc->dci = gst_buffer_new_and_alloc (omx_buffer->nFilledLen);
+ if (!h264enc->dci) {
+ GST_ERROR_OBJECT (h264enc, "failed to allocate memory...");
+ return;
+ }
+ memcpy (GST_BUFFER_DATA (h264enc->dci),
+ omx_buffer->pBuffer + omx_buffer->nOffset, omx_buffer->nFilledLen);
+ }
+}
+
+static void
+finalize (GObject * obj)
+{
+ GstOmxH264Enc *h264enc;
+ h264enc = GST_OMX_H264ENC (obj);
+
+ GST_LOG_OBJECT (h264enc, "h264enc finalize enter");
+
+ if (h264enc->dci) {
+ gst_buffer_unref(h264enc->dci);
+ h264enc->dci = NULL;
+ }
+ G_OBJECT_CLASS (parent_class)->finalize (obj);
+}
+