]> git.sesse.net Git - vlc/blob - modules/access/rtsp/real_rmff.c
d2072485127e3117399ce8c9eb7edf86d202ff57
[vlc] / modules / access / rtsp / real_rmff.c
1 /*
2  * Copyright (C) 2002-2003 the xine project
3  *
4  * This file is part of xine, a free video player.
5  *
6  * xine is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * xine 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
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
19  *
20  * $Id$
21  *
22  * functions for real media file format
23  * adopted from joschkas real tools
24  */
25
26 #include "real.h"
27
28 #define BE_16(x)  ((((uint8_t*)(x))[0] << 8) | ((uint8_t*)(x))[1])
29 #define BE_32(x)  ((((uint8_t*)(x))[0] << 24) | \
30                    (((uint8_t*)(x))[1] << 16) | \
31                    (((uint8_t*)(x))[2] << 8) | \
32                     ((uint8_t*)(x))[3])
33
34 /*
35  * writes header data to a buffer
36  */
37
38 static int rmff_dump_fileheader(rmff_fileheader_t *fileheader, uint8_t *buffer, int bufsize) {
39     if (!fileheader) return 0;
40     if (bufsize < RMFF_FILEHEADER_SIZE)
41         return -1;
42
43     fileheader->object_id=BE_32(&fileheader->object_id);
44     fileheader->size=BE_32(&fileheader->size);
45     fileheader->object_version=BE_16(&fileheader->object_version);
46     fileheader->file_version=BE_32(&fileheader->file_version);
47     fileheader->num_headers=BE_32(&fileheader->num_headers);
48
49     memcpy(buffer, fileheader, 8);
50     memcpy(&buffer[8], &fileheader->object_version, 2);
51     memcpy(&buffer[10], &fileheader->file_version, 8);
52
53     fileheader->size=BE_32(&fileheader->size);
54     fileheader->object_version=BE_16(&fileheader->object_version);
55     fileheader->file_version=BE_32(&fileheader->file_version);
56     fileheader->num_headers=BE_32(&fileheader->num_headers);
57     fileheader->object_id=BE_32(&fileheader->object_id);
58
59     return RMFF_FILEHEADER_SIZE;
60 }
61
62 static int rmff_dump_prop(rmff_prop_t *prop, uint8_t *buffer, int bufsize) {
63
64     if (!prop) return 0;
65     
66     if (bufsize < RMFF_PROPHEADER_SIZE)
67         return -1;
68
69     prop->object_id=BE_32(&prop->object_id);
70     prop->size=BE_32(&prop->size);
71     prop->object_version=BE_16(&prop->object_version);
72     prop->max_bit_rate=BE_32(&prop->max_bit_rate);
73     prop->avg_bit_rate=BE_32(&prop->avg_bit_rate);
74     prop->max_packet_size=BE_32(&prop->max_packet_size);
75     prop->avg_packet_size=BE_32(&prop->avg_packet_size);
76     prop->num_packets=BE_32(&prop->num_packets);
77     prop->duration=BE_32(&prop->duration);
78     prop->preroll=BE_32(&prop->preroll);
79     prop->index_offset=BE_32(&prop->index_offset);
80     prop->data_offset=BE_32(&prop->data_offset);
81     prop->num_streams=BE_16(&prop->num_streams);
82     prop->flags=BE_16(&prop->flags);
83
84     memcpy(buffer, prop, 8);
85     memcpy(&buffer[8], &prop->object_version, 2);
86     memcpy(&buffer[10], &prop->max_bit_rate, 36);
87     memcpy(&buffer[46], &prop->num_streams, 2);
88     memcpy(&buffer[48], &prop->flags, 2);
89
90     prop->size=BE_32(&prop->size);
91     prop->object_version=BE_16(&prop->object_version);
92     prop->max_bit_rate=BE_32(&prop->max_bit_rate);
93     prop->avg_bit_rate=BE_32(&prop->avg_bit_rate);
94     prop->max_packet_size=BE_32(&prop->max_packet_size);
95     prop->avg_packet_size=BE_32(&prop->avg_packet_size);
96     prop->num_packets=BE_32(&prop->num_packets);
97     prop->duration=BE_32(&prop->duration);
98     prop->preroll=BE_32(&prop->preroll);
99     prop->index_offset=BE_32(&prop->index_offset);
100     prop->data_offset=BE_32(&prop->data_offset);
101     prop->num_streams=BE_16(&prop->num_streams);
102     prop->flags=BE_16(&prop->flags);
103     prop->object_id=BE_32(&prop->object_id);
104
105     return RMFF_PROPHEADER_SIZE;
106 }
107
108 static int rmff_dump_mdpr(rmff_mdpr_t *mdpr, uint8_t *buffer, int bufsize) {
109
110     int s1, s2, s3;
111
112     if (!mdpr) return 0;
113     if (bufsize < RMFF_MDPRHEADER_SIZE + mdpr->type_specific_len +
114             mdpr->stream_name_size + mdpr->mime_type_size)
115     return -1;
116
117     mdpr->object_id=BE_32(&mdpr->object_id);
118     mdpr->size=BE_32(&mdpr->size);
119     mdpr->object_version=BE_16(&mdpr->object_version);
120     mdpr->stream_number=BE_16(&mdpr->stream_number);
121     mdpr->max_bit_rate=BE_32(&mdpr->max_bit_rate);
122     mdpr->avg_bit_rate=BE_32(&mdpr->avg_bit_rate);
123     mdpr->max_packet_size=BE_32(&mdpr->max_packet_size);
124     mdpr->avg_packet_size=BE_32(&mdpr->avg_packet_size);
125     mdpr->start_time=BE_32(&mdpr->start_time);
126     mdpr->preroll=BE_32(&mdpr->preroll);
127     mdpr->duration=BE_32(&mdpr->duration);
128
129     memcpy(buffer, mdpr, 8);
130     memcpy(&buffer[8], &mdpr->object_version, 2);
131     memcpy(&buffer[10], &mdpr->stream_number, 2);
132     memcpy(&buffer[12], &mdpr->max_bit_rate, 28);
133     memcpy(&buffer[40], &mdpr->stream_name_size, 1);
134     s1=mdpr->stream_name_size;
135     memcpy(&buffer[41], mdpr->stream_name, s1);
136
137     memcpy(&buffer[41+s1], &mdpr->mime_type_size, 1);
138     s2=mdpr->mime_type_size;
139     memcpy(&buffer[42+s1], mdpr->mime_type, s2);
140
141     mdpr->type_specific_len=BE_32(&mdpr->type_specific_len);
142     memcpy(&buffer[42+s1+s2], &mdpr->type_specific_len, 4);
143     mdpr->type_specific_len=BE_32(&mdpr->type_specific_len);
144     s3=mdpr->type_specific_len;
145     memcpy(&buffer[46+s1+s2], mdpr->type_specific_data, s3);
146
147     mdpr->size=BE_32(&mdpr->size);
148     mdpr->stream_number=BE_16(&mdpr->stream_number);
149     mdpr->max_bit_rate=BE_32(&mdpr->max_bit_rate);
150     mdpr->avg_bit_rate=BE_32(&mdpr->avg_bit_rate);
151     mdpr->max_packet_size=BE_32(&mdpr->max_packet_size);
152     mdpr->avg_packet_size=BE_32(&mdpr->avg_packet_size);
153     mdpr->start_time=BE_32(&mdpr->start_time);
154     mdpr->preroll=BE_32(&mdpr->preroll);
155     mdpr->duration=BE_32(&mdpr->duration);
156     mdpr->object_id=BE_32(&mdpr->object_id);
157
158     return RMFF_MDPRHEADER_SIZE + s1 + s2 + s3;
159 }
160
161 static int rmff_dump_cont(rmff_cont_t *cont, uint8_t *buffer, int bufsize) {
162
163     int p;
164
165     if (!cont) return 0;
166     
167     if (bufsize < RMFF_CONTHEADER_SIZE + cont->title_len + cont->author_len + \
168             cont->copyright_len + cont->comment_len)
169         return -1;
170
171     cont->object_id=BE_32(&cont->object_id);
172     cont->size=BE_32(&cont->size);
173     cont->object_version=BE_16(&cont->object_version);
174
175     memcpy(buffer, cont, 8);
176     memcpy(&buffer[8], &cont->object_version, 2);
177
178     cont->title_len=BE_16(&cont->title_len);
179     memcpy(&buffer[10], &cont->title_len, 2);
180     cont->title_len=BE_16(&cont->title_len);
181     memcpy(&buffer[12], cont->title, cont->title_len);
182     p=12+cont->title_len;
183
184     cont->author_len=BE_16(&cont->author_len);
185     memcpy(&buffer[p], &cont->author_len, 2);
186     cont->author_len=BE_16(&cont->author_len);
187     memcpy(&buffer[p+2], cont->author, cont->author_len);
188     p+=2+cont->author_len;
189
190     cont->copyright_len=BE_16(&cont->copyright_len);
191     memcpy(&buffer[p], &cont->copyright_len, 2);
192     cont->copyright_len=BE_16(&cont->copyright_len);
193     memcpy(&buffer[p+2], cont->copyright, cont->copyright_len);
194     p+=2+cont->copyright_len;
195
196     cont->comment_len=BE_16(&cont->comment_len);
197     memcpy(&buffer[p], &cont->comment_len, 2);
198     cont->comment_len=BE_16(&cont->comment_len);
199     memcpy(&buffer[p+2], cont->comment, cont->comment_len);
200
201     cont->size=BE_32(&cont->size);
202     cont->object_version=BE_16(&cont->object_version);
203     cont->object_id=BE_32(&cont->object_id);
204
205     return RMFF_CONTHEADER_SIZE + cont->title_len + cont->author_len + \
206         cont->copyright_len + cont->comment_len;
207 }
208
209 static int rmff_dump_dataheader(rmff_data_t *data, uint8_t *buffer, int bufsize) {
210
211   if (!data) return 0;
212   
213   if (bufsize < RMFF_DATAHEADER_SIZE)
214       return -1;
215
216
217   data->object_id=BE_32(&data->object_id);
218   data->size=BE_32(&data->size);
219   data->object_version=BE_16(&data->object_version);
220   data->num_packets=BE_32(&data->num_packets);
221   data->next_data_header=BE_32(&data->next_data_header);
222
223   memcpy(buffer, data, 8);
224   memcpy(&buffer[8], &data->object_version, 2);
225   memcpy(&buffer[10], &data->num_packets, 8);
226
227   data->num_packets=BE_32(&data->num_packets);
228   data->next_data_header=BE_32(&data->next_data_header);
229   data->size=BE_32(&data->size);
230   data->object_version=BE_16(&data->object_version);
231   data->object_id=BE_32(&data->object_id);
232
233   return RMFF_DATAHEADER_SIZE;
234 }
235
236 int rmff_dump_header(rmff_header_t *h, void *buf_gen, int max) {
237     uint8_t *buffer = buf_gen;
238
239     int written=0, size;
240     rmff_mdpr_t **stream=h->streams;
241
242     if ((size=rmff_dump_fileheader(h->fileheader, &buffer[written], max)) < 0)
243         return -1;
244     
245     written += size;
246     max -= size;
247
248     if ((size=rmff_dump_prop(h->prop, &buffer[written], max)) < 0)
249         return -1;
250     
251     written += size;
252     max -= size;
253
254     if ((size=rmff_dump_cont(h->cont, &buffer[written], max)) < 0)
255         return -1;
256
257     written += size;
258     max -= size;
259
260     if (stream) {
261         while(*stream) {
262             if ((size=rmff_dump_mdpr(*stream, &buffer[written], max)) < 0)
263                 return -1;
264             written += size;
265             max -= size;
266             stream++;
267         }
268     }
269
270     if ((size=rmff_dump_dataheader(h->data, &buffer[written], max)) < 0)
271         return -1;
272     
273     written+=size;
274
275     return written;
276 }
277
278 void rmff_dump_pheader(rmff_pheader_t *h, char *data) {
279
280   data[0]=(h->object_version>>8) & 0xff;
281   data[1]=h->object_version & 0xff;
282   data[2]=(h->length>>8) & 0xff;
283   data[3]=h->length & 0xff;
284   data[4]=(h->stream_number>>8) & 0xff;
285   data[5]=h->stream_number & 0xff;
286   data[6]=(h->timestamp>>24) & 0xff;
287   data[7]=(h->timestamp>>16) & 0xff;
288   data[8]=(h->timestamp>>8) & 0xff;
289   data[9]=h->timestamp & 0xff;
290   data[10]=h->reserved;
291   data[11]=h->flags;
292 }
293
294 rmff_fileheader_t *rmff_new_fileheader(uint32_t num_headers) {
295
296   rmff_fileheader_t *fileheader = malloc(sizeof(rmff_fileheader_t));
297   if( !fileheader ) return NULL;
298
299   memset(fileheader, 0, sizeof(rmff_fileheader_t));
300   fileheader->object_id=RMF_TAG;
301   fileheader->size=18;
302   fileheader->object_version=0;
303   fileheader->file_version=0;
304   fileheader->num_headers=num_headers;
305
306   return fileheader;
307 }
308
309 rmff_prop_t *rmff_new_prop (
310   uint32_t max_bit_rate,
311   uint32_t avg_bit_rate,
312   uint32_t max_packet_size,
313   uint32_t avg_packet_size,
314   uint32_t num_packets,
315   uint32_t duration,
316   uint32_t preroll,
317   uint32_t index_offset,
318   uint32_t data_offset,
319   uint16_t num_streams,
320   uint16_t flags ) {
321
322   rmff_prop_t *prop = malloc(sizeof(rmff_prop_t));
323   if( !prop ) return NULL;
324
325   memset(prop, 0, sizeof(rmff_prop_t));
326   prop->object_id=PROP_TAG;
327   prop->size=50;
328   prop->object_version=0;
329   prop->max_bit_rate=max_bit_rate;
330   prop->avg_bit_rate=avg_bit_rate;
331   prop->max_packet_size=max_packet_size;
332   prop->avg_packet_size=avg_packet_size;
333   prop->num_packets=num_packets;
334   prop->duration=duration;
335   prop->preroll=preroll;
336   prop->index_offset=index_offset;
337   prop->data_offset=data_offset;
338   prop->num_streams=num_streams;
339   prop->flags=flags;
340
341   return prop;
342 }
343
344 rmff_mdpr_t *rmff_new_mdpr(
345   uint16_t   stream_number,
346   uint32_t   max_bit_rate,
347   uint32_t   avg_bit_rate,
348   uint32_t   max_packet_size,
349   uint32_t   avg_packet_size,
350   uint32_t   start_time,
351   uint32_t   preroll,
352   uint32_t   duration,
353   const char *stream_name,
354   const char *mime_type,
355   uint32_t   type_specific_len,
356   const char *type_specific_data ) {
357
358   rmff_mdpr_t *mdpr = malloc(sizeof(rmff_mdpr_t));
359   if( !mdpr ) return NULL;
360
361   memset(mdpr, 0, sizeof(rmff_mdpr_t));
362   mdpr->object_id=MDPR_TAG;
363   mdpr->object_version=0;
364   mdpr->stream_number=stream_number;
365   mdpr->max_bit_rate=max_bit_rate;
366   mdpr->avg_bit_rate=avg_bit_rate;
367   mdpr->max_packet_size=max_packet_size;
368   mdpr->avg_packet_size=avg_packet_size;
369   mdpr->start_time=start_time;
370   mdpr->preroll=preroll;
371   mdpr->duration=duration;
372   mdpr->stream_name_size=0;
373   if (stream_name) {
374     mdpr->stream_name=strdup(stream_name);
375     mdpr->stream_name_size=strlen(stream_name);
376   }
377   mdpr->mime_type_size=0;
378   if (mime_type) {
379     mdpr->mime_type=strdup(mime_type);
380     mdpr->mime_type_size=strlen(mime_type);
381   }
382   mdpr->type_specific_len=type_specific_len;
383
384   mdpr->type_specific_data = malloc(sizeof(char)*type_specific_len);
385   if( !mdpr->type_specific_data ) {
386     if( mdpr->stream_name ) free( mdpr->stream_name );
387     free( mdpr );
388     return NULL;
389   }
390   memcpy(mdpr->type_specific_data,type_specific_data,type_specific_len);
391   mdpr->mlti_data=NULL;
392   mdpr->size=mdpr->stream_name_size+mdpr->mime_type_size+mdpr->type_specific_len+46;
393   return mdpr;
394 }
395
396 rmff_cont_t *rmff_new_cont(const char *title, const char *author, const char *copyright, const char *comment) {
397
398   rmff_cont_t *cont = malloc(sizeof(rmff_cont_t));
399   if( !cont ) return NULL;
400
401   memset(cont, 0, sizeof(rmff_cont_t));
402   cont->object_id=CONT_TAG;
403   cont->object_version=0;
404   cont->title=NULL;
405   cont->author=NULL;
406   cont->copyright=NULL;
407   cont->comment=NULL;
408   cont->title_len=0;
409   cont->author_len=0;
410   cont->copyright_len=0;
411   cont->comment_len=0;
412
413   if (title) {
414     cont->title_len=strlen(title);
415     cont->title=strdup(title);
416   }
417   if (author)
418   {
419     cont->author_len=strlen(author);
420     cont->author=strdup(author);
421   }
422   if (copyright) {
423     cont->copyright_len=strlen(copyright);
424     cont->copyright=strdup(copyright);
425   }
426   if (comment) {
427     cont->comment_len=strlen(comment);
428     cont->comment=strdup(comment);
429   }
430   cont->size=cont->title_len+cont->author_len+cont->copyright_len+cont->comment_len+18;
431   return cont;
432 }
433
434 rmff_data_t *rmff_new_dataheader(uint32_t num_packets, uint32_t next_data_header) {
435   rmff_data_t *data = malloc(sizeof(rmff_data_t));
436   if( !data ) return NULL;
437
438   memset(data, 0, sizeof(rmff_data_t));
439   data->object_id=DATA_TAG;
440   data->size=18;
441   data->object_version=0;
442   data->num_packets=num_packets;
443   data->next_data_header=next_data_header;
444
445   return data;
446 }
447
448 void rmff_print_header(rmff_header_t *h) {
449   rmff_mdpr_t **stream;
450
451   if(!h) {
452     printf("rmff_print_header: NULL given\n");
453     return;
454   }
455   if(h->fileheader) {
456     printf("\nFILE:\n");
457     printf("file version      : %d\n", h->fileheader->file_version);
458     printf("number of headers : %d\n", h->fileheader->num_headers);
459   }
460   if(h->cont) {
461     printf("\nCONTENT:\n");
462     printf("title     : %s\n", h->cont->title);
463     printf("author    : %s\n", h->cont->author);
464     printf("copyright : %s\n", h->cont->copyright);
465     printf("comment   : %s\n", h->cont->comment);
466   }
467   if(h->prop) {
468     printf("\nSTREAM PROPERTIES:\n");
469     printf("bit rate (max/avg)    : %i/%i\n", h->prop->max_bit_rate, h->prop->avg_bit_rate);
470     printf("packet size (max/avg) : %i/%i bytes\n", h->prop->max_packet_size, h->prop->avg_packet_size);
471     printf("packets       : %i\n", h->prop->num_packets);
472     printf("duration      : %i ms\n", h->prop->duration);
473     printf("pre-buffer    : %i ms\n", h->prop->preroll);
474     printf("index offset  : %i bytes\n", h->prop->index_offset);
475     printf("data offset   : %i bytes\n", h->prop->data_offset);
476     printf("media streams : %i\n", h->prop->num_streams);
477     printf("flags         : ");
478     if (h->prop->flags & PN_SAVE_ENABLED) printf("save_enabled ");
479     if (h->prop->flags & PN_PERFECT_PLAY_ENABLED) printf("perfect_play_enabled ");
480     if (h->prop->flags & PN_LIVE_BROADCAST) printf("live_broadcast ");
481     printf("\n");
482   }
483   stream=h->streams;
484   if(stream) {
485     while (*stream) {
486       printf("\nSTREAM %i:\n", (*stream)->stream_number);
487       printf("stream name [mime type] : %s [%s]\n", (*stream)->stream_name, (*stream)->mime_type);
488       printf("bit rate (max/avg)      : %i/%i\n", (*stream)->max_bit_rate, (*stream)->avg_bit_rate);
489       printf("packet size (max/avg)   : %i/%i bytes\n", (*stream)->max_packet_size, (*stream)->avg_packet_size);
490       printf("start time : %i\n", (*stream)->start_time);
491       printf("pre-buffer : %i ms\n", (*stream)->preroll);
492       printf("duration   : %i ms\n", (*stream)->duration);
493       printf("type specific data:\n");
494       stream++;
495     }
496   }
497   if(h->data) {
498     printf("\nDATA:\n");
499     printf("size      : %i\n", h->data->size);
500     printf("packets   : %i\n", h->data->num_packets);
501     printf("next DATA : 0x%08x\n", h->data->next_data_header);
502   }
503 }
504
505 void rmff_fix_header(rmff_header_t *h) {
506
507   unsigned int num_headers=0;
508   unsigned int header_size=0;
509   rmff_mdpr_t **streams;
510   int num_streams=0;
511
512   if (!h) {
513     lprintf("rmff_fix_header: fatal: no header given.\n");
514     return;
515   }
516   if (!h->streams) {
517     lprintf("rmff_fix_header: warning: no MDPR chunks\n");
518   } else {
519     streams=h->streams;
520     while (*streams) {
521         num_streams++;
522         num_headers++;
523         header_size+=(*streams)->size;
524         streams++;
525     }
526   }
527   if (h->prop) {
528     if (h->prop->size != 50) {
529       lprintf("rmff_fix_header: correcting prop.size from %i to %i\n", h->prop->size, 50);
530       h->prop->size=50;
531     }
532     if (h->prop->num_streams != num_streams) {
533       lprintf("rmff_fix_header: correcting prop.num_streams from %i to %i\n", h->prop->num_streams, num_streams);
534       h->prop->num_streams=num_streams;
535     }
536     num_headers++;
537     header_size+=50;
538   } else lprintf("rmff_fix_header: warning: no PROP chunk.\n");
539
540   if (h->cont) {
541     num_headers++;
542     header_size+=h->cont->size;
543   } else lprintf("rmff_fix_header: warning: no CONT chunk.\n");
544
545   if (!h->data) {
546     lprintf("rmff_fix_header: no DATA chunk, creating one\n");
547     h->data = malloc(sizeof(rmff_data_t));
548     if( h->data ) {
549       memset(h->data, 0, sizeof(rmff_data_t));
550       h->data->object_id=DATA_TAG;
551       h->data->object_version=0;
552       h->data->size=18;
553       h->data->num_packets=0;
554       h->data->next_data_header=0;
555     }
556   }
557   num_headers++;
558
559   if (!h->fileheader) {
560     lprintf("rmff_fix_header: no fileheader, creating one");
561     h->fileheader = malloc(sizeof(rmff_fileheader_t));
562     if( h->fileheader ) {
563       memset(h->fileheader, 0, sizeof(rmff_fileheader_t));
564       h->fileheader->object_id=RMF_TAG;
565       h->fileheader->size=18;
566       h->fileheader->object_version=0;
567       h->fileheader->file_version=0;
568       h->fileheader->num_headers=num_headers+1;
569     }
570   }
571   header_size+=h->fileheader->size;
572   num_headers++;
573
574   if(h->fileheader->num_headers != num_headers) {
575     lprintf("rmff_fix_header: setting num_headers from %i to %i\n", h->fileheader->num_headers, num_headers);
576     h->fileheader->num_headers=num_headers;
577   }
578   if(h->prop) {
579     if (h->prop->data_offset != header_size) {
580       lprintf("rmff_fix_header: setting prop.data_offset from %i to %i\n", h->prop->data_offset, header_size);
581       h->prop->data_offset=header_size;
582     }
583
584     /* FIXME: I doubt this is right to do this here.
585      * It should belong to the demux. */
586     if (h->prop->num_packets == 0) {
587       int p=(int)(h->prop->avg_bit_rate/8.0*(h->prop->duration/1000.0)/h->prop->avg_packet_size);
588       lprintf("rmff_fix_header: assuming prop.num_packets=%i\n", p);
589       h->prop->num_packets=p;
590     }
591     if (h->data->num_packets == 0) {
592       lprintf("rmff_fix_header: assuming data.num_packets=%i\n", h->prop->num_packets);
593       h->data->num_packets=h->prop->num_packets;
594     }
595     if (h->data->size == 18 || !h->data->size ) {
596       lprintf("rmff_fix_header: assuming data.size=%i\n", h->prop->num_packets*h->prop->avg_packet_size);
597       h->data->size+=h->prop->num_packets*h->prop->avg_packet_size;
598     }
599   }
600 }
601
602 int rmff_get_header_size(rmff_header_t *h) {
603   if (!h) return 0;
604   if (!h->prop) return -1;
605   return h->prop->data_offset+18;
606 }
607
608 void rmff_free_header(rmff_header_t *h)
609 {
610   if (!h) return;
611
612   if (h->fileheader) free(h->fileheader);
613   if (h->prop) free(h->prop);
614   if (h->data) free(h->data);
615   if (h->cont) {
616     free(h->cont->title);
617     free(h->cont->author);
618     free(h->cont->copyright);
619     free(h->cont->comment);
620     free(h->cont);
621   }
622   if (h->streams) {
623     rmff_mdpr_t **s=h->streams;
624
625     while(*s) {
626       free((*s)->stream_name);
627       free((*s)->mime_type);
628       free((*s)->type_specific_data);
629       free(*s);
630       s++;
631     }
632     free(h->streams);
633   }
634   free(h);
635 }