• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
gst_riff_read_chunk(GstElement * element,GstPad * pad,guint64 * _offset,guint32 * tag,GstBuffer ** _chunk_data)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
gst_riff_parse_chunk(GstElement * element,GstBuffer * buf,guint * _offset,guint32 * _fourcc,GstBuffer ** chunk_data)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
gst_riff_parse_file_header(GstElement * element,GstBuffer * buf,guint32 * doctype)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       && tag != GST_RIFF_TAG_RF64)
240     goto not_riff;
241 
242   *doctype = GST_READ_UINT32_LE (info.data + 8);
243   gst_buffer_unmap (buf, &info);
244 
245   gst_buffer_unref (buf);
246 
247   return TRUE;
248 
249   /* ERRORS */
250 too_small:
251   {
252     GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
253         ("Not enough data to parse RIFF header (%" G_GSIZE_FORMAT " available,"
254             " %d needed)", info.size, 12));
255     gst_buffer_unmap (buf, &info);
256     gst_buffer_unref (buf);
257     return FALSE;
258   }
259 not_riff:
260   {
261     GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
262         ("Stream is no RIFF stream: 0x%" G_GINT32_MODIFIER "x", tag));
263     gst_buffer_unmap (buf, &info);
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
gst_riff_parse_strh(GstElement * element,GstBuffer * buf,gst_riff_strh ** _strh)283 gst_riff_parse_strh (GstElement * element,
284     GstBuffer * buf, gst_riff_strh ** _strh)
285 {
286   gst_riff_strh *strh;
287   GstMapInfo info;
288 
289   g_return_val_if_fail (buf != NULL, FALSE);
290   g_return_val_if_fail (_strh != NULL, FALSE);
291 
292   gst_buffer_map (buf, &info, GST_MAP_READ);
293   if (info.size < sizeof (gst_riff_strh))
294     goto too_small;
295 
296   strh = g_memdup2 (info.data, info.size);
297   gst_buffer_unmap (buf, &info);
298 
299   gst_buffer_unref (buf);
300 
301 #if (G_BYTE_ORDER == G_BIG_ENDIAN)
302   strh->type = GUINT32_FROM_LE (strh->type);
303   strh->fcc_handler = GUINT32_FROM_LE (strh->fcc_handler);
304   strh->flags = GUINT32_FROM_LE (strh->flags);
305   strh->priority = GUINT32_FROM_LE (strh->priority);
306   strh->init_frames = GUINT32_FROM_LE (strh->init_frames);
307   strh->scale = GUINT32_FROM_LE (strh->scale);
308   strh->rate = GUINT32_FROM_LE (strh->rate);
309   strh->start = GUINT32_FROM_LE (strh->start);
310   strh->length = GUINT32_FROM_LE (strh->length);
311   strh->bufsize = GUINT32_FROM_LE (strh->bufsize);
312   strh->quality = GUINT32_FROM_LE (strh->quality);
313   strh->samplesize = GUINT32_FROM_LE (strh->samplesize);
314 #endif
315 
316   /* avoid divisions by zero */
317   if (!strh->scale)
318     strh->scale = 1;
319   if (!strh->rate)
320     strh->rate = 1;
321 
322   /* debug */
323   GST_INFO_OBJECT (element, "strh tag found:");
324   GST_INFO_OBJECT (element, " type        %" GST_FOURCC_FORMAT,
325       GST_FOURCC_ARGS (strh->type));
326   GST_INFO_OBJECT (element, " fcc_handler %" GST_FOURCC_FORMAT,
327       GST_FOURCC_ARGS (strh->fcc_handler));
328   GST_INFO_OBJECT (element, " flags       0x%08x", strh->flags);
329   GST_INFO_OBJECT (element, " priority    %d", strh->priority);
330   GST_INFO_OBJECT (element, " init_frames %d", strh->init_frames);
331   GST_INFO_OBJECT (element, " scale       %d", strh->scale);
332   GST_INFO_OBJECT (element, " rate        %d", strh->rate);
333   GST_INFO_OBJECT (element, " start       %d", strh->start);
334   GST_INFO_OBJECT (element, " length      %d", strh->length);
335   GST_INFO_OBJECT (element, " bufsize     %d", strh->bufsize);
336   GST_INFO_OBJECT (element, " quality     %d", strh->quality);
337   GST_INFO_OBJECT (element, " samplesize  %d", strh->samplesize);
338 
339   *_strh = strh;
340 
341   return TRUE;
342 
343   /* ERRORS */
344 too_small:
345   {
346     GST_ERROR_OBJECT (element,
347         "Too small strh (%" G_GSIZE_FORMAT " available, %d needed)",
348         info.size, (int) sizeof (gst_riff_strh));
349     gst_buffer_unmap (buf, &info);
350     gst_buffer_unref (buf);
351     return FALSE;
352   }
353 }
354 
355 /**
356  * gst_riff_parse_strf_vids:
357  * @element: caller element (used for debugging/error).
358  * @buf: input data to be used for parsing, stripped from header.
359  * @strf: a pointer (returned by this function) to a filled-in
360  *        strf/vids structure. Caller should free it.
361  * @data: a pointer (returned by this function) to a buffer
362  *        containing extradata for this particular stream (e.g.
363  *        palette, codec initialization data).
364  *
365  * Parses a video stream's strf structure plus optionally some
366  * extradata from input data. This function takes ownership of @buf.
367  *
368  * Returns: TRUE if parsing succeeded, otherwise FALSE. The stream
369  *          should be skipped on error, but it is not fatal.
370  */
371 
372 gboolean
gst_riff_parse_strf_vids(GstElement * element,GstBuffer * buf,gst_riff_strf_vids ** _strf,GstBuffer ** data)373 gst_riff_parse_strf_vids (GstElement * element,
374     GstBuffer * buf, gst_riff_strf_vids ** _strf, GstBuffer ** data)
375 {
376   gst_riff_strf_vids *strf;
377   GstMapInfo info;
378 
379   g_return_val_if_fail (buf != NULL, FALSE);
380   g_return_val_if_fail (_strf != NULL, FALSE);
381   g_return_val_if_fail (data != NULL, FALSE);
382 
383   gst_buffer_map (buf, &info, GST_MAP_READ);
384   if (info.size < sizeof (gst_riff_strf_vids))
385     goto too_small;
386 
387   strf = g_memdup2 (info.data, info.size);
388   gst_buffer_unmap (buf, &info);
389 
390 #if (G_BYTE_ORDER == G_BIG_ENDIAN)
391   strf->size = GUINT32_FROM_LE (strf->size);
392   strf->width = GUINT32_FROM_LE (strf->width);
393   strf->height = GUINT32_FROM_LE (strf->height);
394   strf->planes = GUINT16_FROM_LE (strf->planes);
395   strf->bit_cnt = GUINT16_FROM_LE (strf->bit_cnt);
396   strf->compression = GUINT32_FROM_LE (strf->compression);
397   strf->image_size = GUINT32_FROM_LE (strf->image_size);
398   strf->xpels_meter = GUINT32_FROM_LE (strf->xpels_meter);
399   strf->ypels_meter = GUINT32_FROM_LE (strf->ypels_meter);
400   strf->num_colors = GUINT32_FROM_LE (strf->num_colors);
401   strf->imp_colors = GUINT32_FROM_LE (strf->imp_colors);
402 #endif
403 
404   /* size checking */
405   *data = NULL;
406   if (strf->size > info.size) {
407     GST_WARNING_OBJECT (element,
408         "strf_vids header gave %d bytes data, only %" G_GSIZE_FORMAT
409         " available", strf->size, info.size);
410     strf->size = info.size;
411   }
412   if (sizeof (gst_riff_strf_vids) < info.size) {
413     *data =
414         gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL,
415         sizeof (gst_riff_strf_vids), info.size - sizeof (gst_riff_strf_vids));
416   }
417   gst_buffer_unref (buf);
418 
419   /* debug */
420   GST_INFO_OBJECT (element, "strf tag found in context vids:");
421   GST_INFO_OBJECT (element, " size        %d", strf->size);
422   GST_INFO_OBJECT (element, " width       %d", strf->width);
423   GST_INFO_OBJECT (element, " height      %d", strf->height);
424   GST_INFO_OBJECT (element, " planes      %d", strf->planes);
425   GST_INFO_OBJECT (element, " bit_cnt     %d", strf->bit_cnt);
426   GST_INFO_OBJECT (element, " compression %" GST_FOURCC_FORMAT,
427       GST_FOURCC_ARGS (strf->compression));
428   GST_INFO_OBJECT (element, " image_size  %d", strf->image_size);
429   GST_INFO_OBJECT (element, " xpels_meter %d", strf->xpels_meter);
430   GST_INFO_OBJECT (element, " ypels_meter %d", strf->ypels_meter);
431   GST_INFO_OBJECT (element, " num_colors  %d", strf->num_colors);
432   GST_INFO_OBJECT (element, " imp_colors  %d", strf->imp_colors);
433   if (*data)
434     GST_INFO_OBJECT (element, " %" G_GSIZE_FORMAT " bytes extradata",
435         gst_buffer_get_size (*data));
436 
437   *_strf = strf;
438 
439   return TRUE;
440 
441   /* ERRORS */
442 too_small:
443   {
444     GST_ERROR_OBJECT (element,
445         "Too small strf_vids (%" G_GSIZE_FORMAT " available, %d needed)",
446         info.size, (int) sizeof (gst_riff_strf_vids));
447     gst_buffer_unmap (buf, &info);
448     gst_buffer_unref (buf);
449     return FALSE;
450   }
451 }
452 
453 /**
454  * gst_riff_parse_strf_auds:
455  * @element: caller element (used for debugging/error).
456  * @buf: input data to be used for parsing, stripped from header.
457  * @strf: a pointer (returned by this function) to a filled-in
458  *        strf/auds structure. Caller should free it.
459  * @data: a pointer (returned by this function) to a buffer
460  *        containing extradata for this particular stream (e.g.
461  *        codec initialization data).
462  *
463  * Parses an audio stream's strf structure plus optionally some
464  * extradata from input data. This function takes ownership of @buf.
465  * use.
466  *
467  * Returns: TRUE if parsing succeeded, otherwise FALSE. The stream
468  *          should be skipped on error, but it is not fatal.
469  */
470 gboolean
gst_riff_parse_strf_auds(GstElement * element,GstBuffer * buf,gst_riff_strf_auds ** _strf,GstBuffer ** data)471 gst_riff_parse_strf_auds (GstElement * element,
472     GstBuffer * buf, gst_riff_strf_auds ** _strf, GstBuffer ** data)
473 {
474   gst_riff_strf_auds *strf;
475   GstMapInfo info;
476 
477   g_return_val_if_fail (buf != NULL, FALSE);
478   g_return_val_if_fail (_strf != NULL, FALSE);
479   g_return_val_if_fail (data != NULL, FALSE);
480 
481   gst_buffer_map (buf, &info, GST_MAP_READ);
482   if (info.size < sizeof (gst_riff_strf_auds))
483     goto too_small;
484 
485   strf = g_memdup2 (info.data, info.size);
486 
487 #if (G_BYTE_ORDER == G_BIG_ENDIAN)
488   strf->format = GUINT16_FROM_LE (strf->format);
489   strf->channels = GUINT16_FROM_LE (strf->channels);
490   strf->rate = GUINT32_FROM_LE (strf->rate);
491   strf->av_bps = GUINT32_FROM_LE (strf->av_bps);
492   strf->blockalign = GUINT16_FROM_LE (strf->blockalign);
493   strf->bits_per_sample = GUINT16_FROM_LE (strf->bits_per_sample);
494 #endif
495 
496   /* size checking */
497   *data = NULL;
498   if (info.size > sizeof (gst_riff_strf_auds) + 2) {
499     gint len;
500 
501     len = GST_READ_UINT16_LE (&info.data[16]);
502     if (len + 2 + sizeof (gst_riff_strf_auds) > info.size) {
503       GST_WARNING_OBJECT (element,
504           "Extradata indicated %d bytes, but only %" G_GSIZE_FORMAT
505           " available", len, info.size - 2 - sizeof (gst_riff_strf_auds));
506       len = info.size - 2 - sizeof (gst_riff_strf_auds);
507     }
508     if (len)
509       *data = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL,
510           sizeof (gst_riff_strf_auds) + 2, len);
511   }
512 
513   /* debug */
514   GST_INFO_OBJECT (element, "strf tag found in context auds:");
515   GST_INFO_OBJECT (element, " format      %d", strf->format);
516   GST_INFO_OBJECT (element, " channels    %d", strf->channels);
517   GST_INFO_OBJECT (element, " rate        %d", strf->rate);
518   GST_INFO_OBJECT (element, " av_bps      %d", strf->av_bps);
519   GST_INFO_OBJECT (element, " blockalign  %d", strf->blockalign);
520   GST_INFO_OBJECT (element, " bits/sample %d", strf->bits_per_sample);
521   if (*data)
522     GST_INFO_OBJECT (element, " %" G_GSIZE_FORMAT " bytes extradata",
523         gst_buffer_get_size (*data));
524 
525   gst_buffer_unmap (buf, &info);
526   gst_buffer_unref (buf);
527 
528   *_strf = strf;
529 
530   return TRUE;
531 
532   /* ERROR */
533 too_small:
534   {
535     GST_ERROR_OBJECT (element,
536         "Too small strf_auds (%" G_GSIZE_FORMAT " available"
537         ", %" G_GSIZE_FORMAT " needed)", info.size,
538         sizeof (gst_riff_strf_auds));
539     gst_buffer_unmap (buf, &info);
540     gst_buffer_unref (buf);
541     return FALSE;
542   }
543 }
544 
545 /**
546  * gst_riff_parse_strf_iavs:
547  * @element: caller element (used for debugging/error).
548  * @buf: input data to be used for parsing, stripped from header.
549  * @strf: a pointer (returned by this function) to a filled-in
550  *        strf/iavs structure. Caller should free it.
551  * @data: a pointer (returned by this function) to a buffer
552  *        containing extradata for this particular stream (e.g.
553  *        codec initialization data).
554  *
555  * Parses a interleaved (also known as "complex")  stream´s strf
556  * structure plus optionally some extradata from input data. This
557  * function takes ownership of @buf.
558  *
559  * Returns: TRUE if parsing succeeded, otherwise FALSE.
560  */
561 
562 gboolean
gst_riff_parse_strf_iavs(GstElement * element,GstBuffer * buf,gst_riff_strf_iavs ** _strf,GstBuffer ** data)563 gst_riff_parse_strf_iavs (GstElement * element,
564     GstBuffer * buf, gst_riff_strf_iavs ** _strf, GstBuffer ** data)
565 {
566   gst_riff_strf_iavs *strf;
567   GstMapInfo info;
568 
569   g_return_val_if_fail (buf != NULL, FALSE);
570   g_return_val_if_fail (_strf != NULL, FALSE);
571   g_return_val_if_fail (data != NULL, FALSE);
572 
573   gst_buffer_map (buf, &info, GST_MAP_READ);
574   if (info.size < sizeof (gst_riff_strf_iavs))
575     goto too_small;
576 
577   strf = g_memdup2 (info.data, info.size);
578   gst_buffer_unmap (buf, &info);
579 
580   gst_buffer_unref (buf);
581 
582 #if (G_BYTE_ORDER == G_BIG_ENDIAN)
583   strf->DVAAuxSrc = GUINT32_FROM_LE (strf->DVAAuxSrc);
584   strf->DVAAuxCtl = GUINT32_FROM_LE (strf->DVAAuxCtl);
585   strf->DVAAuxSrc1 = GUINT32_FROM_LE (strf->DVAAuxSrc1);
586   strf->DVAAuxCtl1 = GUINT32_FROM_LE (strf->DVAAuxCtl1);
587   strf->DVVAuxSrc = GUINT32_FROM_LE (strf->DVVAuxSrc);
588   strf->DVVAuxCtl = GUINT32_FROM_LE (strf->DVVAuxCtl);
589   strf->DVReserved1 = GUINT32_FROM_LE (strf->DVReserved1);
590   strf->DVReserved2 = GUINT32_FROM_LE (strf->DVReserved2);
591 #endif
592 
593   /* debug */
594   GST_INFO_OBJECT (element, "strf tag found in context iavs:");
595   GST_INFO_OBJECT (element, " DVAAuxSrc   %08x", strf->DVAAuxSrc);
596   GST_INFO_OBJECT (element, " DVAAuxCtl   %08x", strf->DVAAuxCtl);
597   GST_INFO_OBJECT (element, " DVAAuxSrc1  %08x", strf->DVAAuxSrc1);
598   GST_INFO_OBJECT (element, " DVAAuxCtl1  %08x", strf->DVAAuxCtl1);
599   GST_INFO_OBJECT (element, " DVVAuxSrc   %08x", strf->DVVAuxSrc);
600   GST_INFO_OBJECT (element, " DVVAuxCtl   %08x", strf->DVVAuxCtl);
601   GST_INFO_OBJECT (element, " DVReserved1 %08x", strf->DVReserved1);
602   GST_INFO_OBJECT (element, " DVReserved2 %08x", strf->DVReserved2);
603 
604   *_strf = strf;
605   *data = NULL;
606 
607   return TRUE;
608 
609   /* ERRORS */
610 too_small:
611   {
612     GST_ERROR_OBJECT (element,
613         "Too small strf_iavs (%" G_GSIZE_FORMAT "available"
614         ", %" G_GSIZE_FORMAT " needed)", info.size,
615         sizeof (gst_riff_strf_iavs));
616     gst_buffer_unmap (buf, &info);
617     gst_buffer_unref (buf);
618     return FALSE;
619   }
620 }
621 
622 static void
parse_tag_value(GstElement * element,GstTagList * taglist,const gchar * type,guint8 * ptr,guint tsize)623 parse_tag_value (GstElement * element, GstTagList * taglist, const gchar * type,
624     guint8 * ptr, guint tsize)
625 {
626   static const gchar *env_vars[] = { "GST_AVI_TAG_ENCODING",
627     "GST_RIFF_TAG_ENCODING", "GST_TAG_ENCODING", NULL
628   };
629   GType tag_type;
630   gchar *val;
631 
632   tag_type = gst_tag_get_type (type);
633   val = gst_tag_freeform_string_to_utf8 ((gchar *) ptr, tsize, env_vars);
634 
635   if (val != NULL) {
636     if (tag_type == G_TYPE_STRING) {
637       gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, type, val, NULL);
638     } else {
639       GValue tag_val = { 0, };
640 
641       g_value_init (&tag_val, tag_type);
642       if (gst_value_deserialize (&tag_val, val)) {
643         gst_tag_list_add_value (taglist, GST_TAG_MERGE_APPEND, type, &tag_val);
644       } else {
645         GST_WARNING_OBJECT (element, "could not deserialize '%s' into a "
646             "tag %s of type %s", val, type, g_type_name (tag_type));
647       }
648       g_value_unset (&tag_val);
649     }
650     g_free (val);
651   } else {
652     GST_WARNING_OBJECT (element, "could not extract %s tag", type);
653   }
654 }
655 
656 /**
657  * gst_riff_parse_info:
658  * @element: caller element (used for debugging/error).
659  * @buf: input data to be used for parsing, stripped from header.
660  * @taglist: a pointer to a taglist (returned by this function)
661  *           containing information about this stream. May be
662  *           NULL if no supported tags were found.
663  *
664  * Parses stream metadata from input data.
665  */
666 void
gst_riff_parse_info(GstElement * element,GstBuffer * buf,GstTagList ** _taglist)667 gst_riff_parse_info (GstElement * element,
668     GstBuffer * buf, GstTagList ** _taglist)
669 {
670   GstMapInfo info;
671   guint8 *ptr;
672   gsize left;
673   guint tsize;
674   guint32 tag;
675   const gchar *type;
676   GstTagList *taglist;
677 
678   g_return_if_fail (_taglist != NULL);
679 
680   if (!buf) {
681     *_taglist = NULL;
682     return;
683   }
684   gst_buffer_map (buf, &info, GST_MAP_READ);
685 
686   taglist = gst_tag_list_new_empty ();
687 
688   ptr = info.data;
689   left = info.size;
690 
691   while (left > 8) {
692     tag = GST_READ_UINT32_LE (ptr);
693     tsize = GST_READ_UINT32_LE (ptr + 4);
694 
695     GST_MEMDUMP_OBJECT (element, "tag chunk", ptr, MIN (tsize + 8, left));
696 
697     left -= 8;
698     ptr += 8;
699 
700     GST_DEBUG ("tag %" GST_FOURCC_FORMAT ", size %u",
701         GST_FOURCC_ARGS (tag), tsize);
702 
703     if (tsize > left) {
704       GST_WARNING_OBJECT (element,
705           "Tagsize %d is larger than available data %" G_GSIZE_FORMAT,
706           tsize, left);
707       tsize = left;
708     }
709 
710     /* make uppercase */
711     tag = tag & 0xDFDFDFDF;
712 
713     /* find out the type of metadata */
714     switch (tag) {
715       case GST_RIFF_INFO_IARL:
716         type = GST_TAG_LOCATION;
717         break;
718       case GST_RIFF_INFO_IAAR:
719         type = GST_TAG_ALBUM_ARTIST;
720         break;
721       case GST_RIFF_INFO_IART:
722         type = GST_TAG_ARTIST;
723         break;
724       case GST_RIFF_INFO_ICMS:
725         type = NULL;            /*"Commissioner"; */
726         break;
727       case GST_RIFF_INFO_ICMT:
728         type = GST_TAG_COMMENT;
729         break;
730       case GST_RIFF_INFO_ICOP:
731         type = GST_TAG_COPYRIGHT;
732         break;
733       case GST_RIFF_INFO_ICRD:
734         type = GST_TAG_DATE_TIME;
735         break;
736       case GST_RIFF_INFO_ICRP:
737         type = NULL;            /*"Cropped"; */
738         break;
739       case GST_RIFF_INFO_IDIM:
740         type = NULL;            /*"Dimensions"; */
741         break;
742       case GST_RIFF_INFO_IDPI:
743         type = NULL;            /*"Dots per Inch"; */
744         break;
745       case GST_RIFF_INFO_IENG:
746         type = NULL;            /*"Engineer"; */
747         break;
748       case GST_RIFF_INFO_IGNR:
749         type = GST_TAG_GENRE;
750         break;
751       case GST_RIFF_INFO_IKEY:
752         type = GST_TAG_KEYWORDS;
753         break;
754       case GST_RIFF_INFO_ILGT:
755         type = NULL;            /*"Lightness"; */
756         break;
757       case GST_RIFF_INFO_IMED:
758         type = NULL;            /*"Medium"; */
759         break;
760       case GST_RIFF_INFO_INAM:
761         type = GST_TAG_TITLE;
762         break;
763       case GST_RIFF_INFO_IPLT:
764         type = NULL;            /*"Palette"; */
765         break;
766       case GST_RIFF_INFO_IPRD:
767         type = GST_TAG_ALBUM;
768         break;
769       case GST_RIFF_INFO_ISBJ:
770         type = GST_TAG_ALBUM_ARTIST;
771         break;
772       case GST_RIFF_INFO_ISFT:
773         type = GST_TAG_ENCODER;
774         break;
775       case GST_RIFF_INFO_ISHP:
776         type = NULL;            /*"Sharpness"; */
777         break;
778       case GST_RIFF_INFO_ISRC:
779         type = GST_TAG_ISRC;
780         break;
781       case GST_RIFF_INFO_ISRF:
782         type = NULL;            /*"Source Form"; */
783         break;
784       case GST_RIFF_INFO_ITCH:
785         type = NULL;            /*"Technician"; */
786         break;
787       case GST_RIFF_INFO_ITRK:
788         type = GST_TAG_TRACK_NUMBER;
789         break;
790       default:
791         type = NULL;
792         GST_WARNING_OBJECT (element,
793             "Unknown INFO (metadata) tag entry %" GST_FOURCC_FORMAT,
794             GST_FOURCC_ARGS (tag));
795         break;
796     }
797 
798     if (type != NULL && ptr[0] != '\0') {
799       GST_DEBUG_OBJECT (element, "mapped tag %" GST_FOURCC_FORMAT " to tag %s",
800           GST_FOURCC_ARGS (tag), type);
801 
802       parse_tag_value (element, taglist, type, ptr, tsize);
803     }
804 
805     if (tsize & 1) {
806       tsize++;
807       if (tsize > left)
808         tsize = left;
809     }
810 
811     ptr += tsize;
812     left -= tsize;
813   }
814 
815   if (!gst_tag_list_is_empty (taglist)) {
816     GST_INFO_OBJECT (element, "extracted tags: %" GST_PTR_FORMAT, taglist);
817     *_taglist = taglist;
818   } else {
819     *_taglist = NULL;
820     gst_tag_list_unref (taglist);
821   }
822   gst_buffer_unmap (buf, &info);
823 
824   return;
825 }
826