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