eedfcc72234c8d29fba2a32fa5d11349bae9b08b
[platform/upstream/gstreamer.git] / libs / riff / gstriff.c
1 /* Gnome-Streamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
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
21 #include <gstriff.h>
22
23 //#define debug(format,args...) g_print(format,##args)
24 #define debug(format,args...)
25
26
27 GstRiff *gst_riff_new(GstRiffCallback function, gpointer data) {
28   GstRiff *riff;
29
30   riff = (GstRiff *)g_malloc(sizeof(GstRiff));
31   g_return_val_if_fail(riff != NULL, NULL);
32
33   riff->form = 0;
34   riff->chunks = NULL;
35   riff->state = 0;
36   riff->curoffset = 0;
37   riff->nextlikely = 0;
38         riff->new_tag_found = function;
39         riff->callback_data = data;
40         riff->incomplete_chunk = NULL;
41         riff->dataleft = NULL;
42
43   return riff;
44 }
45
46 gint gst_riff_next_buffer(GstRiff *riff,GstBuffer *buf,gulong off) {
47   gulong last, size;
48   GstRiffChunk *chunk;
49
50   g_return_val_if_fail(riff != NULL, GST_RIFF_EINVAL);
51   g_return_val_if_fail(buf != NULL, GST_RIFF_EINVAL);
52   g_return_val_if_fail(GST_BUFFER_DATA(buf) != NULL, GST_RIFF_EINVAL);
53
54         size = GST_BUFFER_SIZE(buf);
55   last = off + size;
56
57         debug("offset new buffer 0x%08lx size 0x%08x\n", off, GST_BUFFER_SIZE(buf));
58
59         if (riff->dataleft) {
60                 gulong newsize;
61
62           debug("recovering left data\n");
63                 newsize = riff->dataleft_size + size;
64                 riff->dataleft = g_realloc(riff->dataleft, newsize);
65                 memcpy(riff->dataleft+riff->dataleft_size, GST_BUFFER_DATA(buf), size);
66                 gst_buffer_unref(buf);
67
68                 buf = gst_buffer_new();
69                 GST_BUFFER_DATA(buf) = riff->dataleft;
70                 GST_BUFFER_SIZE(buf) = newsize;
71                 off -= riff->dataleft_size;
72                 //last -= riff->dataleft_size;
73           riff->dataleft = NULL;
74         }
75
76   if (off == 0) {
77     gulong *words = (gulong *)GST_BUFFER_DATA(buf);
78
79                 // don't even try to parse the head if it's not there FIXME
80                 if (last < 12) {
81       riff->state = GST_RIFF_ENOTRIFF;
82       return riff->state;
83                 }
84
85     //g_print("testing is 0x%08lx '%s'\n",words[0],gst_riff_id_to_fourcc(words[0]));
86     /* verify this is a valid RIFF file, first of all */
87     if (words[0] != GST_RIFF_TAG_RIFF) {
88       riff->state = GST_RIFF_ENOTRIFF;
89       return riff->state;
90     }
91     riff->form = words[2];
92     //g_print("form is 0x%08lx '%s'\n",words[2],gst_riff_id_to_fourcc(words[2]));
93     riff->nextlikely = 12;      /* skip 'RIFF', length, and form */
94                 // all OK here
95           riff->incomplete_chunk = NULL;
96   }
97
98         // if we have an incomplete chunk from the previous buffer
99         if (riff->incomplete_chunk) {
100                 guint leftover;
101                 debug("have incomplete chunk %08x filled\n", riff->incomplete_chunk_size);
102                 leftover = riff->incomplete_chunk->size - riff->incomplete_chunk_size;
103                 if (leftover <= size) {
104                   debug("we can fill it from %08x with %08x bytes = %08x\n", riff->incomplete_chunk_size, leftover, riff->incomplete_chunk_size+leftover);
105                         memcpy(riff->incomplete_chunk->data+riff->incomplete_chunk_size, GST_BUFFER_DATA(buf), leftover);
106
107                   if (riff->new_tag_found) {
108                     riff->new_tag_found(riff->incomplete_chunk, riff->callback_data);
109                   }
110             g_free(riff->incomplete_chunk->data);
111             g_free(riff->incomplete_chunk);
112             riff->incomplete_chunk = NULL;
113                 }
114                 else {
115                   debug("we cannot fill it %08x >= %08lx\n", leftover, size);
116                         memcpy(riff->incomplete_chunk->data+riff->incomplete_chunk_size, GST_BUFFER_DATA(buf), size);
117                   riff->incomplete_chunk_size += size;
118                         return 0;
119                 }
120         }
121
122   if ((riff->nextlikely+12) > last) {
123                 guint left = last - riff->nextlikely;
124     debug("not enough data next 0x%08x  last 0x%08lx %08x %08x\n",riff->nextlikely, last, left, off);
125
126                 riff->dataleft = g_malloc(left);
127                 riff->dataleft_size = left;
128                 memcpy(riff->dataleft, GST_BUFFER_DATA(buf)+size-left, left);
129
130                 return 0;
131         }
132
133   debug("next 0x%08x  last 0x%08lx offset %08x\n",riff->nextlikely, last, off);
134   /* loop while the next likely chunk header is in this buffer */
135   while ((riff->nextlikely+12) <= last) {
136     gulong *words = (gulong *)((guchar *)GST_BUFFER_DATA(buf) + riff->nextlikely - off );
137
138                 // loop over all of the chunks to check which one is finished
139                 while (riff->chunks) {
140                         chunk = g_list_nth_data(riff->chunks, 0);
141
142       debug("next 0x%08x  offset 0x%08lx size 0x%08x\n",riff->nextlikely, chunk->offset, chunk->size);
143                         if (riff->nextlikely >= chunk->offset+chunk->size) {
144         //g_print("found END LIST\n");
145                                 // we have the end of the chunk on the stack, remove it
146                                 riff->chunks = g_list_remove(riff->chunks, chunk);
147                         }
148                         else break;
149                 }
150
151     debug("next likely chunk is at offset 0x%08x\n",riff->nextlikely);
152
153     chunk = (GstRiffChunk *)g_malloc(sizeof(GstRiffChunk));
154     g_return_val_if_fail(chunk != NULL, GST_RIFF_ENOMEM);
155
156     chunk->offset = riff->nextlikely+8; /* point to the actual data */
157     chunk->id = words[0];
158     chunk->size = words[1];
159                 chunk->data = (gchar *)(words+2);
160                 // we need word alignment
161                 //if (chunk->size & 0x01) chunk->size++;
162                 chunk->form = words[2]; /* fill in the form,  might not be valid */
163
164
165                 if (chunk->id == GST_RIFF_TAG_LIST) {
166       //g_print("found LIST %s\n", gst_riff_id_to_fourcc(chunk->form));
167       riff->nextlikely += 12;   
168                         // we push the list chunk on our 'stack'
169       riff->chunks = g_list_prepend(riff->chunks,chunk);
170                   // send the buffer to the listener if we have received a function
171                   if (riff->new_tag_found) {
172                     riff->new_tag_found(chunk, riff->callback_data);
173                   }
174                 }
175                 else {
176
177       debug("chunk id offset %08x is 0x%08lx '%s' and is 0x%08lx long\n",riff->nextlikely, words[0],
178             gst_riff_id_to_fourcc(words[0]),words[1]);
179
180       riff->nextlikely += 8 + chunk->size;      /* doesn't include hdr */
181                         // if this buffer is incomplete
182                         if (riff->nextlikely > last) {
183                                 guint left = size - (riff->nextlikely - chunk->size - off);
184
185                     //g_print("make incomplete buffer %08x\n", left);
186                                 chunk->data = g_malloc(chunk->size);
187                     memcpy(chunk->data, (gchar *)(words+2), left);
188                                 riff->incomplete_chunk = chunk;
189                                 riff->incomplete_chunk_size = left;
190                   }
191                         else {
192                     // send the buffer to the listener if we have received a function
193                     if (riff->new_tag_found) {
194                       riff->new_tag_found(chunk, riff->callback_data);
195                     }
196                           g_free(chunk);
197                         }
198
199       //riff->chunks = g_list_prepend(riff->chunks,chunk);
200                 }
201   }
202
203   return 0;
204 }
205
206
207 gulong gst_riff_fourcc_to_id(gchar *fourcc) {
208   g_return_val_if_fail(fourcc != NULL, 0);
209
210   return (fourcc[0] << 0) | (fourcc[1] << 8) |
211          (fourcc[2] << 16) | (fourcc[3] << 24);
212 }
213
214 gchar *gst_riff_id_to_fourcc(gulong id) {
215   gchar *fourcc = (gchar *)g_malloc(5);
216
217   g_return_val_if_fail(fourcc != NULL, NULL);
218
219   fourcc[0] = (id >> 0) & 0xff;
220   fourcc[1] = (id >> 8) & 0xff;
221   fourcc[2] = (id >> 16) & 0xff;
222   fourcc[3] = (id >> 24) & 0xff;
223   fourcc[4] = 0;
224
225   return fourcc;
226 }