Tizen 2.1 base
[profile/ivi/gst-openmax0.10.git] / omx / gstomx_h264dec.c
1 /*
2  * Copyright (C) 2007-2009 Nokia Corporation.
3  *
4  * Author: Felipe Contreras <felipe.contreras@nokia.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation
9  * version 2.1 of the License.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #include "gstomx_h264dec.h"
23 #include "gstomx.h"
24
25 GSTOMX_BOILERPLATE (GstOmxH264Dec, gst_omx_h264dec, GstOmxBaseVideoDec,
26     GST_OMX_BASE_VIDEODEC_TYPE);
27
28 /*
29  *  description : find stream format(3gpp or nalu)
30  *  params      : @self : GstOmxH264Dec, @buf: input gstbuffer in pad_chain
31  *  return      : none
32  *  comments    : finding whether the stream format of input buf is 3GPP or Elementary Stream(nalu)
33  */
34 static void
35 check_frame (GstOmxH264Dec *self, GstBuffer * buf)
36 {
37   guint buf_size = GST_BUFFER_SIZE (buf);
38   guint8 *buf_data = GST_BUFFER_DATA (buf);
39   guint nal_type = 0;
40   guint forbidden_zero_bit = 0;
41   guint check_byte= 0;
42
43   if (buf_data == NULL || buf_size < GSTOMX_H264_NAL_START_LEN + 1) {
44     self->h264Format = GSTOMX_H264_FORMAT_UNKNOWN;
45     GST_WARNING_OBJECT(self, "H264 format is unknown");
46     return;
47   }
48
49   self->h264Format = GSTOMX_H264_FORMAT_PACKETIZED;
50
51   if ((buf_data[0] == 0x00)&&(buf_data[1] == 0x00)&&
52     ((buf_data[2] == 0x01)||((buf_data[2] == 0x00)&&(buf_data[3] == 0x01)))) {
53     check_byte = (buf_data[2] == 0x01) ? 3 : 4;
54
55     nal_type = (guint)(buf_data[check_byte] & 0x1f);
56     forbidden_zero_bit = (guint)(buf_data[check_byte] & 0x80);
57     GST_LOG_OBJECT(self, "check first frame: nal_type=%d, forbidden_zero_bit=%d", nal_type, forbidden_zero_bit);
58
59     if (forbidden_zero_bit == 0) {
60       /* check nal_unit_type is invaild value: ex) slice, DPA,DPB... */
61       if ((0 < nal_type && nal_type <= 15) || nal_type == 19 || nal_type == 20) {
62         GST_INFO_OBJECT(self, "H264 format is Byte-stream format");
63         self->h264Format = GSTOMX_H264_FORMAT_BYTE_STREAM;
64       }
65     }
66   }
67
68   if (self->h264Format == GSTOMX_H264_FORMAT_PACKETIZED)
69     GST_INFO_OBJECT(self, "H264 format is Packetized format");
70 }
71
72 /*
73  *  description : convert input 3gpp buffer to nalu based buffer
74  *  params      : @self : GstOmxH264Dec, @buf: buffer to be converted
75  *  return      : none
76  *  comments    : none
77  */
78 static void
79 convert_frame (GstOmxH264Dec *self, GstBuffer **buf)
80 {
81   OMX_U8 frameType;
82   OMX_U32 nalSize = 0;
83   OMX_U32 cumulSize = 0;
84   OMX_U32 offset = 0;
85   OMX_U32 nalHeaderSize = 0;
86   OMX_U32 outSize = 0;
87   OMX_U8 *frame_3gpp = GST_BUFFER_DATA(*buf);
88   OMX_U32 frame_3gpp_size = GST_BUFFER_SIZE(*buf);
89   GstBuffer *nalu_next_buf = NULL;
90   GstBuffer *nalu_buf = NULL;
91
92   do {
93       /* get NAL Length based on length of length*/
94       if (self->h264NalLengthSize == 1) {
95           nalSize = frame_3gpp[0];
96       } else if (self->h264NalLengthSize == 2) {
97           nalSize = GSTOMX_H264_RB16(frame_3gpp);
98       } else {
99           nalSize = GSTOMX_H264_RB32(frame_3gpp);
100       }
101
102       GST_LOG_OBJECT(self, "packetized frame size = %d", nalSize);
103
104       frame_3gpp += self->h264NalLengthSize;
105
106       /* Checking frame type */
107       frameType = *frame_3gpp & 0x1f;
108
109       switch (frameType)
110       {
111           case GSTOMX_H264_NUT_SLICE:
112              GST_LOG_OBJECT(self, "Frame is non-IDR frame...");
113               break;
114           case GSTOMX_H264_NUT_IDR:
115              GST_LOG_OBJECT(self, "Frame is an IDR frame...");
116               break;
117           case GSTOMX_H264_NUT_SEI:
118              GST_LOG_OBJECT(self, "Found SEI Data...");
119               break;
120           case GSTOMX_H264_NUT_SPS:
121              GST_LOG_OBJECT(self, "Found SPS data...");
122               break;
123           case GSTOMX_H264_NUT_PPS:
124              GST_LOG_OBJECT(self, "Found PPS data...");
125               break;
126           case GSTOMX_H264_NUT_EOSEQ:
127              GST_LOG_OBJECT(self, "End of sequence...");
128               break;
129           case GSTOMX_H264_NUT_EOSTREAM:
130              GST_LOG_OBJECT(self, "End of stream...");
131               break;
132           case GSTOMX_H264_NUT_DPA:
133           case GSTOMX_H264_NUT_DPB:
134           case GSTOMX_H264_NUT_DPC:
135           case GSTOMX_H264_NUT_AUD:
136           case GSTOMX_H264_NUT_FILL:
137           case GSTOMX_H264_NUT_MIXED:
138               break;
139           default:
140              GST_INFO_OBJECT(self, "Unknown Frame type: %d. do check_frame one more time with next frame.", frameType);
141              self->h264Format = GSTOMX_H264_FORMAT_UNKNOWN;
142               goto EXIT;
143       }
144
145       /* if nal size is same, we can change only start code */
146       if((nalSize + GSTOMX_H264_NAL_START_LEN) == frame_3gpp_size) {
147           GST_LOG_OBJECT(self, "only change start code");
148           *buf = gst_buffer_make_writable(*buf); /* make writable to support memsrc */
149           GSTOMX_H264_WB32(GST_BUFFER_DATA(*buf), 1);
150           return;
151       }
152
153       /* Convert 3GPP Frame to NALU Frame */
154       offset = outSize;
155       nalHeaderSize = offset ? 3 : 4;
156
157       outSize += nalSize + nalHeaderSize;
158
159       if (nalSize > frame_3gpp_size) {
160           GST_ERROR_OBJECT(self, "out of bounds Error. frame_nalu_size=%d", outSize);
161           goto EXIT;
162       }
163
164       if (nalu_buf) {
165           nalu_next_buf= gst_buffer_new_and_alloc(nalSize + nalHeaderSize);
166           if (nalu_next_buf == NULL) {
167               GST_ERROR_OBJECT(self, "gst_buffer_new_and_alloc failed.(nalu_next_buf)");
168               goto EXIT;
169           }
170       } else {
171           nalu_buf = gst_buffer_new_and_alloc(outSize);
172       }
173
174       if (nalu_buf == NULL) {
175           GST_ERROR_OBJECT(self, "gst_buffer_new_and_alloc failed.(nalu_buf)");
176           goto EXIT;
177       }
178
179       if (!offset) {
180           memcpy(GST_BUFFER_DATA(nalu_buf)+nalHeaderSize, frame_3gpp, nalSize);
181           GSTOMX_H264_WB32(GST_BUFFER_DATA(nalu_buf), 1);
182       } else {
183           if (nalu_next_buf) {
184               GstBuffer *nalu_joined_buf = gst_buffer_join(nalu_buf,nalu_next_buf);
185               nalu_buf = nalu_joined_buf;
186               nalu_next_buf = NULL;
187           }
188           memcpy(GST_BUFFER_DATA(nalu_buf)+nalHeaderSize+offset, frame_3gpp, nalSize);
189           (GST_BUFFER_DATA(nalu_buf)+offset)[0] = (GST_BUFFER_DATA(nalu_buf)+offset)[1] = 0;
190           (GST_BUFFER_DATA(nalu_buf)+offset)[2] = 1;
191       }
192
193       frame_3gpp += nalSize;
194       cumulSize += nalSize + self->h264NalLengthSize;
195       GST_LOG_OBJECT(self, "frame_3gpp_size = %d => frame_nalu_size=%d", frame_3gpp_size, outSize);
196   } while (cumulSize < frame_3gpp_size);
197
198   gst_buffer_copy_metadata(nalu_buf, *buf, GST_BUFFER_COPY_ALL);
199
200   gst_buffer_unref (*buf);
201   *buf = nalu_buf;
202
203   return;
204
205 EXIT:
206   if (nalu_buf) { gst_buffer_unref (nalu_buf); }
207   GST_ERROR_OBJECT(self, "converting frame error.");
208
209   return;
210 }
211
212 /*
213  *  description : convert input 3gpp buffer(codec data) to nalu based buffer
214  *  params      : @self : GstOmxH264Dec, @buf: buffer to be converted, @dci_nalu: converted buffer
215  *  return         : true on successes / false on failure
216  *  comments  : none
217  */
218 static gboolean
219 convert_dci (GstOmxH264Dec *self, GstBuffer *buf, GstBuffer **dci_nalu)
220 {
221   gboolean ret = FALSE;
222   OMX_U8 *out = NULL;
223   OMX_U16 unitSize = 0;
224   OMX_U32 totalSize = 0;
225   OMX_U8 unitNb = 0;
226   OMX_U8 spsDone = 0;
227
228   OMX_U8 *pInputStream = GST_BUFFER_DATA(buf);
229   OMX_U32 pBuffSize = GST_BUFFER_SIZE(buf);
230
231   const OMX_U8 *extraData = (guchar*)pInputStream + 4;
232   static const OMX_U8 naluHeader[GSTOMX_H264_NAL_START_LEN] = {0, 0, 0, 1};
233
234   if (pInputStream != NULL) {
235       /* retrieve Length of Length*/
236       self->h264NalLengthSize = (*extraData++ & 0x03) + 1;
237        GST_INFO("Length Of Length is %d", self->h264NalLengthSize);
238       if (self->h264NalLengthSize == 3)
239       {
240           GST_INFO("LengthOfLength is WRONG...");
241           goto EXIT;
242       }
243      /* retrieve sps and pps unit(s) */
244       unitNb = *extraData++ & 0x1f;
245       GST_INFO("No. of SPS units = %u", unitNb);
246       if (!unitNb) {
247           GST_INFO("SPS is not present...");
248           goto EXIT;
249       }
250
251       while (unitNb--) {
252           /* get SPS/PPS data Length*/
253           unitSize = GSTOMX_H264_RB16(extraData);
254
255           /* Extra 4 bytes for adding delimiter */
256           totalSize += unitSize + GSTOMX_H264_NAL_START_LEN;
257
258           /* Check if SPS/PPS Data Length crossed buffer Length */
259           if ((extraData + 2 + unitSize) > (pInputStream + pBuffSize)) {
260               GST_INFO("SPS length is wrong in DCI...");
261               goto EXIT;
262           }
263
264           if (out)
265             out = g_realloc(out, totalSize);
266           else
267             out = g_malloc(totalSize);
268
269           if (!out) {
270               GST_INFO("realloc failed...");
271               goto EXIT;
272           }
273
274           /* Copy NALU header */
275           memcpy(out + totalSize - unitSize - GSTOMX_H264_NAL_START_LEN,
276               naluHeader, GSTOMX_H264_NAL_START_LEN);
277
278           /* Copy SPS/PPS Length and data */
279           memcpy(out + totalSize - unitSize, extraData + GSTOMX_H264_SPSPPS_LEN, unitSize);
280
281           extraData += (GSTOMX_H264_SPSPPS_LEN + unitSize);
282
283           if (!unitNb && !spsDone++)
284           {
285               /* Completed reading SPS data, now read PPS data */
286               unitNb = *extraData++; /* number of pps unit(s) */
287               GST_INFO( "No. of PPS units = %d", unitNb);
288           }
289       }
290
291       *dci_nalu = gst_buffer_new_and_alloc(totalSize);
292       if (*dci_nalu == NULL) {
293           GST_ERROR_OBJECT(self, " gst_buffer_new_and_alloc failed...\n");
294           goto EXIT;
295       }
296
297       memcpy(GST_BUFFER_DATA(*dci_nalu), out, totalSize);
298       GST_INFO( "Total SPS+PPS size = %d", totalSize);
299   }
300
301   ret = TRUE;
302
303 EXIT:
304   if (out) {
305       g_free(out);
306   }
307   return ret;
308 }
309
310
311 static GstOmxReturn
312 process_input_buf (GstOmxBaseFilter * omx_base_filter, GstBuffer **buf)
313 {
314   GstOmxH264Dec *h264_self;
315
316   h264_self = GST_OMX_H264DEC (omx_base_filter);
317
318   if (h264_self->h264Format == GSTOMX_H264_FORMAT_UNKNOWN) {
319     check_frame(h264_self, *buf);
320   }
321
322   if (h264_self->h264Format == GSTOMX_H264_FORMAT_PACKETIZED) {
323
324     if (omx_base_filter->last_pad_push_return != GST_FLOW_OK ||
325         !(omx_base_filter->gomx->omx_state == OMX_StateExecuting ||
326             omx_base_filter->gomx->omx_state == OMX_StatePause)) {
327         GST_LOG_OBJECT(h264_self, "this frame will not be converted and go to out_flushing");
328         return GSTOMX_RETURN_OK;
329     }
330
331     GST_LOG_OBJECT(h264_self, "H264 format is Packetized format. convert to Byte-stream format");
332     convert_frame(h264_self, buf);
333   }
334
335 /* if you want to use commonly for videodec input, use this */
336 /*  GST_OMX_BASE_FILTER_CLASS (parent_class)->process_input_buf (omx_base_filter, buf); */
337
338   return GSTOMX_RETURN_OK;
339 }
340
341 static void
342 type_base_init (gpointer g_class)
343 {
344   GstElementClass *element_class;
345
346   element_class = GST_ELEMENT_CLASS (g_class);
347
348   gst_element_class_set_details_simple (element_class,
349       "OpenMAX IL H.264/AVC video decoder",
350       "Codec/Decoder/Video",
351       "Decodes video in H.264/AVC format with OpenMAX IL", "Felipe Contreras");
352
353   gst_element_class_add_pad_template (element_class,
354       gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
355           gstomx_template_caps (G_TYPE_FROM_CLASS (g_class), "sink")));
356
357   gst_element_class_add_pad_template (element_class,
358       gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
359           gstomx_template_caps (G_TYPE_FROM_CLASS (g_class), "src")));
360 }
361
362 static void
363 type_class_init (gpointer g_class, gpointer class_data)
364 {
365   GstOmxBaseFilterClass *basefilter_class;
366
367   basefilter_class = GST_OMX_BASE_FILTER_CLASS (g_class);
368
369   basefilter_class->process_input_buf = process_input_buf;
370 }
371
372 /* h264 dec has its own sink_setcaps for supporting nalu convert codec data */
373 static gboolean
374 sink_setcaps (GstPad * pad, GstCaps * caps)
375 {
376   GstStructure *structure;
377   GstOmxBaseVideoDec *self;
378   GstOmxH264Dec *h264_self;
379   GstOmxBaseFilter *omx_base;
380   GOmxCore *gomx;
381   OMX_PARAM_PORTDEFINITIONTYPE param;
382   gint width = 0;
383   gint height = 0;
384
385   self = GST_OMX_BASE_VIDEODEC (GST_PAD_PARENT (pad));
386   h264_self = GST_OMX_H264DEC (GST_PAD_PARENT (pad));
387   omx_base = GST_OMX_BASE_FILTER (self);
388
389   gomx = (GOmxCore *) omx_base->gomx;
390
391   GST_INFO_OBJECT (self, "setcaps (sink)(h264): %" GST_PTR_FORMAT, caps);
392
393   g_return_val_if_fail (gst_caps_get_size (caps) == 1, FALSE);
394
395   structure = gst_caps_get_structure (caps, 0);
396
397   gst_structure_get_int (structure, "width", &width);
398   gst_structure_get_int (structure, "height", &height);
399
400   {
401     const GValue *framerate = NULL;
402     framerate = gst_structure_get_value (structure, "framerate");
403     if (framerate) {
404       self->framerate_num = gst_value_get_fraction_numerator (framerate);
405       self->framerate_denom = gst_value_get_fraction_denominator (framerate);
406     }
407   }
408
409   G_OMX_INIT_PARAM (param);
410
411   {
412     const GValue *codec_data;
413     GstBuffer *buffer;
414     gboolean ret = FALSE;
415     guint8 *buf_data = NULL;
416
417     codec_data = gst_structure_get_value (structure, "codec_data");
418     if (codec_data) {
419       buffer = gst_value_get_buffer (codec_data);
420
421       buf_data = GST_BUFFER_DATA(buffer);
422
423       if (GST_BUFFER_SIZE(buffer) <= GSTOMX_H264_NAL_START_LEN) {
424           GST_ERROR("codec data size (%d) is less than start code length.", GST_BUFFER_SIZE(buffer));
425       } else {
426         if ((buf_data[0] == 0x00)&&(buf_data[1] == 0x00)&&
427            ((buf_data[2] == 0x01)||((buf_data[2] == 0x00)&&(buf_data[3] == 0x01)))) {
428              h264_self->h264Format = GSTOMX_H264_FORMAT_BYTE_STREAM;
429              GST_INFO_OBJECT(self, "H264 codec_data format is Byte-stream.");
430         } else {
431            h264_self->h264Format = GSTOMX_H264_FORMAT_PACKETIZED;
432            GST_INFO_OBJECT(self, "H264 codec_data format is Packetized.");
433         }
434
435         /* if codec data is 3gpp format, convert nalu format */
436         if(h264_self->h264Format == GSTOMX_H264_FORMAT_PACKETIZED) {
437           GstBuffer *nalu_dci = NULL;
438
439           ret = convert_dci(h264_self, buffer, &nalu_dci);
440           if (ret) {
441             omx_base->codec_data = nalu_dci;
442           } else {
443             GST_ERROR_OBJECT(h264_self, "converting dci error.");
444             if (nalu_dci) {
445               gst_buffer_unref (nalu_dci);
446               nalu_dci = NULL;
447             }
448             omx_base->codec_data = buffer;
449             gst_buffer_ref (buffer);
450           }
451
452         } else { /* not 3GPP format */
453           omx_base->codec_data = buffer;
454           gst_buffer_ref (buffer);
455         }
456       }
457       h264_self->h264Format = GSTOMX_H264_FORMAT_UNKNOWN;
458     }
459   }
460   /* Input port configuration. */
461   {
462     param.nPortIndex = omx_base->in_port->port_index;
463     OMX_GetParameter (gomx->omx_handle, OMX_IndexParamPortDefinition, &param);
464
465     param.format.video.nFrameWidth = width;
466     param.format.video.nFrameHeight = height;
467
468     OMX_SetParameter (gomx->omx_handle, OMX_IndexParamPortDefinition, &param);
469   }
470   return gst_pad_set_caps (pad, caps);
471 }
472
473 static void
474 type_instance_init (GTypeInstance * instance, gpointer g_class)
475 {
476   GstOmxBaseVideoDec *omx_base;
477   GstOmxBaseFilter *omx_base_filter;
478
479   omx_base = GST_OMX_BASE_VIDEODEC (instance);
480   omx_base_filter = GST_OMX_BASE_FILTER (instance);
481
482   omx_base->compression_format = OMX_VIDEO_CodingAVC;
483
484   gst_pad_set_setcaps_function (omx_base_filter->sinkpad, sink_setcaps);
485 }