riff: Print invalid fourcc in error message in hex
[platform/upstream/gstreamer.git] / gst-libs / gst / riff / riff-read.c
1 /* GStreamer RIFF I/O
2  * Copyright (C) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
3  *
4  * riff-read.c: RIFF input file parsing
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
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  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <string.h>
27 #include <gst/gstutils.h>
28 #include <gst/tag/tag.h>
29
30 #include "riff-read.h"
31
32 GST_DEBUG_CATEGORY_EXTERN (riff_debug);
33 #define GST_CAT_DEFAULT riff_debug
34
35 /**
36  * gst_riff_read_chunk:
37  * @element: caller element (used for debugging).
38  * @pad: pad to pull data from.
39  * @offset: offset to pull from, incremented by this function.
40  * @tag: fourcc of the chunk (returned by this function).
41  * @chunk_data: buffer (returned by this function).
42  *
43  * Reads a single chunk of data. 'JUNK' chunks are skipped
44  * automatically.
45  *
46  * Returns: flow status.
47  */
48
49 GstFlowReturn
50 gst_riff_read_chunk (GstElement * element,
51     GstPad * pad, guint64 * _offset, guint32 * tag, GstBuffer ** _chunk_data)
52 {
53   GstBuffer *buf;
54   GstFlowReturn res;
55   GstMapInfo info;
56   guint size;
57   guint64 offset = *_offset;
58
59   g_return_val_if_fail (element != NULL, GST_FLOW_ERROR);
60   g_return_val_if_fail (pad != NULL, GST_FLOW_ERROR);
61   g_return_val_if_fail (_offset != NULL, GST_FLOW_ERROR);
62   g_return_val_if_fail (tag != NULL, GST_FLOW_ERROR);
63   g_return_val_if_fail (_chunk_data != NULL, GST_FLOW_ERROR);
64
65 skip_junk:
66   size = 8;
67   buf = NULL;
68   if ((res = gst_pad_pull_range (pad, offset, size, &buf)) != GST_FLOW_OK)
69     return res;
70   else if (gst_buffer_get_size (buf) < size)
71     goto too_small;
72
73   gst_buffer_map (buf, &info, GST_MAP_READ);
74   *tag = GST_READ_UINT32_LE (info.data);
75   size = GST_READ_UINT32_LE (info.data + 4);
76   gst_buffer_unmap (buf, &info);
77   gst_buffer_unref (buf);
78
79   GST_DEBUG_OBJECT (element, "fourcc=%" GST_FOURCC_FORMAT ", size=%u",
80       GST_FOURCC_ARGS (*tag), size);
81
82   /* skip 'JUNK' chunks */
83   if (*tag == GST_RIFF_TAG_JUNK || *tag == GST_RIFF_TAG_JUNQ) {
84     size = GST_ROUND_UP_2 (size);
85     *_offset += 8 + size;
86     offset += 8 + size;
87     GST_DEBUG_OBJECT (element, "skipping JUNK chunk");
88     goto skip_junk;
89   }
90
91   buf = NULL;
92   if ((res = gst_pad_pull_range (pad, offset + 8, size, &buf)) != GST_FLOW_OK)
93     return res;
94   else if (gst_buffer_get_size (buf) < size)
95     goto too_small;
96
97   *_chunk_data = buf;
98   *_offset += 8 + GST_ROUND_UP_2 (size);
99
100   return GST_FLOW_OK;
101
102   /* ERRORS */
103 too_small:
104   {
105     /* short read, we return EOS to mark the EOS case */
106     GST_DEBUG_OBJECT (element, "not enough data (available=%" G_GSIZE_FORMAT
107         ", needed=%u)", gst_buffer_get_size (buf), size);
108     gst_buffer_unref (buf);
109     return GST_FLOW_EOS;
110   }
111 }
112
113 /**
114  * gst_riff_parse_chunk:
115  * @element: caller element (used for debugging).
116  * @buf: input buffer.
117  * @offset: offset in the buffer in the caller. Is incremented
118  *          by the read size by this function.
119  * @fourcc: fourcc (returned by this function0 of the chunk.
120  * @chunk_data: buffer (returned by the function) containing the
121  *              chunk data, which may be NULL if chunksize == 0
122  *
123  * Reads a single chunk.
124  *
125  * Returns: FALSE on error, TRUE otherwise
126  */
127 gboolean
128 gst_riff_parse_chunk (GstElement * element, GstBuffer * buf,
129     guint * _offset, guint32 * _fourcc, GstBuffer ** chunk_data)
130 {
131   guint size, bufsize;
132   guint32 fourcc;
133   guint8 *ptr;
134   GstMapInfo info;
135   guint offset = *_offset;
136
137   g_return_val_if_fail (element != NULL, FALSE);
138   g_return_val_if_fail (buf != NULL, FALSE);
139   g_return_val_if_fail (_offset != NULL, FALSE);
140   g_return_val_if_fail (_fourcc != NULL, FALSE);
141   g_return_val_if_fail (chunk_data != NULL, FALSE);
142
143   *chunk_data = NULL;
144   *_fourcc = 0;
145
146   bufsize = gst_buffer_get_size (buf);
147
148   if (bufsize == offset)
149     goto end_offset;
150
151   if (bufsize < offset + 8)
152     goto too_small;
153
154   /* read header */
155   gst_buffer_map (buf, &info, GST_MAP_READ);
156   ptr = info.data + offset;
157   fourcc = GST_READ_UINT32_LE (ptr);
158   size = GST_READ_UINT32_LE (ptr + 4);
159   gst_buffer_unmap (buf, &info);
160
161   GST_DEBUG_OBJECT (element, "fourcc=%" GST_FOURCC_FORMAT ", size=%u",
162       GST_FOURCC_ARGS (fourcc), size);
163
164   /* be paranoid: size may be nonsensical value here, such as (guint) -1 */
165   if (G_UNLIKELY (size > G_MAXINT))
166     goto bogus_size;
167
168   if (bufsize < size + 8 + offset) {
169     GST_DEBUG_OBJECT (element,
170         "Needed chunk data (%d) is more than available (%d), shortcutting",
171         size, bufsize - 8 - offset);
172     size = bufsize - 8 - offset;
173   }
174
175   if (size)
176     *chunk_data =
177         gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, offset + 8, size);
178   else
179     *chunk_data = NULL;
180
181   *_fourcc = fourcc;
182   *_offset += 8 + GST_ROUND_UP_2 (size);
183
184   return TRUE;
185
186   /* ERRORS */
187 end_offset:
188   {
189     GST_DEBUG_OBJECT (element, "End of chunk (offset %d)", offset);
190     return FALSE;
191   }
192 too_small:
193   {
194     GST_DEBUG_OBJECT (element,
195         "Failed to parse chunk header (offset %d, %d available, %d needed)",
196         offset, bufsize, 8);
197     return FALSE;
198   }
199 bogus_size:
200   {
201     GST_ERROR_OBJECT (element, "Broken file: bogus chunk size %u", size);
202     return FALSE;
203   }
204 }
205
206 /**
207  * gst_riff_parse_file_header:
208  * @element: caller element (used for debugging/error).
209  * @buf: input buffer from which the file header will be parsed,
210  *       should be at least 12 bytes long.
211  * @doctype: a fourcc (returned by this function) to indicate the
212  *           type of document (according to the header).
213  *
214  * Reads the first few bytes from the provided buffer, checks
215  * if this stream is a RIFF stream, and determines document type.
216  * This function takes ownership of @buf so it should not be used anymore
217  * after calling this function.
218  *
219  * Returns: FALSE if this is not a RIFF stream (in which case the
220  * caller should error out; we already throw an error), or TRUE
221  * if it is.
222  */
223 gboolean
224 gst_riff_parse_file_header (GstElement * element,
225     GstBuffer * buf, guint32 * doctype)
226 {
227   GstMapInfo info;
228   guint32 tag;
229
230   g_return_val_if_fail (buf != NULL, FALSE);
231   g_return_val_if_fail (doctype != NULL, FALSE);
232
233   gst_buffer_map (buf, &info, GST_MAP_READ);
234   if (info.size < 12)
235     goto too_small;
236
237   tag = GST_READ_UINT32_LE (info.data);
238   if (tag != GST_RIFF_TAG_RIFF && tag != GST_RIFF_TAG_AVF0)
239     goto not_riff;
240
241   *doctype = GST_READ_UINT32_LE (info.data + 8);
242   gst_buffer_unmap (buf, &info);
243
244   gst_buffer_unref (buf);
245
246   return TRUE;
247
248   /* ERRORS */
249 too_small:
250   {
251     GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
252         ("Not enough data to parse RIFF header (%" G_GSIZE_FORMAT " available,"
253             " %d needed)", info.size, 12));
254     gst_buffer_unmap (buf, &info);
255     gst_buffer_unref (buf);
256     return FALSE;
257   }
258 not_riff:
259   {
260     GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
261         ("Stream is no RIFF stream: 0x%" G_GINT32_MODIFIER "x", tag));
262     gst_buffer_unmap (buf, &info);
263     gst_buffer_unref (buf);
264     return FALSE;
265   }
266 }
267
268 /**
269  * gst_riff_parse_strh:
270  * @element: caller element (used for debugging/error).
271  * @buf: input data to be used for parsing, stripped from header.
272  * @strh: a pointer (returned by this function) to a filled-in
273  *        strh structure. Caller should free it.
274  *
275  * Parses a strh structure from input data. Takes ownership of @buf.
276  *
277  * Returns: TRUE if parsing succeeded, otherwise FALSE. The stream
278  *          should be skipped on error, but it is not fatal.
279  */
280
281 gboolean
282 gst_riff_parse_strh (GstElement * element,
283     GstBuffer * buf, gst_riff_strh ** _strh)
284 {
285   gst_riff_strh *strh;
286   GstMapInfo info;
287
288   g_return_val_if_fail (buf != NULL, FALSE);
289   g_return_val_if_fail (_strh != NULL, FALSE);
290
291   gst_buffer_map (buf, &info, GST_MAP_READ);
292   if (info.size < sizeof (gst_riff_strh))
293     goto too_small;
294
295   strh = g_memdup (info.data, info.size);
296   gst_buffer_unmap (buf, &info);
297
298   gst_buffer_unref (buf);
299
300 #if (G_BYTE_ORDER == G_BIG_ENDIAN)
301   strh->type = GUINT32_FROM_LE (strh->type);
302   strh->fcc_handler = GUINT32_FROM_LE (strh->fcc_handler);
303   strh->flags = GUINT32_FROM_LE (strh->flags);
304   strh->priority = GUINT32_FROM_LE (strh->priority);
305   strh->init_frames = GUINT32_FROM_LE (strh->init_frames);
306   strh->scale = GUINT32_FROM_LE (strh->scale);
307   strh->rate = GUINT32_FROM_LE (strh->rate);
308   strh->start = GUINT32_FROM_LE (strh->start);
309   strh->length = GUINT32_FROM_LE (strh->length);
310   strh->bufsize = GUINT32_FROM_LE (strh->bufsize);
311   strh->quality = GUINT32_FROM_LE (strh->quality);
312   strh->samplesize = GUINT32_FROM_LE (strh->samplesize);
313 #endif
314
315   /* avoid divisions by zero */
316   if (!strh->scale)
317     strh->scale = 1;
318   if (!strh->rate)
319     strh->rate = 1;
320
321   /* debug */
322   GST_INFO_OBJECT (element, "strh tag found:");
323   GST_INFO_OBJECT (element, " type        %" GST_FOURCC_FORMAT,
324       GST_FOURCC_ARGS (strh->type));
325   GST_INFO_OBJECT (element, " fcc_handler %" GST_FOURCC_FORMAT,
326       GST_FOURCC_ARGS (strh->fcc_handler));
327   GST_INFO_OBJECT (element, " flags       0x%08x", strh->flags);
328   GST_INFO_OBJECT (element, " priority    %d", strh->priority);
329   GST_INFO_OBJECT (element, " init_frames %d", strh->init_frames);
330   GST_INFO_OBJECT (element, " scale       %d", strh->scale);
331   GST_INFO_OBJECT (element, " rate        %d", strh->rate);
332   GST_INFO_OBJECT (element, " start       %d", strh->start);
333   GST_INFO_OBJECT (element, " length      %d", strh->length);
334   GST_INFO_OBJECT (element, " bufsize     %d", strh->bufsize);
335   GST_INFO_OBJECT (element, " quality     %d", strh->quality);
336   GST_INFO_OBJECT (element, " samplesize  %d", strh->samplesize);
337
338   *_strh = strh;
339
340   return TRUE;
341
342   /* ERRORS */
343 too_small:
344   {
345     GST_ERROR_OBJECT (element,
346         "Too small strh (%" G_GSIZE_FORMAT " available, %d needed)",
347         info.size, (int) sizeof (gst_riff_strh));
348     gst_buffer_unmap (buf, &info);
349     gst_buffer_unref (buf);
350     return FALSE;
351   }
352 }
353
354 /**
355  * gst_riff_parse_strf_vids:
356  * @element: caller element (used for debugging/error).
357  * @buf: input data to be used for parsing, stripped from header.
358  * @strf: a pointer (returned by this function) to a filled-in
359  *        strf/vids structure. Caller should free it.
360  * @data: a pointer (returned by this function) to a buffer
361  *        containing extradata for this particular stream (e.g.
362  *        palette, codec initialization data).
363  *
364  * Parses a video stream´s strf structure plus optionally some
365  * extradata from input data. This function takes ownership of @buf.
366  *
367  * Returns: TRUE if parsing succeeded, otherwise FALSE. The stream
368  *          should be skipped on error, but it is not fatal.
369  */
370
371 gboolean
372 gst_riff_parse_strf_vids (GstElement * element,
373     GstBuffer * buf, gst_riff_strf_vids ** _strf, GstBuffer ** data)
374 {
375   gst_riff_strf_vids *strf;
376   GstMapInfo info;
377
378   g_return_val_if_fail (buf != NULL, FALSE);
379   g_return_val_if_fail (_strf != NULL, FALSE);
380   g_return_val_if_fail (data != NULL, FALSE);
381
382   gst_buffer_map (buf, &info, GST_MAP_READ);
383   if (info.size < sizeof (gst_riff_strf_vids))
384     goto too_small;
385
386   strf = g_memdup (info.data, info.size);
387   gst_buffer_unmap (buf, &info);
388
389 #if (G_BYTE_ORDER == G_BIG_ENDIAN)
390   strf->size = GUINT32_FROM_LE (strf->size);
391   strf->width = GUINT32_FROM_LE (strf->width);
392   strf->height = GUINT32_FROM_LE (strf->height);
393   strf->planes = GUINT16_FROM_LE (strf->planes);
394   strf->bit_cnt = GUINT16_FROM_LE (strf->bit_cnt);
395   strf->compression = GUINT32_FROM_LE (strf->compression);
396   strf->image_size = GUINT32_FROM_LE (strf->image_size);
397   strf->xpels_meter = GUINT32_FROM_LE (strf->xpels_meter);
398   strf->ypels_meter = GUINT32_FROM_LE (strf->ypels_meter);
399   strf->num_colors = GUINT32_FROM_LE (strf->num_colors);
400   strf->imp_colors = GUINT32_FROM_LE (strf->imp_colors);
401 #endif
402
403   /* size checking */
404   *data = NULL;
405   if (strf->size > info.size) {
406     GST_WARNING_OBJECT (element,
407         "strf_vids header gave %d bytes data, only %" G_GSIZE_FORMAT
408         " available", strf->size, info.size);
409     strf->size = info.size;
410   }
411   if (sizeof (gst_riff_strf_vids) < info.size) {
412     *data =
413         gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL,
414         sizeof (gst_riff_strf_vids), info.size - sizeof (gst_riff_strf_vids));
415   }
416   gst_buffer_unref (buf);
417
418   /* debug */
419   GST_INFO_OBJECT (element, "strf tag found in context vids:");
420   GST_INFO_OBJECT (element, " size        %d", strf->size);
421   GST_INFO_OBJECT (element, " width       %d", strf->width);
422   GST_INFO_OBJECT (element, " height      %d", strf->height);
423   GST_INFO_OBJECT (element, " planes      %d", strf->planes);
424   GST_INFO_OBJECT (element, " bit_cnt     %d", strf->bit_cnt);
425   GST_INFO_OBJECT (element, " compression %" GST_FOURCC_FORMAT,
426       GST_FOURCC_ARGS (strf->compression));
427   GST_INFO_OBJECT (element, " image_size  %d", strf->image_size);
428   GST_INFO_OBJECT (element, " xpels_meter %d", strf->xpels_meter);
429   GST_INFO_OBJECT (element, " ypels_meter %d", strf->ypels_meter);
430   GST_INFO_OBJECT (element, " num_colors  %d", strf->num_colors);
431   GST_INFO_OBJECT (element, " imp_colors  %d", strf->imp_colors);
432   if (*data)
433     GST_INFO_OBJECT (element, " %" G_GSIZE_FORMAT " bytes extradata",
434         gst_buffer_get_size (*data));
435
436   *_strf = strf;
437
438   return TRUE;
439
440   /* ERRORS */
441 too_small:
442   {
443     GST_ERROR_OBJECT (element,
444         "Too small strf_vids (%" G_GSIZE_FORMAT " available, %d needed)",
445         info.size, (int) sizeof (gst_riff_strf_vids));
446     gst_buffer_unmap (buf, &info);
447     gst_buffer_unref (buf);
448     return FALSE;
449   }
450 }
451
452 /**
453  * gst_riff_parse_strf_auds:
454  * @element: caller element (used for debugging/error).
455  * @buf: input data to be used for parsing, stripped from header.
456  * @strf: a pointer (returned by this function) to a filled-in
457  *        strf/auds structure. Caller should free it.
458  * @data: a pointer (returned by this function) to a buffer
459  *        containing extradata for this particular stream (e.g.
460  *        codec initialization data).
461  *
462  * Parses an audio stream´s strf structure plus optionally some
463  * extradata from input data. This function takes ownership of @buf.
464  * use.
465  *
466  * Returns: TRUE if parsing succeeded, otherwise FALSE. The stream
467  *          should be skipped on error, but it is not fatal.
468  */
469 gboolean
470 gst_riff_parse_strf_auds (GstElement * element,
471     GstBuffer * buf, gst_riff_strf_auds ** _strf, GstBuffer ** data)
472 {
473   gst_riff_strf_auds *strf;
474   GstMapInfo info;
475
476   g_return_val_if_fail (buf != NULL, FALSE);
477   g_return_val_if_fail (_strf != NULL, FALSE);
478   g_return_val_if_fail (data != NULL, FALSE);
479
480   gst_buffer_map (buf, &info, GST_MAP_READ);
481   if (info.size < sizeof (gst_riff_strf_auds))
482     goto too_small;
483
484   strf = g_memdup (info.data, info.size);
485
486 #if (G_BYTE_ORDER == G_BIG_ENDIAN)
487   strf->format = GUINT16_FROM_LE (strf->format);
488   strf->channels = GUINT16_FROM_LE (strf->channels);
489   strf->rate = GUINT32_FROM_LE (strf->rate);
490   strf->av_bps = GUINT32_FROM_LE (strf->av_bps);
491   strf->blockalign = GUINT16_FROM_LE (strf->blockalign);
492   strf->bits_per_sample = GUINT16_FROM_LE (strf->bits_per_sample);
493 #endif
494
495   /* size checking */
496   *data = NULL;
497   if (info.size > sizeof (gst_riff_strf_auds) + 2) {
498     gint len;
499
500     len = GST_READ_UINT16_LE (&info.data[16]);
501     if (len + 2 + sizeof (gst_riff_strf_auds) > info.size) {
502       GST_WARNING_OBJECT (element,
503           "Extradata indicated %d bytes, but only %" G_GSIZE_FORMAT
504           " available", len, info.size - 2 - sizeof (gst_riff_strf_auds));
505       len = info.size - 2 - sizeof (gst_riff_strf_auds);
506     }
507     if (len)
508       *data = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL,
509           sizeof (gst_riff_strf_auds) + 2, len);
510   }
511
512   /* debug */
513   GST_INFO_OBJECT (element, "strf tag found in context auds:");
514   GST_INFO_OBJECT (element, " format      %d", strf->format);
515   GST_INFO_OBJECT (element, " channels    %d", strf->channels);
516   GST_INFO_OBJECT (element, " rate        %d", strf->rate);
517   GST_INFO_OBJECT (element, " av_bps      %d", strf->av_bps);
518   GST_INFO_OBJECT (element, " blockalign  %d", strf->blockalign);
519   GST_INFO_OBJECT (element, " bits/sample %d", strf->bits_per_sample);
520   if (*data)
521     GST_INFO_OBJECT (element, " %" G_GSIZE_FORMAT " bytes extradata",
522         gst_buffer_get_size (*data));
523
524   gst_buffer_unmap (buf, &info);
525   gst_buffer_unref (buf);
526
527   *_strf = strf;
528
529   return TRUE;
530
531   /* ERROR */
532 too_small:
533   {
534     GST_ERROR_OBJECT (element,
535         "Too small strf_auds (%" G_GSIZE_FORMAT " available"
536         ", %" G_GSIZE_FORMAT " needed)", info.size,
537         sizeof (gst_riff_strf_auds));
538     gst_buffer_unmap (buf, &info);
539     gst_buffer_unref (buf);
540     return FALSE;
541   }
542 }
543
544 /**
545  * gst_riff_parse_strf_iavs:
546  * @element: caller element (used for debugging/error).
547  * @buf: input data to be used for parsing, stripped from header.
548  * @strf: a pointer (returned by this function) to a filled-in
549  *        strf/iavs structure. Caller should free it.
550  * @data: a pointer (returned by this function) to a buffer
551  *        containing extradata for this particular stream (e.g.
552  *        codec initialization data).
553  *
554  * Parses a interleaved (also known as "complex")  stream´s strf
555  * structure plus optionally some extradata from input data. This
556  * function takes ownership of @buf.
557  *
558  * Returns: TRUE if parsing succeeded, otherwise FALSE.
559  */
560
561 gboolean
562 gst_riff_parse_strf_iavs (GstElement * element,
563     GstBuffer * buf, gst_riff_strf_iavs ** _strf, GstBuffer ** data)
564 {
565   gst_riff_strf_iavs *strf;
566   GstMapInfo info;
567
568   g_return_val_if_fail (buf != NULL, FALSE);
569   g_return_val_if_fail (_strf != NULL, FALSE);
570   g_return_val_if_fail (data != NULL, FALSE);
571
572   gst_buffer_map (buf, &info, GST_MAP_READ);
573   if (info.size < sizeof (gst_riff_strf_iavs))
574     goto too_small;
575
576   strf = g_memdup (info.data, info.size);
577   gst_buffer_unmap (buf, &info);
578
579   gst_buffer_unref (buf);
580
581 #if (G_BYTE_ORDER == G_BIG_ENDIAN)
582   strf->DVAAuxSrc = GUINT32_FROM_LE (strf->DVAAuxSrc);
583   strf->DVAAuxCtl = GUINT32_FROM_LE (strf->DVAAuxCtl);
584   strf->DVAAuxSrc1 = GUINT32_FROM_LE (strf->DVAAuxSrc1);
585   strf->DVAAuxCtl1 = GUINT32_FROM_LE (strf->DVAAuxCtl1);
586   strf->DVVAuxSrc = GUINT32_FROM_LE (strf->DVVAuxSrc);
587   strf->DVVAuxCtl = GUINT32_FROM_LE (strf->DVVAuxCtl);
588   strf->DVReserved1 = GUINT32_FROM_LE (strf->DVReserved1);
589   strf->DVReserved2 = GUINT32_FROM_LE (strf->DVReserved2);
590 #endif
591
592   /* debug */
593   GST_INFO_OBJECT (element, "strf tag found in context iavs:");
594   GST_INFO_OBJECT (element, " DVAAuxSrc   %08x", strf->DVAAuxSrc);
595   GST_INFO_OBJECT (element, " DVAAuxCtl   %08x", strf->DVAAuxCtl);
596   GST_INFO_OBJECT (element, " DVAAuxSrc1  %08x", strf->DVAAuxSrc1);
597   GST_INFO_OBJECT (element, " DVAAuxCtl1  %08x", strf->DVAAuxCtl1);
598   GST_INFO_OBJECT (element, " DVVAuxSrc   %08x", strf->DVVAuxSrc);
599   GST_INFO_OBJECT (element, " DVVAuxCtl   %08x", strf->DVVAuxCtl);
600   GST_INFO_OBJECT (element, " DVReserved1 %08x", strf->DVReserved1);
601   GST_INFO_OBJECT (element, " DVReserved2 %08x", strf->DVReserved2);
602
603   *_strf = strf;
604   *data = NULL;
605
606   return TRUE;
607
608   /* ERRORS */
609 too_small:
610   {
611     GST_ERROR_OBJECT (element,
612         "Too small strf_iavs (%" G_GSIZE_FORMAT "available"
613         ", %" G_GSIZE_FORMAT " needed)", info.size,
614         sizeof (gst_riff_strf_iavs));
615     gst_buffer_unmap (buf, &info);
616     gst_buffer_unref (buf);
617     return FALSE;
618   }
619 }
620
621 static void
622 parse_tag_value (GstElement * element, GstTagList * taglist, const gchar * type,
623     guint8 * ptr, guint tsize)
624 {
625   static const gchar *env_vars[] = { "GST_AVI_TAG_ENCODING",
626     "GST_RIFF_TAG_ENCODING", "GST_TAG_ENCODING", NULL
627   };
628   GType tag_type;
629   gchar *val;
630
631   tag_type = gst_tag_get_type (type);
632   val = gst_tag_freeform_string_to_utf8 ((gchar *) ptr, tsize, env_vars);
633
634   if (val != NULL) {
635     if (tag_type == G_TYPE_STRING) {
636       gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, type, val, NULL);
637     } else {
638       GValue tag_val = { 0, };
639
640       g_value_init (&tag_val, tag_type);
641       if (gst_value_deserialize (&tag_val, val)) {
642         gst_tag_list_add_value (taglist, GST_TAG_MERGE_APPEND, type, &tag_val);
643       } else {
644         GST_WARNING_OBJECT (element, "could not deserialize '%s' into a "
645             "tag %s of type %s", val, type, g_type_name (tag_type));
646       }
647       g_value_unset (&tag_val);
648     }
649     g_free (val);
650   } else {
651     GST_WARNING_OBJECT (element, "could not extract %s tag", type);
652   }
653 }
654
655 /**
656  * gst_riff_parse_info:
657  * @element: caller element (used for debugging/error).
658  * @buf: input data to be used for parsing, stripped from header.
659  * @taglist: a pointer to a taglist (returned by this function)
660  *           containing information about this stream. May be
661  *           NULL if no supported tags were found.
662  *
663  * Parses stream metadata from input data.
664  */
665 void
666 gst_riff_parse_info (GstElement * element,
667     GstBuffer * buf, GstTagList ** _taglist)
668 {
669   GstMapInfo info;
670   guint8 *ptr;
671   gsize left;
672   guint tsize;
673   guint32 tag;
674   const gchar *type;
675   GstTagList *taglist;
676
677   g_return_if_fail (_taglist != NULL);
678
679   if (!buf) {
680     *_taglist = NULL;
681     return;
682   }
683   gst_buffer_map (buf, &info, GST_MAP_READ);
684
685   taglist = gst_tag_list_new_empty ();
686
687   ptr = info.data;
688   left = info.size;
689
690   while (left > 8) {
691     tag = GST_READ_UINT32_LE (ptr);
692     tsize = GST_READ_UINT32_LE (ptr + 4);
693
694     GST_MEMDUMP_OBJECT (element, "tag chunk", ptr, MIN (tsize + 8, left));
695
696     left -= 8;
697     ptr += 8;
698
699     GST_DEBUG ("tag %" GST_FOURCC_FORMAT ", size %u",
700         GST_FOURCC_ARGS (tag), tsize);
701
702     if (tsize > left) {
703       GST_WARNING_OBJECT (element,
704           "Tagsize %d is larger than available data %" G_GSIZE_FORMAT,
705           tsize, left);
706       tsize = left;
707     }
708
709     /* make uppercase */
710     tag = tag & 0xDFDFDFDF;
711
712     /* find out the type of metadata */
713     switch (tag) {
714       case GST_RIFF_INFO_IARL:
715         type = GST_TAG_LOCATION;
716         break;
717       case GST_RIFF_INFO_IAAR:
718         type = GST_TAG_ALBUM_ARTIST;
719         break;
720       case GST_RIFF_INFO_IART:
721         type = GST_TAG_ARTIST;
722         break;
723       case GST_RIFF_INFO_ICMS:
724         type = NULL;            /*"Commissioner"; */
725         break;
726       case GST_RIFF_INFO_ICMT:
727         type = GST_TAG_COMMENT;
728         break;
729       case GST_RIFF_INFO_ICOP:
730         type = GST_TAG_COPYRIGHT;
731         break;
732       case GST_RIFF_INFO_ICRD:
733         type = GST_TAG_DATE_TIME;
734         break;
735       case GST_RIFF_INFO_ICRP:
736         type = NULL;            /*"Cropped"; */
737         break;
738       case GST_RIFF_INFO_IDIM:
739         type = NULL;            /*"Dimensions"; */
740         break;
741       case GST_RIFF_INFO_IDPI:
742         type = NULL;            /*"Dots per Inch"; */
743         break;
744       case GST_RIFF_INFO_IENG:
745         type = NULL;            /*"Engineer"; */
746         break;
747       case GST_RIFF_INFO_IGNR:
748         type = GST_TAG_GENRE;
749         break;
750       case GST_RIFF_INFO_IKEY:
751         type = GST_TAG_KEYWORDS;
752         break;
753       case GST_RIFF_INFO_ILGT:
754         type = NULL;            /*"Lightness"; */
755         break;
756       case GST_RIFF_INFO_IMED:
757         type = NULL;            /*"Medium"; */
758         break;
759       case GST_RIFF_INFO_INAM:
760         type = GST_TAG_TITLE;
761         break;
762       case GST_RIFF_INFO_IPLT:
763         type = NULL;            /*"Palette"; */
764         break;
765       case GST_RIFF_INFO_IPRD:
766         type = GST_TAG_ALBUM;
767         break;
768       case GST_RIFF_INFO_ISBJ:
769         type = GST_TAG_ALBUM_ARTIST;
770         break;
771       case GST_RIFF_INFO_ISFT:
772         type = GST_TAG_ENCODER;
773         break;
774       case GST_RIFF_INFO_ISHP:
775         type = NULL;            /*"Sharpness"; */
776         break;
777       case GST_RIFF_INFO_ISRC:
778         type = GST_TAG_ISRC;
779         break;
780       case GST_RIFF_INFO_ISRF:
781         type = NULL;            /*"Source Form"; */
782         break;
783       case GST_RIFF_INFO_ITCH:
784         type = NULL;            /*"Technician"; */
785         break;
786       case GST_RIFF_INFO_ITRK:
787         type = GST_TAG_TRACK_NUMBER;
788         break;
789       default:
790         type = NULL;
791         GST_WARNING_OBJECT (element,
792             "Unknown INFO (metadata) tag entry %" GST_FOURCC_FORMAT,
793             GST_FOURCC_ARGS (tag));
794         break;
795     }
796
797     if (type != NULL && ptr[0] != '\0') {
798       GST_DEBUG_OBJECT (element, "mapped tag %" GST_FOURCC_FORMAT " to tag %s",
799           GST_FOURCC_ARGS (tag), type);
800
801       parse_tag_value (element, taglist, type, ptr, tsize);
802     }
803
804     if (tsize & 1) {
805       tsize++;
806       if (tsize > left)
807         tsize = left;
808     }
809
810     ptr += tsize;
811     left -= tsize;
812   }
813
814   if (!gst_tag_list_is_empty (taglist)) {
815     GST_INFO_OBJECT (element, "extracted tags: %" GST_PTR_FORMAT, taglist);
816     *_taglist = taglist;
817   } else {
818     *_taglist = NULL;
819     gst_tag_list_unref (taglist);
820   }
821   gst_buffer_unmap (buf, &info);
822
823   return;
824 }