codecparser: mpeg4 type error
[profile/ivi/gst-plugins-bad.git] / gst / mpegvideoparse / mpegpacketiser.c
1 /* GStreamer
2  * Copyright (C) <2007> Jan Schmidt <thaytan@mad.scientist.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., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <string.h>
25
26 /* The purpose of the packetiser is to parse the incoming buffers into 'blocks'
27  * that consist of the stream split at certain packet boundaries. It splits into
28  * a new block at the start of a GOP, Picture or Sequence packet.
29  * A GOP or sequence header always starts a new block. A Picture
30  * header starts a new block only if the previous packet was not a GOP - 
31  * otherwise it is accumulated with the GOP */
32 #include "mpegpacketiser.h"
33
34 GST_DEBUG_CATEGORY_EXTERN (mpv_parse_debug);
35 #define GST_CAT_DEFAULT mpv_parse_debug
36
37 static void collect_packets (MPEGPacketiser * p, GstBuffer * buf);
38
39 void
40 mpeg_packetiser_init (MPEGPacketiser * p)
41 {
42   p->adapter = gst_adapter_new ();
43   p->n_blocks = 0;
44   p->blocks = NULL;
45   mpeg_packetiser_flush (p);
46 }
47
48 void
49 mpeg_packetiser_free (MPEGPacketiser * p)
50 {
51   g_object_unref (p->adapter);
52   g_free (p->blocks);
53 }
54
55 void
56 mpeg_packetiser_add_buf (MPEGPacketiser * p, GstBuffer * buf)
57 {
58   /* Add the buffer to our pool */
59   gst_adapter_push (p->adapter, buf);
60
61   /* Store the timestamp to apply to the next picture that gets collected */
62   if (p->cur_buf_ts != GST_CLOCK_TIME_NONE) {
63     p->prev_buf_ts = p->cur_buf_ts;
64   }
65   p->cur_buf_ts = GST_BUFFER_TIMESTAMP (buf);
66
67   /* read what new packets we have in this buffer */
68   collect_packets (p, buf);
69
70   p->tracked_offset += GST_BUFFER_SIZE (buf);
71 }
72
73 void
74 mpeg_packetiser_flush (MPEGPacketiser * p)
75 {
76   gst_adapter_clear (p->adapter);
77   p->adapter_offset = 0;
78
79   p->sync_word = 0xffffffff;
80   p->tracked_offset = 0;
81   p->prev_sync_packet = MPEG_PACKET_NONE;
82
83   /* Reset our block info */
84   p->cur_block_idx = -1;
85   p->first_block_idx = -1;
86
87   /* Clear any pending timestamps */
88   p->prev_buf_ts = GST_CLOCK_TIME_NONE;
89   p->cur_buf_ts = GST_CLOCK_TIME_NONE;
90 }
91
92 guint8 *
93 mpeg_util_find_start_code (guint32 * sync_word, guint8 * cur, guint8 * end)
94 {
95   guint32 code;
96
97   if (G_UNLIKELY (cur == NULL))
98     return NULL;
99
100   code = *sync_word;
101
102   while (cur < end) {
103     code <<= 8;
104
105     if (code == 0x00000100) {
106       /* Reset the sync word accumulator */
107       *sync_word = 0xffffffff;
108       return cur;
109     }
110
111     /* accelerate search for start code */
112     if (*cur > 1) {
113       while (cur < (end - 4) && *cur > 1)
114         if (cur[3] > 1)
115           cur += 4;
116         else
117           cur++;
118       code = 0xffffff00;
119     }
120
121     /* Add the next available byte to the collected sync word */
122     code |= *cur++;
123   }
124
125   *sync_word = code;
126   return NULL;
127 }
128
129 /* When we need to reallocate the blocks array, grow it by this much */
130 #define BLOCKS_INCREMENT 5
131
132 /* Get the index of the next unfilled block in the buffer. May need to grow
133  * the array first */
134 static gint
135 get_next_free_block (MPEGPacketiser * p)
136 {
137   gint next;
138   gboolean grow_array = FALSE;
139
140   /* Get a free block from the blocks array. May need to grow
141    * the array first */
142   if (p->n_blocks == 0) {
143     grow_array = TRUE;
144     next = 0;
145   } else {
146     if (G_UNLIKELY (p->cur_block_idx == -1)) {
147       next = 0;
148     } else {
149       next = p->cur_block_idx;
150       if (((next + 1) % p->n_blocks) == p->first_block_idx)
151         grow_array = TRUE;
152     }
153   }
154
155   if (grow_array) {
156     gint old_n_blocks = p->n_blocks;
157
158     p->n_blocks += BLOCKS_INCREMENT;
159
160     p->blocks = g_realloc (p->blocks, sizeof (MPEGBlockInfo) * p->n_blocks);
161
162     /* Now we may need to move some data up to the end of the array, if the 
163      * cur_block_idx is before the first_block_idx in the array. */
164     if (p->cur_block_idx < p->first_block_idx) {
165
166       GST_LOG ("Moving %d blocks from idx %d to idx %d of %d",
167           old_n_blocks - p->first_block_idx,
168           p->first_block_idx, p->first_block_idx + BLOCKS_INCREMENT,
169           p->n_blocks);
170
171       memmove (p->blocks + p->first_block_idx + BLOCKS_INCREMENT,
172           p->blocks + p->first_block_idx,
173           sizeof (MPEGBlockInfo) * (old_n_blocks - p->first_block_idx));
174       p->first_block_idx += BLOCKS_INCREMENT;
175     }
176   }
177
178   return next;
179 }
180
181 /* Mark the current block as complete */
182 static void
183 complete_current_block (MPEGPacketiser * p, guint64 offset)
184 {
185   MPEGBlockInfo *block;
186
187   if (G_UNLIKELY (p->cur_block_idx == -1))
188     return;                     /* No block is in progress */
189
190   /* If we're pointing at the first_block_idx, then we're about to re-complete
191    * a previously completed buffer. Not allowed, because the array should have
192    * been previously expanded to cope via a get_next_free_block call. */
193   g_assert (p->cur_block_idx != p->first_block_idx);
194
195   /* Get the appropriate entry from the blocks array */
196   g_assert (p->blocks != NULL && p->cur_block_idx < p->n_blocks);
197   block = p->blocks + p->cur_block_idx;
198
199   /* Extend the block length to the current offset */
200   g_assert (block->offset < offset);
201   block->length = offset - block->offset;
202
203   GST_LOG ("Completed block of type 0x%02x @ offset %" G_GUINT64_FORMAT
204       " with size %u", block->first_pack_type, block->offset, block->length);
205
206   /* If this is the first complete block, set first_block_idx to be this block */
207   if (p->first_block_idx == -1)
208     p->first_block_idx = p->cur_block_idx;
209
210   /* Update the statistics regarding the packet we're handling */
211   if (block->flags & MPEG_BLOCK_FLAG_PICTURE)
212     p->n_pictures++;
213
214   /* And advance the cur_block_idx ptr to the next slot */
215   p->cur_block_idx = (p->cur_block_idx + 1) % p->n_blocks;
216 }
217
218 /* Accumulate the packet up to 'offset' into the current block 
219  * (discard if no block is in progress). Update the block info
220  * to indicate what is in it. */
221 static void
222 append_to_current_block (MPEGPacketiser * p, guint64 offset, guint8 pack_type)
223 {
224   MPEGBlockInfo *block;
225
226   if (G_UNLIKELY (p->cur_block_idx == -1))
227     return;                     /* No block in progress, drop this packet */
228
229   /* Get the appropriate entry from the blocks array */
230   g_assert (p->blocks != NULL && p->cur_block_idx < p->n_blocks);
231   block = p->blocks + p->cur_block_idx;
232
233   /* Extend the block length to the current offset */
234   g_assert (block->offset < offset);
235   block->length = offset - block->offset;
236
237   /* Update flags */
238   switch (pack_type) {
239     case MPEG_PACKET_SEQUENCE:
240       g_assert (!(block->flags &
241               (MPEG_BLOCK_FLAG_GOP | MPEG_BLOCK_FLAG_PICTURE)));
242       block->flags |= MPEG_BLOCK_FLAG_SEQUENCE;
243       break;
244     case MPEG_PACKET_GOP:
245       block->flags |= MPEG_BLOCK_FLAG_GOP;
246       break;
247     case MPEG_PACKET_PICTURE:
248       block->flags |= MPEG_BLOCK_FLAG_PICTURE;
249       break;
250     default:
251       break;
252   }
253 }
254
255 static void
256 start_new_block (MPEGPacketiser * p, guint64 offset, guint8 pack_type)
257 {
258   gint block_idx;
259   MPEGBlockInfo *block;
260
261   /* First, append data up to the start of this block to the current one, but
262    * not including this packet info */
263   complete_current_block (p, offset);
264
265   block_idx = get_next_free_block (p);
266   /* FIXME: Retrieve the appropriate entry from the blocks array */
267   /* Get the appropriate entry from the blocks array */
268   g_assert (p->blocks != NULL && block_idx < p->n_blocks);
269   block = p->blocks + block_idx;
270
271   /* Init the block */
272   block->first_pack_type = pack_type;
273   block->offset = offset;
274   block->ts = GST_CLOCK_TIME_NONE;
275
276   /* Initially, the length is 0. It grows as we encounter new sync headers */
277   block->length = 0;
278   switch (pack_type) {
279     case MPEG_PACKET_SEQUENCE:
280       block->flags = MPEG_BLOCK_FLAG_SEQUENCE;
281       break;
282     case MPEG_PACKET_GOP:
283       block->flags = MPEG_BLOCK_FLAG_GOP;
284       break;
285     case MPEG_PACKET_PICTURE:
286       block->flags = MPEG_BLOCK_FLAG_PICTURE;
287       break;
288     default:
289       /* We don't start blocks with other packet types */
290       g_assert_not_reached ();
291   }
292
293   /* Make this our current block */
294   p->cur_block_idx = block_idx;
295
296   GST_LOG ("Started new block in slot %d with first pack 0x%02x @ offset %"
297       G_GUINT64_FORMAT, block_idx, block->first_pack_type, block->offset);
298
299 }
300
301 static void
302 handle_packet (MPEGPacketiser * p, guint64 offset, guint8 pack_type)
303 {
304   GST_LOG ("offset %" G_GUINT64_FORMAT ", pack_type %2x", offset, pack_type);
305   switch (pack_type) {
306     case MPEG_PACKET_SEQUENCE:
307     case MPEG_PACKET_GOP:
308       /* Start a new block */
309       start_new_block (p, offset, pack_type);
310       p->prev_sync_packet = pack_type;
311       break;
312     case MPEG_PACKET_PICTURE:{
313       MPEGBlockInfo *block;
314       GstClockTime ts;
315
316       /* Start a new block unless the previous sync packet was a GOP */
317       if (p->prev_sync_packet != MPEG_PACKET_GOP) {
318         start_new_block (p, offset, pack_type);
319       } else {
320         append_to_current_block (p, offset, pack_type);
321       }
322       p->prev_sync_packet = pack_type;
323
324       /* We have a picture packet, apply any pending timestamp. The logic here
325        * is that the timestamp on any incoming buffer needs to apply to the next
326        * picture packet where the _first_byte_ of the sync word starts after the
327        * packet boundary. We track the ts from the current buffer and a
328        * previous buffer in order to handle this correctly. It would still be
329        * possible to get it wrong if there was a PES packet smaller than 3 bytes
330        * but anyone that does that can suck it.  */
331       if ((offset >= p->tracked_offset)
332           && (p->cur_buf_ts != GST_CLOCK_TIME_NONE)) {
333         /* sync word started within this buffer - take the cur ts */
334         ts = p->cur_buf_ts;
335         p->cur_buf_ts = GST_CLOCK_TIME_NONE;
336         p->prev_buf_ts = GST_CLOCK_TIME_NONE;
337       } else {
338         /* sync word started in a previous buffer - take the old ts */
339         ts = p->prev_buf_ts;
340         p->prev_buf_ts = GST_CLOCK_TIME_NONE;
341       }
342
343       /* If we didn't drop the packet, set the timestamp on it */
344       if (G_LIKELY (p->cur_block_idx != -1)) {
345         block = p->blocks + p->cur_block_idx;
346         block->ts = ts;
347         GST_LOG ("Picture @ offset %" G_GINT64_FORMAT " has ts %"
348             GST_TIME_FORMAT, block->offset, GST_TIME_ARGS (block->ts));
349       }
350       break;
351     }
352     default:
353       append_to_current_block (p, offset, pack_type);
354       break;
355   }
356 }
357
358 static void
359 collect_packets (MPEGPacketiser * p, GstBuffer * buf)
360 {
361   guint8 *cur;
362   guint8 *end = GST_BUFFER_DATA (buf) + GST_BUFFER_SIZE (buf);
363
364   cur = mpeg_util_find_start_code (&(p->sync_word), GST_BUFFER_DATA (buf), end);
365   while (cur != NULL) {
366     /* Calculate the offset as tracked since the last flush. Note that cur
367      * points to the last byte of the sync word, so we adjust by -3 to get the
368      * first byte */
369     guint64 offset = p->tracked_offset + (cur - GST_BUFFER_DATA (buf) - 3);
370
371     handle_packet (p, offset, *cur);
372     cur = mpeg_util_find_start_code (&(p->sync_word), cur, end);
373   }
374 }
375
376 void
377 mpeg_packetiser_handle_eos (MPEGPacketiser * p)
378 {
379   /* Append any remaining data to the current block */
380   if (p->tracked_offset > 0) {
381     complete_current_block (p, p->tracked_offset);
382   }
383 }
384
385 /* Returns a pointer to the block info for the completed block at the
386  * head of the queue, and extracts the bytes from the adapter if requested.
387  * Caller should move to the next block by calling mpeg_packetiser_next_block
388  * afterward.
389  */
390 MPEGBlockInfo *
391 mpeg_packetiser_get_block (MPEGPacketiser * p, GstBuffer ** buf)
392 {
393   MPEGBlockInfo *block;
394
395   if (buf)
396     *buf = NULL;
397
398   if (G_UNLIKELY (p->first_block_idx == -1)) {
399     return NULL;                /* No complete blocks to discard */
400   }
401
402   /* p->first_block_idx can't get set != -1 unless some block storage got
403    * allocated */
404   g_assert (p->blocks != NULL && p->n_blocks != 0);
405   block = p->blocks + p->first_block_idx;
406
407   /* Can only get the buffer out once, so we'll return NULL on later attempts */
408   if (buf != NULL && block->length > 0 && p->adapter_offset <= block->offset) {
409     /* Kick excess data out of the adapter */
410     if (p->adapter_offset < block->offset) {
411       guint64 to_flush = block->offset - p->adapter_offset;
412
413       g_assert (gst_adapter_available (p->adapter) >= to_flush);
414       gst_adapter_flush (p->adapter, to_flush);
415       p->adapter_offset += to_flush;
416     }
417
418     g_assert (gst_adapter_available (p->adapter) >= block->length);
419     *buf = gst_adapter_take_buffer (p->adapter, block->length);
420     p->adapter_offset += block->length;
421
422     GST_BUFFER_TIMESTAMP (*buf) = block->ts;
423   } else {
424     GST_DEBUG ("we have a block but do not meet all conditions buf: %p "
425         "block length: %d adapter offset %" G_GUINT64_FORMAT " block offset "
426         "%" G_GUINT64_FORMAT, buf, block->length, p->adapter_offset,
427         block->offset);
428   }
429   return block;
430 }
431
432 /* Advance the first_block pointer to discard a completed block
433  * from the queue */
434 void
435 mpeg_packetiser_next_block (MPEGPacketiser * p)
436 {
437   gint next;
438   MPEGBlockInfo *block;
439
440   block = mpeg_packetiser_get_block (p, NULL);
441   if (G_UNLIKELY (block == NULL))
442     return;                     /* No complete blocks to discard */
443
444   /* Update the statistics regarding the block we're discarding */
445   if (block->flags & MPEG_BLOCK_FLAG_PICTURE)
446     p->n_pictures--;
447
448   next = (p->first_block_idx + 1) % p->n_blocks;
449   if (next == p->cur_block_idx)
450     p->first_block_idx = -1;    /* Discarding the last block */
451   else
452     p->first_block_idx = next;
453 }
454
455 /* Set the Pixel Aspect Ratio in our hdr from a DAR code in the data */
456 static void
457 set_par_from_dar (MPEGSeqHdr * hdr, guint8 asr_code)
458 {
459   /* Pixel_width = DAR_width * display_vertical_size */
460   /* Pixel_height = DAR_height * display_horizontal_size */
461   switch (asr_code) {
462     case 0x02:                 /* 3:4 DAR = 4:3 pixels */
463       hdr->par_w = 4 * hdr->height;
464       hdr->par_h = 3 * hdr->width;
465       break;
466     case 0x03:                 /* 9:16 DAR */
467       hdr->par_w = 16 * hdr->height;
468       hdr->par_h = 9 * hdr->width;
469       break;
470     case 0x04:                 /* 1:2.21 DAR */
471       hdr->par_w = 221 * hdr->height;
472       hdr->par_h = 100 * hdr->width;
473       break;
474     case 0x01:                 /* Square pixels */
475     default:
476       hdr->par_w = hdr->par_h = 1;
477       break;
478   }
479 }
480
481 static void
482 set_fps_from_code (MPEGSeqHdr * hdr, guint8 fps_code)
483 {
484   const gint framerates[][2] = {
485     {30, 1}, {24000, 1001}, {24, 1}, {25, 1},
486     {30000, 1001}, {30, 1}, {50, 1}, {60000, 1001},
487     {60, 1}, {30, 1}
488   };
489
490   if (fps_code < 10) {
491     hdr->fps_n = framerates[fps_code][0];
492     hdr->fps_d = framerates[fps_code][1];
493   } else {
494     /* Force a valid framerate */
495     hdr->fps_n = 30000;
496     hdr->fps_d = 1001;
497   }
498 }
499
500 static gboolean
501 mpeg_util_parse_extension_packet (MPEGSeqHdr * hdr, guint8 * data, guint8 * end)
502 {
503   guint8 ext_code;
504
505   if (G_UNLIKELY (data >= end))
506     return FALSE;               /* short extension packet */
507
508   ext_code = data[0] >> 4;
509
510   switch (ext_code) {
511     case MPEG_PACKET_EXT_SEQUENCE:
512     {
513       /* Parse a Sequence Extension */
514       guint8 horiz_size_ext, vert_size_ext;
515       guint8 fps_n_ext, fps_d_ext;
516
517       if (G_UNLIKELY ((end - data) < 6))
518         /* need at least 10 bytes, minus 4 for the start code 000001b5 */
519         return FALSE;
520
521       hdr->profile = data[0] & 0x0f;    /* profile (0:2) + escape bit (3) */
522       hdr->level = (data[1] >> 4) & 0x0f;
523       hdr->progressive = data[1] & 0x08;
524       /* chroma_format = (data[1] >> 2) & 0x03; */
525       horiz_size_ext = ((data[1] << 1) & 0x02) | ((data[2] >> 7) & 0x01);
526       vert_size_ext = (data[2] >> 5) & 0x03;
527       /* low_delay = data[5] >> 7; */
528       fps_n_ext = (data[5] >> 5) & 0x03;
529       fps_d_ext = data[5] & 0x1f;
530
531       hdr->fps_n *= (fps_n_ext + 1);
532       hdr->fps_d *= (fps_d_ext + 1);
533       hdr->width += (horiz_size_ext << 12);
534       hdr->height += (vert_size_ext << 12);
535       break;
536     }
537     default:
538       break;
539   }
540
541   return TRUE;
542 }
543
544 gboolean
545 mpeg_util_parse_sequence_hdr (MPEGSeqHdr * hdr, guint8 * data, guint8 * end)
546 {
547   guint32 code;
548   guint8 dar_idx, fps_idx;
549   guint32 sync_word = 0xffffffff;
550   gboolean load_intra_flag;
551   gboolean load_non_intra_flag;
552
553   if (G_UNLIKELY ((end - data) < 12))
554     return FALSE;               /* Too small to be a sequence header */
555
556   code = GST_READ_UINT32_BE (data);
557   if (G_UNLIKELY (code != (0x00000100 | MPEG_PACKET_SEQUENCE)))
558     return FALSE;
559
560   /* Skip the sync word */
561   data += 4;
562
563   /* Parse the MPEG 1 bits */
564   hdr->mpeg_version = 1;
565
566   code = GST_READ_UINT32_BE (data);
567   hdr->width = (code >> 20) & 0xfff;
568   hdr->height = (code >> 8) & 0xfff;
569
570   dar_idx = (code >> 4) & 0xf;
571   set_par_from_dar (hdr, dar_idx);
572   fps_idx = code & 0xf;
573   set_fps_from_code (hdr, fps_idx);
574
575   hdr->bitrate = ((data[6] >> 6) | (data[5] << 2) | (data[4] << 10));
576   if (hdr->bitrate == 0x3ffff) {
577     /* VBR stream */
578     hdr->bitrate = 0;
579   } else {
580     /* Value in header is in units of 400 bps */
581     hdr->bitrate *= 400;
582   }
583
584   /* constrained_flag = (data[7] >> 2) & 0x01; */
585   load_intra_flag = (data[7] >> 1) & 0x01;
586   if (load_intra_flag) {
587     if (G_UNLIKELY ((end - data) < 64))
588       return FALSE;
589     data += 64;
590   }
591
592   load_non_intra_flag = data[7] & 0x01;
593   if (load_non_intra_flag) {
594     if (G_UNLIKELY ((end - data) < 64))
595       return FALSE;
596     data += 64;
597   }
598
599   /* Advance past the rest of the MPEG-1 header */
600   data += 8;
601
602   /* Read MPEG-2 sequence extensions */
603   data = mpeg_util_find_start_code (&sync_word, data, end);
604   while (data != NULL) {
605     if (G_UNLIKELY (data >= end))
606       return FALSE;
607
608     /* data points at the last byte of the start code */
609     if (data[0] == MPEG_PACKET_EXTENSION) {
610       if (!mpeg_util_parse_extension_packet (hdr, data + 1, end))
611         return FALSE;
612
613       hdr->mpeg_version = 2;
614     }
615     data = mpeg_util_find_start_code (&sync_word, data, end);
616   }
617
618   return TRUE;
619 }
620
621 gboolean
622 mpeg_util_parse_picture_hdr (MPEGPictureHdr * hdr, guint8 * data, guint8 * end)
623 {
624   guint32 code;
625
626   if (G_UNLIKELY ((end - data) < 6))
627     return FALSE;               /* Packet too small */
628
629   code = GST_READ_UINT32_BE (data);
630   if (G_UNLIKELY (code != (0x00000100 | MPEG_PACKET_PICTURE)))
631     return FALSE;
632
633   /* Skip the start code */
634   data += 4;
635
636   hdr->pic_type = (data[1] >> 3) & 0x07;
637   if (hdr->pic_type == 0 || hdr->pic_type > 4)
638     return FALSE;               /* Corrupted picture packet */
639
640   return TRUE;
641 }