fourcc: remove fourcc from caps
[platform/upstream/gstreamer.git] / gst / isomp4 / atoms.c
1 /* Quicktime muxer plugin for GStreamer
2  * Copyright (C) 2008-2010 Thiago Santos <thiagoss@embedded.ufcg.edu.br>
3  * Copyright (C) 2008 Mark Nauwelaerts <mnauw@users.sf.net>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20 /*
21  * Unless otherwise indicated, Source Code is licensed under MIT license.
22  * See further explanation attached in License Statement (distributed in the file
23  * LICENSE).
24  *
25  * Permission is hereby granted, free of charge, to any person obtaining a copy of
26  * this software and associated documentation files (the "Software"), to deal in
27  * the Software without restriction, including without limitation the rights to
28  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
29  * of the Software, and to permit persons to whom the Software is furnished to do
30  * so, subject to the following conditions:
31  *
32  * The above copyright notice and this permission notice shall be included in all
33  * copies or substantial portions of the Software.
34  *
35  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
36  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
37  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
38  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
39  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
40  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
41  * SOFTWARE.
42  */
43
44 #include "atoms.h"
45 #include <string.h>
46 #include <glib.h>
47
48 #include <gst/gst.h>
49 #include <gst/base/gstbytewriter.h>
50 #include <gst/tag/tag.h>
51
52 /**
53  * Creates a new AtomsContext for the given flavor.
54  */
55 AtomsContext *
56 atoms_context_new (AtomsTreeFlavor flavor)
57 {
58   AtomsContext *context = g_new0 (AtomsContext, 1);
59   context->flavor = flavor;
60   return context;
61 }
62
63 /**
64  * Frees an AtomsContext and all memory associated with it
65  */
66 void
67 atoms_context_free (AtomsContext * context)
68 {
69   g_free (context);
70 }
71
72 /* -- creation, initialization, clear and free functions -- */
73
74 #define SECS_PER_DAY (24 * 60 * 60)
75 #define LEAP_YEARS_FROM_1904_TO_1970 17
76
77 static guint64
78 get_current_qt_time (void)
79 {
80   GTimeVal timeval;
81
82   g_get_current_time (&timeval);
83   /* FIXME this should use UTC coordinated time */
84   return timeval.tv_sec + (((1970 - 1904) * (guint64) 365) +
85       LEAP_YEARS_FROM_1904_TO_1970) * SECS_PER_DAY;
86 }
87
88 static void
89 common_time_info_init (TimeInfo * ti)
90 {
91   ti->creation_time = ti->modification_time = get_current_qt_time ();
92   ti->timescale = 0;
93   ti->duration = 0;
94 }
95
96 static void
97 atom_header_set (Atom * header, guint32 fourcc, gint32 size, gint64 ext_size)
98 {
99   header->type = fourcc;
100   header->size = size;
101   header->extended_size = ext_size;
102 }
103
104 static void
105 atom_clear (Atom * atom)
106 {
107 }
108
109 static void
110 atom_full_init (AtomFull * full, guint32 fourcc, gint32 size, gint64 ext_size,
111     guint8 version, guint8 flags[3])
112 {
113   atom_header_set (&(full->header), fourcc, size, ext_size);
114   full->version = version;
115   full->flags[0] = flags[0];
116   full->flags[1] = flags[1];
117   full->flags[2] = flags[2];
118 }
119
120 static void
121 atom_full_clear (AtomFull * full)
122 {
123   atom_clear (&full->header);
124 }
125
126 static void
127 atom_full_free (AtomFull * full)
128 {
129   atom_full_clear (full);
130   g_free (full);
131 }
132
133 static guint32
134 atom_full_get_flags_as_uint (AtomFull * full)
135 {
136   return full->flags[0] << 16 | full->flags[1] << 8 | full->flags[2];
137 }
138
139 static void
140 atom_full_set_flags_as_uint (AtomFull * full, guint32 flags_as_uint)
141 {
142   full->flags[2] = flags_as_uint & 0xFF;
143   full->flags[1] = (flags_as_uint & 0xFF00) >> 8;
144   full->flags[0] = (flags_as_uint & 0xFF0000) >> 16;
145 }
146
147 static AtomInfo *
148 build_atom_info_wrapper (Atom * atom, gpointer copy_func, gpointer free_func)
149 {
150   AtomInfo *info = NULL;
151
152   if (atom) {
153     info = g_new0 (AtomInfo, 1);
154
155     info->atom = atom;
156     info->copy_data_func = copy_func;
157     info->free_func = free_func;
158   }
159
160   return info;
161 }
162
163 static GList *
164 atom_info_list_prepend_atom (GList * ai, Atom * atom,
165     AtomCopyDataFunc copy_func, AtomFreeFunc free_func)
166 {
167   if (atom)
168     return g_list_prepend (ai,
169         build_atom_info_wrapper (atom, copy_func, free_func));
170   else
171     return ai;
172 }
173
174 static void
175 atom_info_list_free (GList * ai)
176 {
177   while (ai) {
178     AtomInfo *info = (AtomInfo *) ai->data;
179
180     info->free_func (info->atom);
181     g_free (info);
182     ai = g_list_delete_link (ai, ai);
183   }
184 }
185
186 static AtomData *
187 atom_data_new (guint32 fourcc)
188 {
189   AtomData *data = g_new0 (AtomData, 1);
190
191   atom_header_set (&data->header, fourcc, 0, 0);
192   return data;
193 }
194
195 static void
196 atom_data_alloc_mem (AtomData * data, guint32 size)
197 {
198   if (data->data) {
199     g_free (data->data);
200   }
201   data->data = g_new0 (guint8, size);
202   data->datalen = size;
203 }
204
205 static AtomData *
206 atom_data_new_from_gst_buffer (guint32 fourcc, const GstBuffer * buf)
207 {
208   AtomData *data = atom_data_new (fourcc);
209   gsize size = gst_buffer_get_size ((GstBuffer *) buf);
210
211   atom_data_alloc_mem (data, size);
212   gst_buffer_extract ((GstBuffer *) buf, 0, data->data, size);
213   return data;
214 }
215
216 static void
217 atom_data_free (AtomData * data)
218 {
219   atom_clear (&data->header);
220   g_free (data->data);
221   g_free (data);
222 }
223
224 static AtomUUID *
225 atom_uuid_new (void)
226 {
227   AtomUUID *uuid = g_new0 (AtomUUID, 1);
228
229   atom_header_set (&uuid->header, FOURCC_uuid, 0, 0);
230   return uuid;
231 }
232
233 static void
234 atom_uuid_free (AtomUUID * data)
235 {
236   atom_clear (&data->header);
237   g_free (data->data);
238   g_free (data);
239 }
240
241 static void
242 atom_ftyp_init (AtomFTYP * ftyp, guint32 major, guint32 version, GList * brands)
243 {
244   gint index;
245   GList *it = NULL;
246
247   atom_header_set (&ftyp->header, FOURCC_ftyp, 16, 0);
248   ftyp->major_brand = major;
249   ftyp->version = version;
250
251   /* always include major brand as compatible brand */
252   ftyp->compatible_brands_size = g_list_length (brands) + 1;
253   ftyp->compatible_brands = g_new (guint32, ftyp->compatible_brands_size);
254
255   ftyp->compatible_brands[0] = major;
256   index = 1;
257   for (it = brands; it != NULL; it = g_list_next (it)) {
258     ftyp->compatible_brands[index++] = GPOINTER_TO_UINT (it->data);
259   }
260 }
261
262 AtomFTYP *
263 atom_ftyp_new (AtomsContext * context, guint32 major, guint32 version,
264     GList * brands)
265 {
266   AtomFTYP *ftyp = g_new0 (AtomFTYP, 1);
267
268   atom_ftyp_init (ftyp, major, version, brands);
269   return ftyp;
270 }
271
272 void
273 atom_ftyp_free (AtomFTYP * ftyp)
274 {
275   atom_clear (&ftyp->header);
276   g_free (ftyp->compatible_brands);
277   ftyp->compatible_brands = NULL;
278   g_free (ftyp);
279 }
280
281 static void
282 atom_esds_init (AtomESDS * esds)
283 {
284   guint8 flags[3] = { 0, 0, 0 };
285
286   atom_full_init (&esds->header, FOURCC_esds, 0, 0, 0, flags);
287   desc_es_init (&esds->es);
288 }
289
290 static AtomESDS *
291 atom_esds_new (void)
292 {
293   AtomESDS *esds = g_new0 (AtomESDS, 1);
294
295   atom_esds_init (esds);
296   return esds;
297 }
298
299 static void
300 atom_esds_free (AtomESDS * esds)
301 {
302   atom_full_clear (&esds->header);
303   desc_es_descriptor_clear (&esds->es);
304   g_free (esds);
305 }
306
307 static AtomFRMA *
308 atom_frma_new (void)
309 {
310   AtomFRMA *frma = g_new0 (AtomFRMA, 1);
311
312   atom_header_set (&frma->header, FOURCC_frma, 0, 0);
313   return frma;
314 }
315
316 static void
317 atom_frma_free (AtomFRMA * frma)
318 {
319   atom_clear (&frma->header);
320   g_free (frma);
321 }
322
323 static AtomWAVE *
324 atom_wave_new (void)
325 {
326   AtomWAVE *wave = g_new0 (AtomWAVE, 1);
327
328   atom_header_set (&wave->header, FOURCC_wave, 0, 0);
329   return wave;
330 }
331
332 static void
333 atom_wave_free (AtomWAVE * wave)
334 {
335   atom_clear (&wave->header);
336   atom_info_list_free (wave->extension_atoms);
337   g_free (wave);
338 }
339
340 static void
341 atom_elst_init (AtomELST * elst)
342 {
343   guint8 flags[3] = { 0, 0, 0 };
344   atom_full_init (&elst->header, FOURCC_elst, 0, 0, 0, flags);
345   elst->entries = 0;
346 }
347
348 static void
349 atom_elst_clear (AtomELST * elst)
350 {
351   GSList *walker;
352
353   atom_full_clear (&elst->header);
354   walker = elst->entries;
355   while (walker) {
356     g_free ((EditListEntry *) walker->data);
357     walker = g_slist_next (walker);
358   }
359   g_slist_free (elst->entries);
360 }
361
362 static void
363 atom_edts_init (AtomEDTS * edts)
364 {
365   atom_header_set (&edts->header, FOURCC_edts, 0, 0);
366   atom_elst_init (&edts->elst);
367 }
368
369 static void
370 atom_edts_clear (AtomEDTS * edts)
371 {
372   atom_clear (&edts->header);
373   atom_elst_clear (&edts->elst);
374 }
375
376 static AtomEDTS *
377 atom_edts_new (void)
378 {
379   AtomEDTS *edts = g_new0 (AtomEDTS, 1);
380   atom_edts_init (edts);
381   return edts;
382 }
383
384 static void
385 atom_edts_free (AtomEDTS * edts)
386 {
387   atom_edts_clear (edts);
388   g_free (edts);
389 }
390
391 static void
392 atom_sample_entry_init (SampleTableEntry * se, guint32 type)
393 {
394   atom_header_set (&se->header, type, 0, 0);
395
396   memset (se->reserved, 0, sizeof (guint8) * 6);
397   se->data_reference_index = 0;
398 }
399
400 static void
401 atom_sample_entry_free (SampleTableEntry * se)
402 {
403   atom_clear (&se->header);
404 }
405
406 static void
407 sample_entry_mp4a_init (SampleTableEntryMP4A * mp4a)
408 {
409   atom_sample_entry_init (&mp4a->se, FOURCC_mp4a);
410
411   mp4a->version = 0;
412   mp4a->revision_level = 0;
413   mp4a->vendor = 0;
414   mp4a->channels = 2;
415   mp4a->sample_size = 16;
416   mp4a->compression_id = 0;
417   mp4a->packet_size = 0;
418   mp4a->sample_rate = 0;
419   /* following only used if version is 1 */
420   mp4a->samples_per_packet = 0;
421   mp4a->bytes_per_packet = 0;
422   mp4a->bytes_per_frame = 0;
423   mp4a->bytes_per_sample = 0;
424
425   mp4a->extension_atoms = NULL;
426 }
427
428 static SampleTableEntryMP4A *
429 sample_entry_mp4a_new (void)
430 {
431   SampleTableEntryMP4A *mp4a = g_new0 (SampleTableEntryMP4A, 1);
432
433   sample_entry_mp4a_init (mp4a);
434   return mp4a;
435 }
436
437 static void
438 sample_entry_mp4a_free (SampleTableEntryMP4A * mp4a)
439 {
440   atom_sample_entry_free (&mp4a->se);
441   atom_info_list_free (mp4a->extension_atoms);
442   g_free (mp4a);
443 }
444
445 static void
446 sample_entry_mp4v_init (SampleTableEntryMP4V * mp4v, AtomsContext * context)
447 {
448   atom_sample_entry_init (&mp4v->se, FOURCC_mp4v);
449
450   mp4v->version = 0;
451   mp4v->revision_level = 0;
452   mp4v->vendor = 0;
453
454   mp4v->temporal_quality = 0;
455   mp4v->spatial_quality = 0;
456
457   /* qt and ISO base media do not contradict, and examples agree */
458   mp4v->horizontal_resolution = 0x00480000;
459   mp4v->vertical_resolution = 0x00480000;
460
461   mp4v->datasize = 0;
462   mp4v->frame_count = 1;
463
464   memset (mp4v->compressor, 0, sizeof (guint8) * 32);
465
466   mp4v->depth = 0;
467   mp4v->color_table_id = 0;
468
469   mp4v->extension_atoms = NULL;
470 }
471
472 static void
473 sample_entry_mp4v_free (SampleTableEntryMP4V * mp4v)
474 {
475   atom_sample_entry_free (&mp4v->se);
476   atom_info_list_free (mp4v->extension_atoms);
477   g_free (mp4v);
478 }
479
480 static SampleTableEntryMP4V *
481 sample_entry_mp4v_new (AtomsContext * context)
482 {
483   SampleTableEntryMP4V *mp4v = g_new0 (SampleTableEntryMP4V, 1);
484
485   sample_entry_mp4v_init (mp4v, context);
486   return mp4v;
487 }
488
489 static void
490 atom_stsd_init (AtomSTSD * stsd)
491 {
492   guint8 flags[3] = { 0, 0, 0 };
493
494   atom_full_init (&stsd->header, FOURCC_stsd, 0, 0, 0, flags);
495   stsd->entries = NULL;
496   stsd->n_entries = 0;
497 }
498
499 static void
500 atom_stsd_remove_entries (AtomSTSD * stsd)
501 {
502   GList *walker;
503
504   walker = stsd->entries;
505   while (walker) {
506     GList *aux = walker;
507     SampleTableEntry *se = (SampleTableEntry *) aux->data;
508
509     walker = g_list_next (walker);
510     stsd->entries = g_list_remove_link (stsd->entries, aux);
511
512     switch (se->kind) {
513       case AUDIO:
514         sample_entry_mp4a_free ((SampleTableEntryMP4A *) se);
515         break;
516       case VIDEO:
517         sample_entry_mp4v_free ((SampleTableEntryMP4V *) se);
518         break;
519       default:
520         /* best possible cleanup */
521         atom_sample_entry_free (se);
522     }
523     g_list_free (aux);
524   }
525   stsd->n_entries = 0;
526 }
527
528 static void
529 atom_stsd_clear (AtomSTSD * stsd)
530 {
531   atom_stsd_remove_entries (stsd);
532   atom_full_clear (&stsd->header);
533 }
534
535 static void
536 atom_ctts_init (AtomCTTS * ctts)
537 {
538   guint8 flags[3] = { 0, 0, 0 };
539
540   atom_full_init (&ctts->header, FOURCC_ctts, 0, 0, 0, flags);
541   atom_array_init (&ctts->entries, 128);
542   ctts->do_pts = FALSE;
543 }
544
545 static AtomCTTS *
546 atom_ctts_new (void)
547 {
548   AtomCTTS *ctts = g_new0 (AtomCTTS, 1);
549
550   atom_ctts_init (ctts);
551   return ctts;
552 }
553
554 static void
555 atom_ctts_free (AtomCTTS * ctts)
556 {
557   atom_full_clear (&ctts->header);
558   atom_array_clear (&ctts->entries);
559   g_free (ctts);
560 }
561
562 static void
563 atom_stts_init (AtomSTTS * stts)
564 {
565   guint8 flags[3] = { 0, 0, 0 };
566
567   atom_full_init (&stts->header, FOURCC_stts, 0, 0, 0, flags);
568   atom_array_init (&stts->entries, 512);
569 }
570
571 static void
572 atom_stts_clear (AtomSTTS * stts)
573 {
574   atom_full_clear (&stts->header);
575   atom_array_clear (&stts->entries);
576 }
577
578 static void
579 atom_stsz_init (AtomSTSZ * stsz)
580 {
581   guint8 flags[3] = { 0, 0, 0 };
582
583   atom_full_init (&stsz->header, FOURCC_stsz, 0, 0, 0, flags);
584   atom_array_init (&stsz->entries, 1024);
585   stsz->sample_size = 0;
586   stsz->table_size = 0;
587 }
588
589 static void
590 atom_stsz_clear (AtomSTSZ * stsz)
591 {
592   atom_full_clear (&stsz->header);
593   atom_array_clear (&stsz->entries);
594   stsz->table_size = 0;
595 }
596
597 static void
598 atom_stsc_init (AtomSTSC * stsc)
599 {
600   guint8 flags[3] = { 0, 0, 0 };
601
602   atom_full_init (&stsc->header, FOURCC_stsc, 0, 0, 0, flags);
603   atom_array_init (&stsc->entries, 128);
604 }
605
606 static void
607 atom_stsc_clear (AtomSTSC * stsc)
608 {
609   atom_full_clear (&stsc->header);
610   atom_array_clear (&stsc->entries);
611 }
612
613 static void
614 atom_co64_init (AtomSTCO64 * co64)
615 {
616   guint8 flags[3] = { 0, 0, 0 };
617
618   atom_full_init (&co64->header, FOURCC_stco, 0, 0, 0, flags);
619   atom_array_init (&co64->entries, 256);
620 }
621
622 static void
623 atom_stco64_clear (AtomSTCO64 * stco64)
624 {
625   atom_full_clear (&stco64->header);
626   atom_array_clear (&stco64->entries);
627 }
628
629 static void
630 atom_stss_init (AtomSTSS * stss)
631 {
632   guint8 flags[3] = { 0, 0, 0 };
633
634   atom_full_init (&stss->header, FOURCC_stss, 0, 0, 0, flags);
635   atom_array_init (&stss->entries, 128);
636 }
637
638 static void
639 atom_stss_clear (AtomSTSS * stss)
640 {
641   atom_full_clear (&stss->header);
642   atom_array_clear (&stss->entries);
643 }
644
645 void
646 atom_stbl_init (AtomSTBL * stbl)
647 {
648   atom_header_set (&stbl->header, FOURCC_stbl, 0, 0);
649
650   atom_stts_init (&stbl->stts);
651   atom_stss_init (&stbl->stss);
652   atom_stsd_init (&stbl->stsd);
653   atom_stsz_init (&stbl->stsz);
654   atom_stsc_init (&stbl->stsc);
655   stbl->ctts = NULL;
656
657   atom_co64_init (&stbl->stco64);
658 }
659
660 void
661 atom_stbl_clear (AtomSTBL * stbl)
662 {
663   atom_clear (&stbl->header);
664   atom_stsd_clear (&stbl->stsd);
665   atom_stts_clear (&stbl->stts);
666   atom_stss_clear (&stbl->stss);
667   atom_stsc_clear (&stbl->stsc);
668   atom_stsz_clear (&stbl->stsz);
669   if (stbl->ctts) {
670     atom_ctts_free (stbl->ctts);
671   }
672   atom_stco64_clear (&stbl->stco64);
673 }
674
675 static void
676 atom_vmhd_init (AtomVMHD * vmhd, AtomsContext * context)
677 {
678   guint8 flags[3] = { 0, 0, 1 };
679
680   atom_full_init (&vmhd->header, FOURCC_vmhd, 0, 0, 0, flags);
681   vmhd->graphics_mode = 0x0;
682   memset (vmhd->opcolor, 0, sizeof (guint16) * 3);
683
684   if (context->flavor == ATOMS_TREE_FLAVOR_MOV) {
685     vmhd->graphics_mode = 0x40;
686     vmhd->opcolor[0] = 32768;
687     vmhd->opcolor[1] = 32768;
688     vmhd->opcolor[2] = 32768;
689   }
690 }
691
692 static AtomVMHD *
693 atom_vmhd_new (AtomsContext * context)
694 {
695   AtomVMHD *vmhd = g_new0 (AtomVMHD, 1);
696
697   atom_vmhd_init (vmhd, context);
698   return vmhd;
699 }
700
701 static void
702 atom_vmhd_free (AtomVMHD * vmhd)
703 {
704   atom_full_clear (&vmhd->header);
705   g_free (vmhd);
706 }
707
708 static void
709 atom_smhd_init (AtomSMHD * smhd)
710 {
711   guint8 flags[3] = { 0, 0, 0 };
712
713   atom_full_init (&smhd->header, FOURCC_smhd, 0, 0, 0, flags);
714   smhd->balance = 0;
715   smhd->reserved = 0;
716 }
717
718 static AtomSMHD *
719 atom_smhd_new (void)
720 {
721   AtomSMHD *smhd = g_new0 (AtomSMHD, 1);
722
723   atom_smhd_init (smhd);
724   return smhd;
725 }
726
727 static void
728 atom_smhd_free (AtomSMHD * smhd)
729 {
730   atom_full_clear (&smhd->header);
731   g_free (smhd);
732 }
733
734 static void
735 atom_hmhd_free (AtomHMHD * hmhd)
736 {
737   atom_full_clear (&hmhd->header);
738   g_free (hmhd);
739 }
740
741 static void
742 atom_hdlr_init (AtomHDLR * hdlr)
743 {
744   guint8 flags[3] = { 0, 0, 0 };
745
746   atom_full_init (&hdlr->header, FOURCC_hdlr, 0, 0, 0, flags);
747
748   hdlr->component_type = 0;
749   hdlr->handler_type = 0;
750   hdlr->manufacturer = 0;
751   hdlr->flags = 0;
752   hdlr->flags_mask = 0;
753   hdlr->name = g_strdup ("");
754 }
755
756 static AtomHDLR *
757 atom_hdlr_new (void)
758 {
759   AtomHDLR *hdlr = g_new0 (AtomHDLR, 1);
760
761   atom_hdlr_init (hdlr);
762   return hdlr;
763 }
764
765 static void
766 atom_hdlr_clear (AtomHDLR * hdlr)
767 {
768   atom_full_clear (&hdlr->header);
769   if (hdlr->name) {
770     g_free (hdlr->name);
771     hdlr->name = NULL;
772   }
773 }
774
775 static void
776 atom_hdlr_free (AtomHDLR * hdlr)
777 {
778   atom_hdlr_clear (hdlr);
779   g_free (hdlr);
780 }
781
782 static void
783 atom_url_init (AtomURL * url)
784 {
785   guint8 flags[3] = { 0, 0, 1 };
786
787   atom_full_init (&url->header, FOURCC_url_, 0, 0, 0, flags);
788   url->location = NULL;
789 }
790
791 static void
792 atom_url_free (AtomURL * url)
793 {
794   atom_full_clear (&url->header);
795   if (url->location) {
796     g_free (url->location);
797     url->location = NULL;
798   }
799   g_free (url);
800 }
801
802 static AtomURL *
803 atom_url_new (void)
804 {
805   AtomURL *url = g_new0 (AtomURL, 1);
806
807   atom_url_init (url);
808   return url;
809 }
810
811 static AtomFull *
812 atom_alis_new (void)
813 {
814   guint8 flags[3] = { 0, 0, 1 };
815   AtomFull *alis = g_new0 (AtomFull, 1);
816
817   atom_full_init (alis, FOURCC_alis, 0, 0, 0, flags);
818   return alis;
819 }
820
821 static void
822 atom_dref_init (AtomDREF * dref, AtomsContext * context)
823 {
824   guint8 flags[3] = { 0, 0, 0 };
825
826   atom_full_init (&dref->header, FOURCC_dref, 0, 0, 0, flags);
827
828   /* in either case, alis or url init arranges to set self-contained flag */
829   if (context->flavor == ATOMS_TREE_FLAVOR_MOV) {
830     /* alis dref for qt */
831     AtomFull *alis = atom_alis_new ();
832     dref->entries = g_list_append (dref->entries, alis);
833   } else {
834     /* url for iso spec, as 'alis' not specified there */
835     AtomURL *url = atom_url_new ();
836     dref->entries = g_list_append (dref->entries, url);
837   }
838 }
839
840 static void
841 atom_dref_clear (AtomDREF * dref)
842 {
843   GList *walker;
844
845   atom_full_clear (&dref->header);
846   walker = dref->entries;
847   while (walker) {
848     GList *aux = walker;
849     Atom *atom = (Atom *) aux->data;
850
851     walker = g_list_next (walker);
852     dref->entries = g_list_remove_link (dref->entries, aux);
853     switch (atom->type) {
854       case FOURCC_alis:
855         atom_full_free ((AtomFull *) atom);
856         break;
857       case FOURCC_url_:
858         atom_url_free ((AtomURL *) atom);
859         break;
860       default:
861         /* we do nothing, better leak than crash */
862         break;
863     }
864     g_list_free (aux);
865   }
866 }
867
868 static void
869 atom_dinf_init (AtomDINF * dinf, AtomsContext * context)
870 {
871   atom_header_set (&dinf->header, FOURCC_dinf, 0, 0);
872   atom_dref_init (&dinf->dref, context);
873 }
874
875 static void
876 atom_dinf_clear (AtomDINF * dinf)
877 {
878   atom_clear (&dinf->header);
879   atom_dref_clear (&dinf->dref);
880 }
881
882 static void
883 atom_minf_init (AtomMINF * minf, AtomsContext * context)
884 {
885   atom_header_set (&minf->header, FOURCC_minf, 0, 0);
886
887   minf->vmhd = NULL;
888   minf->smhd = NULL;
889   minf->hmhd = NULL;
890
891   if (context->flavor == ATOMS_TREE_FLAVOR_MOV) {
892     minf->hdlr = atom_hdlr_new ();
893     minf->hdlr->component_type = FOURCC_dhlr;
894     minf->hdlr->handler_type = FOURCC_alis;
895   } else {
896     minf->hdlr = NULL;
897   }
898   atom_dinf_init (&minf->dinf, context);
899   atom_stbl_init (&minf->stbl);
900 }
901
902 static void
903 atom_minf_clear_handlers (AtomMINF * minf)
904 {
905   if (minf->vmhd) {
906     atom_vmhd_free (minf->vmhd);
907     minf->vmhd = NULL;
908   }
909   if (minf->smhd) {
910     atom_smhd_free (minf->smhd);
911     minf->smhd = NULL;
912   }
913   if (minf->hmhd) {
914     atom_hmhd_free (minf->hmhd);
915     minf->hmhd = NULL;
916   }
917 }
918
919 static void
920 atom_minf_clear (AtomMINF * minf)
921 {
922   atom_clear (&minf->header);
923   atom_minf_clear_handlers (minf);
924   if (minf->hdlr) {
925     atom_hdlr_free (minf->hdlr);
926   }
927   atom_dinf_clear (&minf->dinf);
928   atom_stbl_clear (&minf->stbl);
929 }
930
931 static void
932 atom_mdhd_init (AtomMDHD * mdhd)
933 {
934   guint8 flags[3] = { 0, 0, 0 };
935
936   atom_full_init (&mdhd->header, FOURCC_mdhd, 0, 0, 0, flags);
937   common_time_info_init (&mdhd->time_info);
938   mdhd->language_code = 0;
939   mdhd->quality = 0;
940 }
941
942 static void
943 atom_mdhd_clear (AtomMDHD * mdhd)
944 {
945   atom_full_clear (&mdhd->header);
946 }
947
948 static void
949 atom_mdia_init (AtomMDIA * mdia, AtomsContext * context)
950 {
951   atom_header_set (&mdia->header, FOURCC_mdia, 0, 0);
952
953   atom_mdhd_init (&mdia->mdhd);
954   atom_hdlr_init (&mdia->hdlr);
955   atom_minf_init (&mdia->minf, context);
956 }
957
958 static void
959 atom_mdia_clear (AtomMDIA * mdia)
960 {
961   atom_clear (&mdia->header);
962   atom_mdhd_clear (&mdia->mdhd);
963   atom_hdlr_clear (&mdia->hdlr);
964   atom_minf_clear (&mdia->minf);
965 }
966
967 static void
968 atom_tkhd_init (AtomTKHD * tkhd, AtomsContext * context)
969 {
970   /*
971    * flags info
972    * 1 -> track enabled
973    * 2 -> track in movie
974    * 4 -> track in preview
975    */
976   guint8 flags[3] = { 0, 0, 7 };
977
978   atom_full_init (&tkhd->header, FOURCC_tkhd, 0, 0, 0, flags);
979
980   tkhd->creation_time = tkhd->modification_time = get_current_qt_time ();
981   tkhd->duration = 0;
982   tkhd->track_ID = 0;
983   tkhd->reserved = 0;
984
985   tkhd->reserved2[0] = tkhd->reserved2[1] = 0;
986   tkhd->layer = 0;
987   tkhd->alternate_group = 0;
988   tkhd->volume = 0;
989   tkhd->reserved3 = 0;
990   memset (tkhd->matrix, 0, sizeof (guint32) * 9);
991   tkhd->matrix[0] = 1 << 16;
992   tkhd->matrix[4] = 1 << 16;
993   tkhd->matrix[8] = 16384 << 16;
994   tkhd->width = 0;
995   tkhd->height = 0;
996 }
997
998 static void
999 atom_tkhd_clear (AtomTKHD * tkhd)
1000 {
1001   atom_full_clear (&tkhd->header);
1002 }
1003
1004 static void
1005 atom_trak_init (AtomTRAK * trak, AtomsContext * context)
1006 {
1007   atom_header_set (&trak->header, FOURCC_trak, 0, 0);
1008
1009   atom_tkhd_init (&trak->tkhd, context);
1010   trak->edts = NULL;
1011   atom_mdia_init (&trak->mdia, context);
1012 }
1013
1014 AtomTRAK *
1015 atom_trak_new (AtomsContext * context)
1016 {
1017   AtomTRAK *trak = g_new0 (AtomTRAK, 1);
1018
1019   atom_trak_init (trak, context);
1020   return trak;
1021 }
1022
1023 static void
1024 atom_trak_clear (AtomTRAK * trak)
1025 {
1026   atom_clear (&trak->header);
1027   atom_tkhd_clear (&trak->tkhd);
1028   if (trak->edts)
1029     atom_edts_free (trak->edts);
1030   atom_mdia_clear (&trak->mdia);
1031 }
1032
1033 static void
1034 atom_trak_free (AtomTRAK * trak)
1035 {
1036   atom_trak_clear (trak);
1037   g_free (trak);
1038 }
1039
1040 static void
1041 atom_ilst_init (AtomILST * ilst)
1042 {
1043   atom_header_set (&ilst->header, FOURCC_ilst, 0, 0);
1044   ilst->entries = NULL;
1045 }
1046
1047 static AtomILST *
1048 atom_ilst_new (void)
1049 {
1050   AtomILST *ilst = g_new0 (AtomILST, 1);
1051
1052   atom_ilst_init (ilst);
1053   return ilst;
1054 }
1055
1056 static void
1057 atom_ilst_free (AtomILST * ilst)
1058 {
1059   if (ilst->entries)
1060     atom_info_list_free (ilst->entries);
1061   atom_clear (&ilst->header);
1062   g_free (ilst);
1063 }
1064
1065 static void
1066 atom_meta_init (AtomMETA * meta)
1067 {
1068   guint8 flags[3] = { 0, 0, 0 };
1069
1070   atom_full_init (&meta->header, FOURCC_meta, 0, 0, 0, flags);
1071   atom_hdlr_init (&meta->hdlr);
1072   /* FIXME (ISOM says this is always 0) */
1073   meta->hdlr.component_type = FOURCC_mhlr;
1074   meta->hdlr.handler_type = FOURCC_mdir;
1075   meta->ilst = NULL;
1076 }
1077
1078 static AtomMETA *
1079 atom_meta_new (void)
1080 {
1081   AtomMETA *meta = g_new0 (AtomMETA, 1);
1082
1083   atom_meta_init (meta);
1084   return meta;
1085 }
1086
1087 static void
1088 atom_meta_free (AtomMETA * meta)
1089 {
1090   atom_full_clear (&meta->header);
1091   atom_hdlr_clear (&meta->hdlr);
1092   if (meta->ilst)
1093     atom_ilst_free (meta->ilst);
1094   meta->ilst = NULL;
1095   g_free (meta);
1096 }
1097
1098 static void
1099 atom_udta_init (AtomUDTA * udta)
1100 {
1101   atom_header_set (&udta->header, FOURCC_udta, 0, 0);
1102   udta->meta = NULL;
1103 }
1104
1105 static AtomUDTA *
1106 atom_udta_new (void)
1107 {
1108   AtomUDTA *udta = g_new0 (AtomUDTA, 1);
1109
1110   atom_udta_init (udta);
1111   return udta;
1112 }
1113
1114 static void
1115 atom_udta_free (AtomUDTA * udta)
1116 {
1117   atom_clear (&udta->header);
1118   if (udta->meta)
1119     atom_meta_free (udta->meta);
1120   udta->meta = NULL;
1121   if (udta->entries)
1122     atom_info_list_free (udta->entries);
1123   g_free (udta);
1124 }
1125
1126 static void
1127 atom_tag_data_init (AtomTagData * data)
1128 {
1129   guint8 flags[] = { 0, 0, 0 };
1130
1131   atom_full_init (&data->header, FOURCC_data, 0, 0, 0, flags);
1132 }
1133
1134 static void
1135 atom_tag_data_clear (AtomTagData * data)
1136 {
1137   atom_full_clear (&data->header);
1138   g_free (data->data);
1139   data->datalen = 0;
1140 }
1141
1142 /*
1143  * Fourcc is the tag fourcc
1144  * flags will be truncated to 24bits
1145  */
1146 static AtomTag *
1147 atom_tag_new (guint32 fourcc, guint32 flags_as_uint)
1148 {
1149   AtomTag *tag = g_new0 (AtomTag, 1);
1150
1151   tag->header.type = fourcc;
1152   atom_tag_data_init (&tag->data);
1153   atom_full_set_flags_as_uint (&tag->data.header, flags_as_uint);
1154   return tag;
1155 }
1156
1157 static void
1158 atom_tag_free (AtomTag * tag)
1159 {
1160   atom_clear (&tag->header);
1161   atom_tag_data_clear (&tag->data);
1162   g_free (tag);
1163 }
1164
1165 static void
1166 atom_mvhd_init (AtomMVHD * mvhd)
1167 {
1168   guint8 flags[3] = { 0, 0, 0 };
1169
1170   atom_full_init (&(mvhd->header), FOURCC_mvhd, sizeof (AtomMVHD), 0, 0, flags);
1171
1172   common_time_info_init (&mvhd->time_info);
1173
1174   mvhd->prefered_rate = 1 << 16;
1175   mvhd->volume = 1 << 8;
1176   mvhd->reserved3 = 0;
1177   memset (mvhd->reserved4, 0, sizeof (guint32[2]));
1178
1179   memset (mvhd->matrix, 0, sizeof (guint32[9]));
1180   mvhd->matrix[0] = 1 << 16;
1181   mvhd->matrix[4] = 1 << 16;
1182   mvhd->matrix[8] = 16384 << 16;
1183
1184   mvhd->preview_time = 0;
1185   mvhd->preview_duration = 0;
1186   mvhd->poster_time = 0;
1187   mvhd->selection_time = 0;
1188   mvhd->selection_duration = 0;
1189   mvhd->current_time = 0;
1190
1191   mvhd->next_track_id = 1;
1192 }
1193
1194 static void
1195 atom_mvhd_clear (AtomMVHD * mvhd)
1196 {
1197   atom_full_clear (&mvhd->header);
1198 }
1199
1200 static void
1201 atom_mehd_init (AtomMEHD * mehd)
1202 {
1203   guint8 flags[3] = { 0, 0, 0 };
1204
1205   atom_full_init (&mehd->header, FOURCC_mehd, 0, 0, 1, flags);
1206   mehd->fragment_duration = 0;
1207 }
1208
1209 static void
1210 atom_mvex_init (AtomMVEX * mvex)
1211 {
1212   atom_header_set (&mvex->header, FOURCC_mvex, 0, 0);
1213   atom_mehd_init (&mvex->mehd);
1214   mvex->trexs = NULL;
1215 }
1216
1217 static void
1218 atom_moov_init (AtomMOOV * moov, AtomsContext * context)
1219 {
1220   atom_header_set (&(moov->header), FOURCC_moov, 0, 0);
1221   atom_mvhd_init (&(moov->mvhd));
1222   atom_mvex_init (&(moov->mvex));
1223   moov->udta = NULL;
1224   moov->traks = NULL;
1225   moov->context = *context;
1226 }
1227
1228 AtomMOOV *
1229 atom_moov_new (AtomsContext * context)
1230 {
1231   AtomMOOV *moov = g_new0 (AtomMOOV, 1);
1232
1233   atom_moov_init (moov, context);
1234   return moov;
1235 }
1236
1237 static void
1238 atom_trex_free (AtomTREX * trex)
1239 {
1240   atom_full_clear (&trex->header);
1241   g_free (trex);
1242 }
1243
1244 static void
1245 atom_mvex_clear (AtomMVEX * mvex)
1246 {
1247   GList *walker;
1248
1249   atom_clear (&mvex->header);
1250   walker = mvex->trexs;
1251   while (walker) {
1252     atom_trex_free ((AtomTREX *) walker->data);
1253     walker = g_list_next (walker);
1254   }
1255   g_list_free (mvex->trexs);
1256   mvex->trexs = NULL;
1257 }
1258
1259 void
1260 atom_moov_free (AtomMOOV * moov)
1261 {
1262   GList *walker;
1263
1264   atom_clear (&moov->header);
1265   atom_mvhd_clear (&moov->mvhd);
1266
1267   walker = moov->traks;
1268   while (walker) {
1269     atom_trak_free ((AtomTRAK *) walker->data);
1270     walker = g_list_next (walker);
1271   }
1272   g_list_free (moov->traks);
1273   moov->traks = NULL;
1274
1275   if (moov->udta) {
1276     atom_udta_free (moov->udta);
1277     moov->udta = NULL;
1278   }
1279
1280   atom_mvex_clear (&moov->mvex);
1281
1282   g_free (moov);
1283 }
1284
1285 /* -- end of init / free -- */
1286
1287 /* -- copy data functions -- */
1288
1289 static guint8
1290 atom_full_get_version (AtomFull * full)
1291 {
1292   return full->version;
1293 }
1294
1295 static guint64
1296 common_time_info_copy_data (TimeInfo * ti, gboolean trunc_to_32,
1297     guint8 ** buffer, guint64 * size, guint64 * offset)
1298 {
1299   guint64 original_offset = *offset;
1300
1301   if (trunc_to_32) {
1302     prop_copy_uint32 ((guint32) ti->creation_time, buffer, size, offset);
1303     prop_copy_uint32 ((guint32) ti->modification_time, buffer, size, offset);
1304     prop_copy_uint32 (ti->timescale, buffer, size, offset);
1305     prop_copy_uint32 ((guint32) ti->duration, buffer, size, offset);
1306   } else {
1307     prop_copy_uint64 (ti->creation_time, buffer, size, offset);
1308     prop_copy_uint64 (ti->modification_time, buffer, size, offset);
1309     prop_copy_uint32 (ti->timescale, buffer, size, offset);
1310     prop_copy_uint64 (ti->duration, buffer, size, offset);
1311   }
1312   return *offset - original_offset;
1313 }
1314
1315 static void
1316 atom_write_size (guint8 ** buffer, guint64 * size, guint64 * offset,
1317     guint64 atom_pos)
1318 {
1319   /* this only works for non-extended atom size, which is OK
1320    * (though it could be made to do mem_move, etc and write extended size) */
1321   prop_copy_uint32 (*offset - atom_pos, buffer, size, &atom_pos);
1322 }
1323
1324 guint64
1325 atom_copy_data (Atom * atom, guint8 ** buffer, guint64 * size, guint64 * offset)
1326 {
1327   guint64 original_offset = *offset;
1328
1329   /* copies type and size */
1330   prop_copy_uint32 (atom->size, buffer, size, offset);
1331   prop_copy_fourcc (atom->type, buffer, size, offset);
1332
1333   /* extended size needed */
1334   if (atom->size == 1) {
1335     /* really should not happen other than with mdat atom;
1336      * would be a problem for size (re)write code, not to mention memory */
1337     g_return_val_if_fail (atom->type == FOURCC_mdat, 0);
1338     prop_copy_uint64 (atom->extended_size, buffer, size, offset);
1339   }
1340
1341   return *offset - original_offset;
1342 }
1343
1344 static guint64
1345 atom_full_copy_data (AtomFull * atom, guint8 ** buffer, guint64 * size,
1346     guint64 * offset)
1347 {
1348   guint64 original_offset = *offset;
1349
1350   if (!atom_copy_data (&atom->header, buffer, size, offset)) {
1351     return 0;
1352   }
1353
1354   prop_copy_uint8 (atom->version, buffer, size, offset);
1355   prop_copy_uint8_array (atom->flags, 3, buffer, size, offset);
1356
1357   atom_write_size (buffer, size, offset, original_offset);
1358   return *offset - original_offset;
1359 }
1360
1361 static guint64
1362 atom_info_list_copy_data (GList * ai, guint8 ** buffer, guint64 * size,
1363     guint64 * offset)
1364 {
1365   guint64 original_offset = *offset;
1366
1367   while (ai) {
1368     AtomInfo *info = (AtomInfo *) ai->data;
1369
1370     if (!info->copy_data_func (info->atom, buffer, size, offset)) {
1371       return 0;
1372     }
1373     ai = g_list_next (ai);
1374   }
1375
1376   return *offset - original_offset;
1377 }
1378
1379 static guint64
1380 atom_data_copy_data (AtomData * data, guint8 ** buffer, guint64 * size,
1381     guint64 * offset)
1382 {
1383   guint64 original_offset = *offset;
1384
1385   if (!atom_copy_data (&data->header, buffer, size, offset)) {
1386     return 0;
1387   }
1388   if (data->datalen)
1389     prop_copy_uint8_array (data->data, data->datalen, buffer, size, offset);
1390
1391   atom_write_size (buffer, size, offset, original_offset);
1392   return *offset - original_offset;
1393 }
1394
1395 static guint64
1396 atom_uuid_copy_data (AtomUUID * uuid, guint8 ** buffer, guint64 * size,
1397     guint64 * offset)
1398 {
1399   guint64 original_offset = *offset;
1400
1401   if (!atom_copy_data (&uuid->header, buffer, size, offset)) {
1402     return 0;
1403   }
1404   prop_copy_uint8_array (uuid->uuid, 16, buffer, size, offset);
1405   if (uuid->datalen)
1406     prop_copy_uint8_array (uuid->data, uuid->datalen, buffer, size, offset);
1407
1408   atom_write_size (buffer, size, offset, original_offset);
1409   return *offset - original_offset;
1410 }
1411
1412 guint64
1413 atom_ftyp_copy_data (AtomFTYP * ftyp, guint8 ** buffer, guint64 * size,
1414     guint64 * offset)
1415 {
1416   guint64 original_offset = *offset;
1417
1418   if (!atom_copy_data (&ftyp->header, buffer, size, offset)) {
1419     return 0;
1420   }
1421   prop_copy_fourcc (ftyp->major_brand, buffer, size, offset);
1422   prop_copy_uint32 (ftyp->version, buffer, size, offset);
1423
1424   prop_copy_fourcc_array (ftyp->compatible_brands, ftyp->compatible_brands_size,
1425       buffer, size, offset);
1426
1427   atom_write_size (buffer, size, offset, original_offset);
1428   return *offset - original_offset;
1429 }
1430
1431 guint64
1432 atom_mvhd_copy_data (AtomMVHD * atom, guint8 ** buffer, guint64 * size,
1433     guint64 * offset)
1434 {
1435   guint8 version;
1436   guint64 original_offset = *offset;
1437
1438   if (!atom_full_copy_data (&(atom->header), buffer, size, offset)) {
1439     return 0;
1440   }
1441
1442   version = atom_full_get_version (&(atom->header));
1443   if (version == 0) {
1444     common_time_info_copy_data (&atom->time_info, TRUE, buffer, size, offset);
1445   } else if (version == 1) {
1446     common_time_info_copy_data (&atom->time_info, FALSE, buffer, size, offset);
1447   } else {
1448     *offset = original_offset;
1449     return 0;
1450   }
1451
1452   prop_copy_uint32 (atom->prefered_rate, buffer, size, offset);
1453   prop_copy_uint16 (atom->volume, buffer, size, offset);
1454   prop_copy_uint16 (atom->reserved3, buffer, size, offset);
1455   prop_copy_uint32_array (atom->reserved4, 2, buffer, size, offset);
1456   prop_copy_uint32_array (atom->matrix, 9, buffer, size, offset);
1457   prop_copy_uint32 (atom->preview_time, buffer, size, offset);
1458   prop_copy_uint32 (atom->preview_duration, buffer, size, offset);
1459   prop_copy_uint32 (atom->poster_time, buffer, size, offset);
1460   prop_copy_uint32 (atom->selection_time, buffer, size, offset);
1461   prop_copy_uint32 (atom->selection_duration, buffer, size, offset);
1462   prop_copy_uint32 (atom->current_time, buffer, size, offset);
1463
1464   prop_copy_uint32 (atom->next_track_id, buffer, size, offset);
1465
1466   atom_write_size (buffer, size, offset, original_offset);
1467   return *offset - original_offset;
1468 }
1469
1470 static guint64
1471 atom_tkhd_copy_data (AtomTKHD * tkhd, guint8 ** buffer, guint64 * size,
1472     guint64 * offset)
1473 {
1474   guint64 original_offset = *offset;
1475
1476   if (!atom_full_copy_data (&tkhd->header, buffer, size, offset)) {
1477     return 0;
1478   }
1479
1480   if (atom_full_get_version (&tkhd->header) == 0) {
1481     prop_copy_uint32 ((guint32) tkhd->creation_time, buffer, size, offset);
1482     prop_copy_uint32 ((guint32) tkhd->modification_time, buffer, size, offset);
1483     prop_copy_uint32 (tkhd->track_ID, buffer, size, offset);
1484     prop_copy_uint32 (tkhd->reserved, buffer, size, offset);
1485     prop_copy_uint32 ((guint32) tkhd->duration, buffer, size, offset);
1486   } else {
1487     prop_copy_uint64 (tkhd->creation_time, buffer, size, offset);
1488     prop_copy_uint64 (tkhd->modification_time, buffer, size, offset);
1489     prop_copy_uint32 (tkhd->track_ID, buffer, size, offset);
1490     prop_copy_uint32 (tkhd->reserved, buffer, size, offset);
1491     prop_copy_uint64 (tkhd->duration, buffer, size, offset);
1492   }
1493
1494   prop_copy_uint32_array (tkhd->reserved2, 2, buffer, size, offset);
1495   prop_copy_uint16 (tkhd->layer, buffer, size, offset);
1496   prop_copy_uint16 (tkhd->alternate_group, buffer, size, offset);
1497   prop_copy_uint16 (tkhd->volume, buffer, size, offset);
1498   prop_copy_uint16 (tkhd->reserved3, buffer, size, offset);
1499   prop_copy_uint32_array (tkhd->matrix, 9, buffer, size, offset);
1500
1501   prop_copy_uint32 (tkhd->width, buffer, size, offset);
1502   prop_copy_uint32 (tkhd->height, buffer, size, offset);
1503
1504   atom_write_size (buffer, size, offset, original_offset);
1505   return *offset - original_offset;
1506 }
1507
1508 static guint64
1509 atom_hdlr_copy_data (AtomHDLR * hdlr, guint8 ** buffer, guint64 * size,
1510     guint64 * offset)
1511 {
1512   guint64 original_offset = *offset;
1513
1514   if (!atom_full_copy_data (&hdlr->header, buffer, size, offset)) {
1515     return 0;
1516   }
1517
1518   prop_copy_fourcc (hdlr->component_type, buffer, size, offset);
1519   prop_copy_fourcc (hdlr->handler_type, buffer, size, offset);
1520   prop_copy_fourcc (hdlr->manufacturer, buffer, size, offset);
1521   prop_copy_uint32 (hdlr->flags, buffer, size, offset);
1522   prop_copy_uint32 (hdlr->flags_mask, buffer, size, offset);
1523
1524   prop_copy_size_string ((guint8 *) hdlr->name, strlen (hdlr->name), buffer,
1525       size, offset);
1526
1527   atom_write_size (buffer, size, offset, original_offset);
1528   return *offset - original_offset;
1529 }
1530
1531 static guint64
1532 atom_vmhd_copy_data (AtomVMHD * vmhd, guint8 ** buffer, guint64 * size,
1533     guint64 * offset)
1534 {
1535   guint64 original_offset = *offset;
1536
1537   if (!atom_full_copy_data (&vmhd->header, buffer, size, offset)) {
1538     return 0;
1539   }
1540   prop_copy_uint16 (vmhd->graphics_mode, buffer, size, offset);
1541   prop_copy_uint16_array (vmhd->opcolor, 3, buffer, size, offset);
1542
1543   atom_write_size (buffer, size, offset, original_offset);
1544   return original_offset - *offset;
1545 }
1546
1547 static guint64
1548 atom_smhd_copy_data (AtomSMHD * smhd, guint8 ** buffer, guint64 * size,
1549     guint64 * offset)
1550 {
1551   guint64 original_offset = *offset;
1552
1553   if (!atom_full_copy_data (&smhd->header, buffer, size, offset)) {
1554     return 0;
1555   }
1556   prop_copy_uint16 (smhd->balance, buffer, size, offset);
1557   prop_copy_uint16 (smhd->reserved, buffer, size, offset);
1558
1559   atom_write_size (buffer, size, offset, original_offset);
1560   return original_offset - *offset;
1561 }
1562
1563 static guint64
1564 atom_hmhd_copy_data (AtomHMHD * hmhd, guint8 ** buffer, guint64 * size,
1565     guint64 * offset)
1566 {
1567   guint64 original_offset = *offset;
1568
1569   if (!atom_full_copy_data (&hmhd->header, buffer, size, offset)) {
1570     return 0;
1571   }
1572   prop_copy_uint16 (hmhd->max_pdu_size, buffer, size, offset);
1573   prop_copy_uint16 (hmhd->avg_pdu_size, buffer, size, offset);
1574   prop_copy_uint32 (hmhd->max_bitrate, buffer, size, offset);
1575   prop_copy_uint32 (hmhd->avg_bitrate, buffer, size, offset);
1576   prop_copy_uint32 (hmhd->sliding_avg_bitrate, buffer, size, offset);
1577
1578   atom_write_size (buffer, size, offset, original_offset);
1579   return original_offset - *offset;
1580 }
1581
1582 static gboolean
1583 atom_url_same_file_flag (AtomURL * url)
1584 {
1585   return (url->header.flags[2] & 0x1) == 1;
1586 }
1587
1588 static guint64
1589 atom_url_copy_data (AtomURL * url, guint8 ** buffer, guint64 * size,
1590     guint64 * offset)
1591 {
1592   guint64 original_offset = *offset;
1593
1594   if (!atom_full_copy_data (&url->header, buffer, size, offset)) {
1595     return 0;
1596   }
1597
1598   if (!atom_url_same_file_flag (url)) {
1599     prop_copy_null_terminated_string (url->location, buffer, size, offset);
1600   }
1601
1602   atom_write_size (buffer, size, offset, original_offset);
1603   return original_offset - *offset;
1604 }
1605
1606 guint64
1607 atom_stts_copy_data (AtomSTTS * stts, guint8 ** buffer, guint64 * size,
1608     guint64 * offset)
1609 {
1610   guint64 original_offset = *offset;
1611   guint i;
1612
1613   if (!atom_full_copy_data (&stts->header, buffer, size, offset)) {
1614     return 0;
1615   }
1616
1617   prop_copy_uint32 (atom_array_get_len (&stts->entries), buffer, size, offset);
1618   /* minimize realloc */
1619   prop_copy_ensure_buffer (buffer, size, offset,
1620       8 * atom_array_get_len (&stts->entries));
1621   for (i = 0; i < atom_array_get_len (&stts->entries); i++) {
1622     STTSEntry *entry = &atom_array_index (&stts->entries, i);
1623
1624     prop_copy_uint32 (entry->sample_count, buffer, size, offset);
1625     prop_copy_int32 (entry->sample_delta, buffer, size, offset);
1626   }
1627
1628   atom_write_size (buffer, size, offset, original_offset);
1629   return *offset - original_offset;
1630 }
1631
1632 static guint64
1633 atom_sample_entry_copy_data (SampleTableEntry * se, guint8 ** buffer,
1634     guint64 * size, guint64 * offset)
1635 {
1636   guint64 original_offset = *offset;
1637
1638   if (!atom_copy_data (&se->header, buffer, size, offset)) {
1639     return 0;
1640   }
1641
1642   prop_copy_uint8_array (se->reserved, 6, buffer, size, offset);
1643   prop_copy_uint16 (se->data_reference_index, buffer, size, offset);
1644
1645   return *offset - original_offset;
1646 }
1647
1648 static guint64
1649 atom_esds_copy_data (AtomESDS * esds, guint8 ** buffer, guint64 * size,
1650     guint64 * offset)
1651 {
1652   guint64 original_offset = *offset;
1653
1654   if (!atom_full_copy_data (&esds->header, buffer, size, offset)) {
1655     return 0;
1656   }
1657   if (!desc_es_descriptor_copy_data (&esds->es, buffer, size, offset)) {
1658     return 0;
1659   }
1660
1661   atom_write_size (buffer, size, offset, original_offset);
1662   return *offset - original_offset;
1663 }
1664
1665 static guint64
1666 atom_frma_copy_data (AtomFRMA * frma, guint8 ** buffer,
1667     guint64 * size, guint64 * offset)
1668 {
1669   guint64 original_offset = *offset;
1670
1671   if (!atom_copy_data (&(frma->header), buffer, size, offset))
1672     return 0;
1673
1674   prop_copy_fourcc (frma->media_type, buffer, size, offset);
1675
1676   atom_write_size (buffer, size, offset, original_offset);
1677   return *offset - original_offset;
1678 }
1679
1680 static guint64
1681 atom_mp4s_copy_data (SampleTableEntryMP4S * mp4s, guint8 ** buffer,
1682     guint64 * size, guint64 * offset)
1683 {
1684   guint64 original_offset = *offset;
1685
1686   if (!atom_sample_entry_copy_data (&mp4s->se, buffer, size, offset)) {
1687     return 0;
1688   }
1689   if (!atom_esds_copy_data (&mp4s->es, buffer, size, offset)) {
1690     return 0;
1691   }
1692
1693   atom_write_size (buffer, size, offset, original_offset);
1694   return *offset - original_offset;
1695 }
1696
1697 static guint64
1698 atom_hint_sample_entry_copy_data (AtomHintSampleEntry * hse, guint8 ** buffer,
1699     guint64 * size, guint64 * offset)
1700 {
1701   guint64 original_offset = *offset;
1702
1703   if (!atom_sample_entry_copy_data (&hse->se, buffer, size, offset)) {
1704     return 0;
1705   }
1706
1707   prop_copy_uint32 (hse->size, buffer, size, offset);
1708   prop_copy_uint8_array (hse->data, hse->size, buffer, size, offset);
1709
1710   atom_write_size (buffer, size, offset, original_offset);
1711   return *offset - original_offset;
1712 }
1713
1714 static guint64
1715 sample_entry_mp4a_copy_data (SampleTableEntryMP4A * mp4a, guint8 ** buffer,
1716     guint64 * size, guint64 * offset)
1717 {
1718   guint64 original_offset = *offset;
1719
1720   if (!atom_sample_entry_copy_data (&mp4a->se, buffer, size, offset)) {
1721     return 0;
1722   }
1723
1724   prop_copy_uint16 (mp4a->version, buffer, size, offset);
1725   prop_copy_uint16 (mp4a->revision_level, buffer, size, offset);
1726   prop_copy_uint32 (mp4a->vendor, buffer, size, offset);
1727   prop_copy_uint16 (mp4a->channels, buffer, size, offset);
1728   prop_copy_uint16 (mp4a->sample_size, buffer, size, offset);
1729   prop_copy_uint16 (mp4a->compression_id, buffer, size, offset);
1730   prop_copy_uint16 (mp4a->packet_size, buffer, size, offset);
1731   prop_copy_uint32 (mp4a->sample_rate, buffer, size, offset);
1732
1733   /* this should always be 0 for mp4 flavor */
1734   if (mp4a->version == 1) {
1735     prop_copy_uint32 (mp4a->samples_per_packet, buffer, size, offset);
1736     prop_copy_uint32 (mp4a->bytes_per_packet, buffer, size, offset);
1737     prop_copy_uint32 (mp4a->bytes_per_frame, buffer, size, offset);
1738     prop_copy_uint32 (mp4a->bytes_per_sample, buffer, size, offset);
1739   }
1740
1741   if (mp4a->extension_atoms) {
1742     if (!atom_info_list_copy_data (mp4a->extension_atoms, buffer, size, offset))
1743       return 0;
1744   }
1745
1746   atom_write_size (buffer, size, offset, original_offset);
1747   return *offset - original_offset;
1748 }
1749
1750 static guint64
1751 sample_entry_mp4v_copy_data (SampleTableEntryMP4V * mp4v, guint8 ** buffer,
1752     guint64 * size, guint64 * offset)
1753 {
1754   guint64 original_offset = *offset;
1755
1756   if (!atom_sample_entry_copy_data (&mp4v->se, buffer, size, offset)) {
1757     return 0;
1758   }
1759
1760   prop_copy_uint16 (mp4v->version, buffer, size, offset);
1761   prop_copy_uint16 (mp4v->revision_level, buffer, size, offset);
1762   prop_copy_fourcc (mp4v->vendor, buffer, size, offset);
1763   prop_copy_uint32 (mp4v->temporal_quality, buffer, size, offset);
1764   prop_copy_uint32 (mp4v->spatial_quality, buffer, size, offset);
1765
1766   prop_copy_uint16 (mp4v->width, buffer, size, offset);
1767   prop_copy_uint16 (mp4v->height, buffer, size, offset);
1768
1769   prop_copy_uint32 (mp4v->horizontal_resolution, buffer, size, offset);
1770   prop_copy_uint32 (mp4v->vertical_resolution, buffer, size, offset);
1771   prop_copy_uint32 (mp4v->datasize, buffer, size, offset);
1772
1773   prop_copy_uint16 (mp4v->frame_count, buffer, size, offset);
1774
1775   prop_copy_fixed_size_string ((guint8 *) mp4v->compressor, 32, buffer, size,
1776       offset);
1777
1778   prop_copy_uint16 (mp4v->depth, buffer, size, offset);
1779   prop_copy_uint16 (mp4v->color_table_id, buffer, size, offset);
1780
1781   /* extra atoms */
1782   if (mp4v->extension_atoms &&
1783       !atom_info_list_copy_data (mp4v->extension_atoms, buffer, size, offset))
1784     return 0;
1785
1786   atom_write_size (buffer, size, offset, original_offset);
1787   return *offset - original_offset;
1788 }
1789
1790 guint64
1791 atom_stsz_copy_data (AtomSTSZ * stsz, guint8 ** buffer, guint64 * size,
1792     guint64 * offset)
1793 {
1794   guint64 original_offset = *offset;
1795   guint i;
1796
1797   if (!atom_full_copy_data (&stsz->header, buffer, size, offset)) {
1798     return 0;
1799   }
1800
1801   prop_copy_uint32 (stsz->sample_size, buffer, size, offset);
1802   prop_copy_uint32 (stsz->table_size, buffer, size, offset);
1803   if (stsz->sample_size == 0) {
1804     /* minimize realloc */
1805     prop_copy_ensure_buffer (buffer, size, offset, 4 * stsz->table_size);
1806     /* entry count must match sample count */
1807     g_assert (atom_array_get_len (&stsz->entries) == stsz->table_size);
1808     for (i = 0; i < atom_array_get_len (&stsz->entries); i++) {
1809       prop_copy_uint32 (atom_array_index (&stsz->entries, i), buffer, size,
1810           offset);
1811     }
1812   }
1813
1814   atom_write_size (buffer, size, offset, original_offset);
1815   return *offset - original_offset;
1816 }
1817
1818 guint64
1819 atom_stsc_copy_data (AtomSTSC * stsc, guint8 ** buffer, guint64 * size,
1820     guint64 * offset)
1821 {
1822   guint64 original_offset = *offset;
1823   guint i;
1824
1825   if (!atom_full_copy_data (&stsc->header, buffer, size, offset)) {
1826     return 0;
1827   }
1828
1829   prop_copy_uint32 (atom_array_get_len (&stsc->entries), buffer, size, offset);
1830   /* minimize realloc */
1831   prop_copy_ensure_buffer (buffer, size, offset,
1832       12 * atom_array_get_len (&stsc->entries));
1833
1834   for (i = 0; i < atom_array_get_len (&stsc->entries); i++) {
1835     STSCEntry *entry = &atom_array_index (&stsc->entries, i);
1836
1837     prop_copy_uint32 (entry->first_chunk, buffer, size, offset);
1838     prop_copy_uint32 (entry->samples_per_chunk, buffer, size, offset);
1839     prop_copy_uint32 (entry->sample_description_index, buffer, size, offset);
1840   }
1841
1842   atom_write_size (buffer, size, offset, original_offset);
1843   return *offset - original_offset;
1844 }
1845
1846 guint64
1847 atom_ctts_copy_data (AtomCTTS * ctts, guint8 ** buffer, guint64 * size,
1848     guint64 * offset)
1849 {
1850   guint64 original_offset = *offset;
1851   guint i;
1852
1853   if (!atom_full_copy_data (&ctts->header, buffer, size, offset)) {
1854     return 0;
1855   }
1856
1857   prop_copy_uint32 (atom_array_get_len (&ctts->entries), buffer, size, offset);
1858   /* minimize realloc */
1859   prop_copy_ensure_buffer (buffer, size, offset,
1860       8 * atom_array_get_len (&ctts->entries));
1861   for (i = 0; i < atom_array_get_len (&ctts->entries); i++) {
1862     CTTSEntry *entry = &atom_array_index (&ctts->entries, i);
1863
1864     prop_copy_uint32 (entry->samplecount, buffer, size, offset);
1865     prop_copy_uint32 (entry->sampleoffset, buffer, size, offset);
1866   }
1867
1868   atom_write_size (buffer, size, offset, original_offset);
1869   return *offset - original_offset;
1870 }
1871
1872 guint64
1873 atom_stco64_copy_data (AtomSTCO64 * stco64, guint8 ** buffer, guint64 * size,
1874     guint64 * offset)
1875 {
1876   guint64 original_offset = *offset;
1877   guint i;
1878   gboolean trunc_to_32 = stco64->header.header.type == FOURCC_stco;
1879
1880   if (!atom_full_copy_data (&stco64->header, buffer, size, offset)) {
1881     return 0;
1882   }
1883
1884   prop_copy_uint32 (atom_array_get_len (&stco64->entries), buffer, size,
1885       offset);
1886
1887   /* minimize realloc */
1888   prop_copy_ensure_buffer (buffer, size, offset,
1889       8 * atom_array_get_len (&stco64->entries));
1890   for (i = 0; i < atom_array_get_len (&stco64->entries); i++) {
1891     guint64 *value = &atom_array_index (&stco64->entries, i);
1892
1893     if (trunc_to_32) {
1894       prop_copy_uint32 ((guint32) * value, buffer, size, offset);
1895     } else {
1896       prop_copy_uint64 (*value, buffer, size, offset);
1897     }
1898   }
1899
1900   atom_write_size (buffer, size, offset, original_offset);
1901   return *offset - original_offset;
1902 }
1903
1904 guint64
1905 atom_stss_copy_data (AtomSTSS * stss, guint8 ** buffer, guint64 * size,
1906     guint64 * offset)
1907 {
1908   guint64 original_offset = *offset;
1909   guint i;
1910
1911   if (atom_array_get_len (&stss->entries) == 0) {
1912     /* FIXME not needing this atom might be confused with error while copying */
1913     return 0;
1914   }
1915
1916   if (!atom_full_copy_data (&stss->header, buffer, size, offset)) {
1917     return 0;
1918   }
1919
1920   prop_copy_uint32 (atom_array_get_len (&stss->entries), buffer, size, offset);
1921   /* minimize realloc */
1922   prop_copy_ensure_buffer (buffer, size, offset,
1923       4 * atom_array_get_len (&stss->entries));
1924   for (i = 0; i < atom_array_get_len (&stss->entries); i++) {
1925     prop_copy_uint32 (atom_array_index (&stss->entries, i), buffer, size,
1926         offset);
1927   }
1928
1929   atom_write_size (buffer, size, offset, original_offset);
1930   return *offset - original_offset;
1931 }
1932
1933 static guint64
1934 atom_stsd_copy_data (AtomSTSD * stsd, guint8 ** buffer, guint64 * size,
1935     guint64 * offset)
1936 {
1937   guint64 original_offset = *offset;
1938   GList *walker;
1939
1940   if (!atom_full_copy_data (&stsd->header, buffer, size, offset)) {
1941     return 0;
1942   }
1943
1944   prop_copy_uint32 (stsd->n_entries, buffer, size, offset);
1945
1946   for (walker = g_list_last (stsd->entries); walker != NULL;
1947       walker = g_list_previous (walker)) {
1948     SampleTableEntry *se = (SampleTableEntry *) walker->data;
1949
1950     switch (((Atom *) walker->data)->type) {
1951       case FOURCC_mp4a:
1952         if (!sample_entry_mp4a_copy_data ((SampleTableEntryMP4A *) walker->data,
1953                 buffer, size, offset)) {
1954           return 0;
1955         }
1956         break;
1957       case FOURCC_mp4s:
1958         if (!atom_mp4s_copy_data ((SampleTableEntryMP4S *) walker->data,
1959                 buffer, size, offset)) {
1960           return 0;
1961         }
1962         break;
1963       case FOURCC_mp4v:
1964         if (!sample_entry_mp4v_copy_data ((SampleTableEntryMP4V *) walker->data,
1965                 buffer, size, offset)) {
1966           return 0;
1967         }
1968         break;
1969       default:
1970         if (se->kind == VIDEO) {
1971           if (!sample_entry_mp4v_copy_data ((SampleTableEntryMP4V *)
1972                   walker->data, buffer, size, offset)) {
1973             return 0;
1974           }
1975         } else if (se->kind == AUDIO) {
1976           if (!sample_entry_mp4a_copy_data ((SampleTableEntryMP4A *)
1977                   walker->data, buffer, size, offset)) {
1978             return 0;
1979           }
1980         } else {
1981           if (!atom_hint_sample_entry_copy_data (
1982                   (AtomHintSampleEntry *) walker->data, buffer, size, offset)) {
1983             return 0;
1984           }
1985         }
1986         break;
1987     }
1988   }
1989
1990   atom_write_size (buffer, size, offset, original_offset);
1991   return *offset - original_offset;
1992 }
1993
1994 static guint64
1995 atom_stbl_copy_data (AtomSTBL * stbl, guint8 ** buffer, guint64 * size,
1996     guint64 * offset)
1997 {
1998   guint64 original_offset = *offset;
1999
2000   if (!atom_copy_data (&stbl->header, buffer, size, offset)) {
2001     return 0;
2002   }
2003
2004   if (!atom_stsd_copy_data (&stbl->stsd, buffer, size, offset)) {
2005     return 0;
2006   }
2007   if (!atom_stts_copy_data (&stbl->stts, buffer, size, offset)) {
2008     return 0;
2009   }
2010   /* this atom is optional, so let's check if we need it
2011    * (to avoid false error) */
2012   if (atom_array_get_len (&stbl->stss.entries)) {
2013     if (!atom_stss_copy_data (&stbl->stss, buffer, size, offset)) {
2014       return 0;
2015     }
2016   }
2017
2018   if (!atom_stsc_copy_data (&stbl->stsc, buffer, size, offset)) {
2019     return 0;
2020   }
2021   if (!atom_stsz_copy_data (&stbl->stsz, buffer, size, offset)) {
2022     return 0;
2023   }
2024   if (stbl->ctts && stbl->ctts->do_pts) {
2025     if (!atom_ctts_copy_data (stbl->ctts, buffer, size, offset)) {
2026       return 0;
2027     }
2028   }
2029   if (!atom_stco64_copy_data (&stbl->stco64, buffer, size, offset)) {
2030     return 0;
2031   }
2032
2033   atom_write_size (buffer, size, offset, original_offset);
2034   return original_offset - *offset;
2035 }
2036
2037
2038 static guint64
2039 atom_dref_copy_data (AtomDREF * dref, guint8 ** buffer, guint64 * size,
2040     guint64 * offset)
2041 {
2042   guint64 original_offset = *offset;
2043   GList *walker;
2044
2045   if (!atom_full_copy_data (&dref->header, buffer, size, offset)) {
2046     return 0;
2047   }
2048
2049   prop_copy_uint32 (g_list_length (dref->entries), buffer, size, offset);
2050
2051   walker = dref->entries;
2052   while (walker != NULL) {
2053     Atom *atom = (Atom *) walker->data;
2054
2055     if (atom->type == FOURCC_url_) {
2056       atom_url_copy_data ((AtomURL *) atom, buffer, size, offset);
2057     } else if (atom->type == FOURCC_alis) {
2058       atom_full_copy_data ((AtomFull *) atom, buffer, size, offset);
2059     } else {
2060       g_error ("Unsupported atom used inside dref atom");
2061     }
2062     walker = g_list_next (walker);
2063   }
2064
2065   atom_write_size (buffer, size, offset, original_offset);
2066   return *offset - original_offset;
2067 }
2068
2069 static guint64
2070 atom_dinf_copy_data (AtomDINF * dinf, guint8 ** buffer, guint64 * size,
2071     guint64 * offset)
2072 {
2073   guint64 original_offset = *offset;
2074
2075   if (!atom_copy_data (&dinf->header, buffer, size, offset)) {
2076     return 0;
2077   }
2078
2079   if (!atom_dref_copy_data (&dinf->dref, buffer, size, offset)) {
2080     return 0;
2081   }
2082
2083   atom_write_size (buffer, size, offset, original_offset);
2084   return original_offset - *offset;
2085 }
2086
2087 static guint64
2088 atom_minf_copy_data (AtomMINF * minf, guint8 ** buffer, guint64 * size,
2089     guint64 * offset)
2090 {
2091   guint64 original_offset = *offset;
2092
2093   if (!atom_copy_data (&minf->header, buffer, size, offset)) {
2094     return 0;
2095   }
2096
2097   if (minf->vmhd) {
2098     if (!atom_vmhd_copy_data (minf->vmhd, buffer, size, offset)) {
2099       return 0;
2100     }
2101   } else if (minf->smhd) {
2102     if (!atom_smhd_copy_data (minf->smhd, buffer, size, offset)) {
2103       return 0;
2104     }
2105   } else if (minf->hmhd) {
2106     if (!atom_hmhd_copy_data (minf->hmhd, buffer, size, offset)) {
2107       return 0;
2108     }
2109   }
2110
2111   if (minf->hdlr) {
2112     if (!atom_hdlr_copy_data (minf->hdlr, buffer, size, offset)) {
2113       return 0;
2114     }
2115   }
2116
2117   if (!atom_dinf_copy_data (&minf->dinf, buffer, size, offset)) {
2118     return 0;
2119   }
2120   if (!atom_stbl_copy_data (&minf->stbl, buffer, size, offset)) {
2121     return 0;
2122   }
2123
2124   atom_write_size (buffer, size, offset, original_offset);
2125   return *offset - original_offset;
2126 }
2127
2128 static guint64
2129 atom_mdhd_copy_data (AtomMDHD * mdhd, guint8 ** buffer, guint64 * size,
2130     guint64 * offset)
2131 {
2132   guint64 original_offset = *offset;
2133
2134   if (!atom_full_copy_data (&mdhd->header, buffer, size, offset)) {
2135     return 0;
2136   }
2137
2138   if (!common_time_info_copy_data (&mdhd->time_info,
2139           atom_full_get_version (&mdhd->header) == 0, buffer, size, offset)) {
2140     return 0;
2141   }
2142
2143   prop_copy_uint16 (mdhd->language_code, buffer, size, offset);
2144   prop_copy_uint16 (mdhd->quality, buffer, size, offset);
2145
2146   atom_write_size (buffer, size, offset, original_offset);
2147   return *offset - original_offset;
2148 }
2149
2150 static guint64
2151 atom_mdia_copy_data (AtomMDIA * mdia, guint8 ** buffer, guint64 * size,
2152     guint64 * offset)
2153 {
2154   guint64 original_offset = *offset;
2155
2156   if (!atom_copy_data (&mdia->header, buffer, size, offset)) {
2157     return 0;
2158   }
2159   if (!atom_mdhd_copy_data (&mdia->mdhd, buffer, size, offset)) {
2160     return 0;
2161   }
2162   if (!atom_hdlr_copy_data (&mdia->hdlr, buffer, size, offset)) {
2163     return 0;
2164   }
2165
2166   if (!atom_minf_copy_data (&mdia->minf, buffer, size, offset)) {
2167     return 0;
2168   }
2169
2170   atom_write_size (buffer, size, offset, original_offset);
2171   return *offset - original_offset;
2172 }
2173
2174 static guint64
2175 atom_elst_copy_data (AtomELST * elst, guint8 ** buffer, guint64 * size,
2176     guint64 * offset)
2177 {
2178   guint64 original_offset = *offset;
2179   GSList *walker;
2180
2181   if (!atom_full_copy_data (&elst->header, buffer, size, offset)) {
2182     return 0;
2183   }
2184
2185   prop_copy_uint32 (g_slist_length (elst->entries), buffer, size, offset);
2186
2187   for (walker = elst->entries; walker != NULL; walker = g_slist_next (walker)) {
2188     EditListEntry *entry = (EditListEntry *) walker->data;
2189     prop_copy_uint32 (entry->duration, buffer, size, offset);
2190     prop_copy_uint32 (entry->media_time, buffer, size, offset);
2191     prop_copy_uint32 (entry->media_rate, buffer, size, offset);
2192   }
2193   atom_write_size (buffer, size, offset, original_offset);
2194   return *offset - original_offset;
2195 }
2196
2197 static guint64
2198 atom_edts_copy_data (AtomEDTS * edts, guint8 ** buffer, guint64 * size,
2199     guint64 * offset)
2200 {
2201   guint64 original_offset = *offset;
2202
2203   if (!atom_copy_data (&(edts->header), buffer, size, offset))
2204     return 0;
2205
2206   if (!atom_elst_copy_data (&(edts->elst), buffer, size, offset))
2207     return 0;
2208
2209   atom_write_size (buffer, size, offset, original_offset);
2210   return *offset - original_offset;
2211 }
2212
2213 guint64
2214 atom_trak_copy_data (AtomTRAK * trak, guint8 ** buffer, guint64 * size,
2215     guint64 * offset)
2216 {
2217   guint64 original_offset = *offset;
2218
2219   if (!atom_copy_data (&trak->header, buffer, size, offset)) {
2220     return 0;
2221   }
2222   if (!atom_tkhd_copy_data (&trak->tkhd, buffer, size, offset)) {
2223     return 0;
2224   }
2225   if (trak->edts) {
2226     if (!atom_edts_copy_data (trak->edts, buffer, size, offset)) {
2227       return 0;
2228     }
2229   }
2230
2231   if (!atom_mdia_copy_data (&trak->mdia, buffer, size, offset)) {
2232     return 0;
2233   }
2234
2235   atom_write_size (buffer, size, offset, original_offset);
2236   return *offset - original_offset;
2237 }
2238
2239 static guint64
2240 atom_tag_data_copy_data (AtomTagData * data, guint8 ** buffer, guint64 * size,
2241     guint64 * offset)
2242 {
2243   guint64 original_offset = *offset;
2244
2245   if (!atom_full_copy_data (&data->header, buffer, size, offset)) {
2246     return 0;
2247   }
2248
2249   prop_copy_uint32 (data->reserved, buffer, size, offset);
2250   prop_copy_uint8_array (data->data, data->datalen, buffer, size, offset);
2251
2252   atom_write_size (buffer, size, offset, original_offset);
2253   return *offset - original_offset;
2254 }
2255
2256 static guint64
2257 atom_tag_copy_data (AtomTag * tag, guint8 ** buffer, guint64 * size,
2258     guint64 * offset)
2259 {
2260   guint64 original_offset = *offset;
2261
2262   if (!atom_copy_data (&tag->header, buffer, size, offset)) {
2263     return 0;
2264   }
2265
2266   if (!atom_tag_data_copy_data (&tag->data, buffer, size, offset)) {
2267     return 0;
2268   }
2269
2270   atom_write_size (buffer, size, offset, original_offset);
2271   return *offset - original_offset;
2272 }
2273
2274 static guint64
2275 atom_ilst_copy_data (AtomILST * ilst, guint8 ** buffer, guint64 * size,
2276     guint64 * offset)
2277 {
2278   guint64 original_offset = *offset;
2279
2280   if (!atom_copy_data (&ilst->header, buffer, size, offset)) {
2281     return 0;
2282   }
2283   /* extra atoms */
2284   if (ilst->entries &&
2285       !atom_info_list_copy_data (ilst->entries, buffer, size, offset))
2286     return 0;
2287
2288   atom_write_size (buffer, size, offset, original_offset);
2289   return *offset - original_offset;
2290 }
2291
2292 static guint64
2293 atom_meta_copy_data (AtomMETA * meta, guint8 ** buffer, guint64 * size,
2294     guint64 * offset)
2295 {
2296   guint64 original_offset = *offset;
2297
2298   if (!atom_full_copy_data (&meta->header, buffer, size, offset)) {
2299     return 0;
2300   }
2301   if (!atom_hdlr_copy_data (&meta->hdlr, buffer, size, offset)) {
2302     return 0;
2303   }
2304   if (meta->ilst) {
2305     if (!atom_ilst_copy_data (meta->ilst, buffer, size, offset)) {
2306       return 0;
2307     }
2308   }
2309
2310   atom_write_size (buffer, size, offset, original_offset);
2311   return *offset - original_offset;
2312 }
2313
2314 static guint64
2315 atom_udta_copy_data (AtomUDTA * udta, guint8 ** buffer, guint64 * size,
2316     guint64 * offset)
2317 {
2318   guint64 original_offset = *offset;
2319
2320   if (!atom_copy_data (&udta->header, buffer, size, offset)) {
2321     return 0;
2322   }
2323   if (udta->meta) {
2324     if (!atom_meta_copy_data (udta->meta, buffer, size, offset)) {
2325       return 0;
2326     }
2327   }
2328   if (udta->entries) {
2329     /* extra atoms */
2330     if (!atom_info_list_copy_data (udta->entries, buffer, size, offset))
2331       return 0;
2332   }
2333
2334   atom_write_size (buffer, size, offset, original_offset);
2335   return *offset - original_offset;
2336 }
2337
2338 static guint64
2339 atom_mehd_copy_data (AtomMEHD * mehd, guint8 ** buffer, guint64 * size,
2340     guint64 * offset)
2341 {
2342   guint64 original_offset = *offset;
2343
2344   if (!atom_full_copy_data (&mehd->header, buffer, size, offset)) {
2345     return 0;
2346   }
2347
2348   prop_copy_uint64 (mehd->fragment_duration, buffer, size, offset);
2349
2350   atom_write_size (buffer, size, offset, original_offset);
2351   return *offset - original_offset;
2352 }
2353
2354 static guint64
2355 atom_trex_copy_data (AtomTREX * trex, guint8 ** buffer, guint64 * size,
2356     guint64 * offset)
2357 {
2358   guint64 original_offset = *offset;
2359
2360   if (!atom_full_copy_data (&trex->header, buffer, size, offset)) {
2361     return 0;
2362   }
2363
2364   prop_copy_uint32 (trex->track_ID, buffer, size, offset);
2365   prop_copy_uint32 (trex->default_sample_description_index, buffer, size,
2366       offset);
2367   prop_copy_uint32 (trex->default_sample_duration, buffer, size, offset);
2368   prop_copy_uint32 (trex->default_sample_size, buffer, size, offset);
2369   prop_copy_uint32 (trex->default_sample_flags, buffer, size, offset);
2370
2371   atom_write_size (buffer, size, offset, original_offset);
2372   return *offset - original_offset;
2373 }
2374
2375 static guint64
2376 atom_mvex_copy_data (AtomMVEX * mvex, guint8 ** buffer, guint64 * size,
2377     guint64 * offset)
2378 {
2379   guint64 original_offset = *offset;
2380   GList *walker;
2381
2382   if (!atom_copy_data (&mvex->header, buffer, size, offset)) {
2383     return 0;
2384   }
2385
2386   if (!atom_mehd_copy_data (&mvex->mehd, buffer, size, offset)) {
2387     return 0;
2388   }
2389
2390   walker = g_list_first (mvex->trexs);
2391   while (walker != NULL) {
2392     if (!atom_trex_copy_data ((AtomTREX *) walker->data, buffer, size, offset)) {
2393       return 0;
2394     }
2395     walker = g_list_next (walker);
2396   }
2397
2398   atom_write_size (buffer, size, offset, original_offset);
2399   return *offset - original_offset;
2400 }
2401
2402 guint64
2403 atom_moov_copy_data (AtomMOOV * atom, guint8 ** buffer, guint64 * size,
2404     guint64 * offset)
2405 {
2406   guint64 original_offset = *offset;
2407   GList *walker;
2408
2409   if (!atom_copy_data (&(atom->header), buffer, size, offset))
2410     return 0;
2411
2412   if (!atom_mvhd_copy_data (&(atom->mvhd), buffer, size, offset))
2413     return 0;
2414
2415   walker = g_list_first (atom->traks);
2416   while (walker != NULL) {
2417     if (!atom_trak_copy_data ((AtomTRAK *) walker->data, buffer, size, offset)) {
2418       return 0;
2419     }
2420     walker = g_list_next (walker);
2421   }
2422
2423   if (atom->udta) {
2424     if (!atom_udta_copy_data (atom->udta, buffer, size, offset)) {
2425       return 0;
2426     }
2427   }
2428
2429   if (atom->fragmented) {
2430     if (!atom_mvex_copy_data (&atom->mvex, buffer, size, offset)) {
2431       return 0;
2432     }
2433   }
2434
2435   atom_write_size (buffer, size, offset, original_offset);
2436   return *offset - original_offset;
2437 }
2438
2439 static guint64
2440 atom_wave_copy_data (AtomWAVE * wave, guint8 ** buffer,
2441     guint64 * size, guint64 * offset)
2442 {
2443   guint64 original_offset = *offset;
2444
2445   if (!atom_copy_data (&(wave->header), buffer, size, offset))
2446     return 0;
2447
2448   if (wave->extension_atoms) {
2449     if (!atom_info_list_copy_data (wave->extension_atoms, buffer, size, offset))
2450       return 0;
2451   }
2452
2453   atom_write_size (buffer, size, offset, original_offset);
2454   return *offset - original_offset;
2455 }
2456
2457 /* -- end of copy data functions -- */
2458
2459 /* -- general functions, API and support functions */
2460
2461 /* add samples to tables */
2462
2463 static void
2464 atom_stsc_add_new_entry (AtomSTSC * stsc, guint32 first_chunk, guint32 nsamples)
2465 {
2466   STSCEntry nentry;
2467   gint len;
2468
2469   if ((len = atom_array_get_len (&stsc->entries)) &&
2470       ((atom_array_index (&stsc->entries, len - 1)).samples_per_chunk ==
2471           nsamples))
2472     return;
2473
2474   nentry.first_chunk = first_chunk;
2475   nentry.samples_per_chunk = nsamples;
2476   nentry.sample_description_index = 1;
2477   atom_array_append (&stsc->entries, nentry, 128);
2478 }
2479
2480 static void
2481 atom_stts_add_entry (AtomSTTS * stts, guint32 sample_count, gint32 sample_delta)
2482 {
2483   STTSEntry *entry = NULL;
2484
2485   if (G_LIKELY (atom_array_get_len (&stts->entries) != 0))
2486     entry = &atom_array_index (&stts->entries,
2487         atom_array_get_len (&stts->entries) - 1);
2488
2489   if (entry && entry->sample_delta == sample_delta) {
2490     entry->sample_count += sample_count;
2491   } else {
2492     STTSEntry nentry;
2493
2494     nentry.sample_count = sample_count;
2495     nentry.sample_delta = sample_delta;
2496     atom_array_append (&stts->entries, nentry, 256);
2497   }
2498 }
2499
2500 static void
2501 atom_stsz_add_entry (AtomSTSZ * stsz, guint32 nsamples, guint32 size)
2502 {
2503   guint32 i;
2504
2505   stsz->table_size += nsamples;
2506   if (stsz->sample_size != 0) {
2507     /* it is constant size, we don't need entries */
2508     return;
2509   }
2510   for (i = 0; i < nsamples; i++) {
2511     atom_array_append (&stsz->entries, size, 1024);
2512   }
2513 }
2514
2515 static guint32
2516 atom_stco64_get_entry_count (AtomSTCO64 * stco64)
2517 {
2518   return atom_array_get_len (&stco64->entries);
2519 }
2520
2521 static void
2522 atom_stco64_add_entry (AtomSTCO64 * stco64, guint64 entry)
2523 {
2524   atom_array_append (&stco64->entries, entry, 256);
2525   if (entry > G_MAXUINT32)
2526     stco64->header.header.type = FOURCC_co64;
2527 }
2528
2529 static void
2530 atom_stss_add_entry (AtomSTSS * stss, guint32 sample)
2531 {
2532   atom_array_append (&stss->entries, sample, 512);
2533 }
2534
2535 static void
2536 atom_stbl_add_stss_entry (AtomSTBL * stbl)
2537 {
2538   guint32 sample_index = stbl->stsz.table_size;
2539
2540   atom_stss_add_entry (&stbl->stss, sample_index);
2541 }
2542
2543 static void
2544 atom_ctts_add_entry (AtomCTTS * ctts, guint32 nsamples, guint32 offset)
2545 {
2546   CTTSEntry *entry = NULL;
2547
2548   if (G_LIKELY (atom_array_get_len (&ctts->entries) != 0))
2549     entry = &atom_array_index (&ctts->entries,
2550         atom_array_get_len (&ctts->entries) - 1);
2551
2552   if (entry == NULL || entry->sampleoffset != offset) {
2553     CTTSEntry nentry;
2554
2555     nentry.samplecount = nsamples;
2556     nentry.sampleoffset = offset;
2557     atom_array_append (&ctts->entries, nentry, 256);
2558     if (offset != 0)
2559       ctts->do_pts = TRUE;
2560   } else {
2561     entry->samplecount += nsamples;
2562   }
2563 }
2564
2565 static void
2566 atom_stbl_add_ctts_entry (AtomSTBL * stbl, guint32 nsamples, guint32 offset)
2567 {
2568   if (stbl->ctts == NULL) {
2569     stbl->ctts = atom_ctts_new ();
2570   }
2571   atom_ctts_add_entry (stbl->ctts, nsamples, offset);
2572 }
2573
2574 void
2575 atom_stbl_add_samples (AtomSTBL * stbl, guint32 nsamples, guint32 delta,
2576     guint32 size, guint64 chunk_offset, gboolean sync, gint64 pts_offset)
2577 {
2578   atom_stts_add_entry (&stbl->stts, nsamples, delta);
2579   atom_stsz_add_entry (&stbl->stsz, nsamples, size);
2580   atom_stco64_add_entry (&stbl->stco64, chunk_offset);
2581   atom_stsc_add_new_entry (&stbl->stsc,
2582       atom_stco64_get_entry_count (&stbl->stco64), nsamples);
2583   if (sync)
2584     atom_stbl_add_stss_entry (stbl);
2585   /* always store to arrange for consistent content */
2586   atom_stbl_add_ctts_entry (stbl, nsamples, pts_offset);
2587 }
2588
2589 void
2590 atom_trak_add_samples (AtomTRAK * trak, guint32 nsamples, guint32 delta,
2591     guint32 size, guint64 chunk_offset, gboolean sync, gint64 pts_offset)
2592 {
2593   AtomSTBL *stbl = &trak->mdia.minf.stbl;
2594   atom_stbl_add_samples (stbl, nsamples, delta, size, chunk_offset, sync,
2595       pts_offset);
2596 }
2597
2598 /* trak and moov molding */
2599
2600 guint32
2601 atom_trak_get_timescale (AtomTRAK * trak)
2602 {
2603   return trak->mdia.mdhd.time_info.timescale;
2604 }
2605
2606 guint32
2607 atom_trak_get_id (AtomTRAK * trak)
2608 {
2609   return trak->tkhd.track_ID;
2610 }
2611
2612 static void
2613 atom_trak_set_id (AtomTRAK * trak, guint32 id)
2614 {
2615   trak->tkhd.track_ID = id;
2616 }
2617
2618 static void
2619 atom_moov_add_trex (AtomMOOV * moov, AtomTREX * trex)
2620 {
2621   moov->mvex.trexs = g_list_append (moov->mvex.trexs, trex);
2622 }
2623
2624 static AtomTREX *
2625 atom_trex_new (AtomTRAK * trak)
2626 {
2627   guint8 flags[3] = { 0, 0, 0 };
2628   AtomTREX *trex = g_new0 (AtomTREX, 1);
2629
2630   atom_full_init (&trex->header, FOURCC_trex, 0, 0, 0, flags);
2631
2632   trex->track_ID = trak->tkhd.track_ID;
2633   trex->default_sample_description_index = 1;
2634   trex->default_sample_duration = 0;
2635   trex->default_sample_size = 0;
2636   trex->default_sample_flags = 0;
2637
2638   return trex;
2639 }
2640
2641 void
2642 atom_moov_add_trak (AtomMOOV * moov, AtomTRAK * trak)
2643 {
2644   atom_trak_set_id (trak, moov->mvhd.next_track_id++);
2645   moov->traks = g_list_append (moov->traks, trak);
2646   /* additional trak means also new trex */
2647   atom_moov_add_trex (moov, atom_trex_new (trak));
2648 }
2649
2650 static guint64
2651 atom_trak_get_duration (AtomTRAK * trak)
2652 {
2653   return trak->tkhd.duration;
2654 }
2655
2656 static guint64
2657 atom_stts_get_total_duration (AtomSTTS * stts)
2658 {
2659   guint i;
2660   guint64 sum = 0;
2661
2662   for (i = 0; i < atom_array_get_len (&stts->entries); i++) {
2663     STTSEntry *entry = &atom_array_index (&stts->entries, i);
2664
2665     sum += (guint64) (entry->sample_count) * entry->sample_delta;
2666   }
2667   return sum;
2668 }
2669
2670 static void
2671 atom_trak_update_duration (AtomTRAK * trak, guint64 moov_timescale)
2672 {
2673   trak->mdia.mdhd.time_info.duration =
2674       atom_stts_get_total_duration (&trak->mdia.minf.stbl.stts);
2675   if (trak->mdia.mdhd.time_info.timescale != 0) {
2676     trak->tkhd.duration =
2677         gst_util_uint64_scale (trak->mdia.mdhd.time_info.duration,
2678         moov_timescale, trak->mdia.mdhd.time_info.timescale);
2679   } else {
2680     trak->tkhd.duration = 0;
2681   }
2682 }
2683
2684 static guint32
2685 atom_moov_get_timescale (AtomMOOV * moov)
2686 {
2687   return moov->mvhd.time_info.timescale;
2688 }
2689
2690 void
2691 atom_moov_update_timescale (AtomMOOV * moov, guint32 timescale)
2692 {
2693   moov->mvhd.time_info.timescale = timescale;
2694 }
2695
2696 void
2697 atom_moov_update_duration (AtomMOOV * moov)
2698 {
2699   GList *traks = moov->traks;
2700   guint64 dur, duration = 0;
2701
2702   while (traks) {
2703     AtomTRAK *trak = (AtomTRAK *) traks->data;
2704
2705     atom_trak_update_duration (trak, atom_moov_get_timescale (moov));
2706     dur = atom_trak_get_duration (trak);
2707     if (dur > duration)
2708       duration = dur;
2709     traks = g_list_next (traks);
2710   }
2711   moov->mvhd.time_info.duration = duration;
2712   moov->mvex.mehd.fragment_duration = duration;
2713 }
2714
2715 void
2716 atom_moov_set_fragmented (AtomMOOV * moov, gboolean fragmented)
2717 {
2718   moov->fragmented = fragmented;
2719 }
2720
2721 void
2722 atom_stco64_chunks_add_offset (AtomSTCO64 * stco64, guint32 offset)
2723 {
2724   guint i;
2725
2726   for (i = 0; i < atom_array_get_len (&stco64->entries); i++) {
2727     guint64 *value = &atom_array_index (&stco64->entries, i);
2728
2729     *value += offset;
2730   }
2731 }
2732
2733 void
2734 atom_moov_chunks_add_offset (AtomMOOV * moov, guint32 offset)
2735 {
2736   GList *traks = moov->traks;
2737
2738   while (traks) {
2739     AtomTRAK *trak = (AtomTRAK *) traks->data;
2740
2741     atom_stco64_chunks_add_offset (&trak->mdia.minf.stbl.stco64, offset);
2742     traks = g_list_next (traks);
2743   }
2744 }
2745
2746 /*
2747  * Meta tags functions
2748  */
2749 static void
2750 atom_moov_init_metatags (AtomMOOV * moov, AtomsContext * context)
2751 {
2752   if (!moov->udta) {
2753     moov->udta = atom_udta_new ();
2754   }
2755   if (context->flavor != ATOMS_TREE_FLAVOR_3GP) {
2756     if (!moov->udta->meta) {
2757       moov->udta->meta = atom_meta_new ();
2758     }
2759     if (!moov->udta->meta->ilst) {
2760       moov->udta->meta->ilst = atom_ilst_new ();
2761     }
2762   }
2763 }
2764
2765 static void
2766 atom_tag_data_alloc_data (AtomTagData * data, guint size)
2767 {
2768   if (data->data != NULL) {
2769     g_free (data->data);
2770   }
2771   data->data = g_new0 (guint8, size);
2772   data->datalen = size;
2773 }
2774
2775 static void
2776 atom_moov_append_tag (AtomMOOV * moov, AtomInfo * tag)
2777 {
2778   GList **entries;
2779
2780   atom_moov_init_metatags (moov, &moov->context);
2781   if (moov->udta->meta)
2782     entries = &moov->udta->meta->ilst->entries;
2783   else
2784     entries = &moov->udta->entries;
2785   *entries = g_list_append (*entries, tag);
2786 }
2787
2788 void
2789 atom_moov_add_tag (AtomMOOV * moov, guint32 fourcc, guint32 flags,
2790     const guint8 * data, guint size)
2791 {
2792   AtomTag *tag;
2793   AtomTagData *tdata;
2794
2795   tag = atom_tag_new (fourcc, flags);
2796   tdata = &tag->data;
2797   atom_tag_data_alloc_data (tdata, size);
2798   g_memmove (tdata->data, data, size);
2799
2800   atom_moov_append_tag (moov,
2801       build_atom_info_wrapper ((Atom *) tag, atom_tag_copy_data,
2802           atom_tag_free));
2803 }
2804
2805 void
2806 atom_moov_add_str_tag (AtomMOOV * moov, guint32 fourcc, const gchar * value)
2807 {
2808   gint len = strlen (value);
2809
2810   if (len > 0)
2811     atom_moov_add_tag (moov, fourcc, METADATA_TEXT_FLAG, (guint8 *) value, len);
2812 }
2813
2814 void
2815 atom_moov_add_uint_tag (AtomMOOV * moov, guint32 fourcc, guint32 flags,
2816     guint32 value)
2817 {
2818   guint8 data[8] = { 0, };
2819
2820   if (flags) {
2821     GST_WRITE_UINT16_BE (data, value);
2822     atom_moov_add_tag (moov, fourcc, flags, data, 2);
2823   } else {
2824     GST_WRITE_UINT32_BE (data + 2, value);
2825     atom_moov_add_tag (moov, fourcc, flags, data, 8);
2826   }
2827 }
2828
2829 static GstBuffer *
2830 _gst_buffer_new_wrapped (gpointer mem, gsize size, GFreeFunc free_func)
2831 {
2832   GstBuffer *buf;
2833
2834   buf = gst_buffer_new ();
2835   gst_buffer_take_memory (buf, -1,
2836       gst_memory_new_wrapped (free_func ? 0 : GST_MEMORY_FLAG_READONLY,
2837           mem, free_func, size, 0, size));
2838
2839   return buf;
2840 }
2841
2842 void
2843 atom_moov_add_blob_tag (AtomMOOV * moov, guint8 * data, guint size)
2844 {
2845   AtomData *data_atom;
2846   GstBuffer *buf;
2847   guint len;
2848   guint32 fourcc;
2849
2850   if (size < 8)
2851     return;
2852
2853   /* blob is unparsed atom;
2854    * extract size and fourcc, and wrap remainder in data atom */
2855   len = GST_READ_UINT32_BE (data);
2856   fourcc = GST_READ_UINT32_LE (data + 4);
2857   if (len > size)
2858     return;
2859
2860   buf = _gst_buffer_new_wrapped (data + 8, len - 8, NULL);
2861   data_atom = atom_data_new_from_gst_buffer (fourcc, buf);
2862   gst_buffer_unref (buf);
2863
2864   atom_moov_append_tag (moov,
2865       build_atom_info_wrapper ((Atom *) data_atom, atom_data_copy_data,
2866           atom_data_free));
2867 }
2868
2869 void
2870 atom_moov_add_3gp_tag (AtomMOOV * moov, guint32 fourcc, guint8 * data,
2871     guint size)
2872 {
2873   AtomData *data_atom;
2874   GstBuffer *buf;
2875   guint8 *bdata;
2876
2877   /* need full atom */
2878   bdata = g_malloc (size + 4);
2879   /* full atom: version and flags */
2880   GST_WRITE_UINT32_BE (bdata, 0);
2881   memcpy (bdata + 4, data, size);
2882
2883   buf = _gst_buffer_new_wrapped (bdata, size + 4, g_free);
2884   data_atom = atom_data_new_from_gst_buffer (fourcc, buf);
2885   gst_buffer_unref (buf);
2886
2887   atom_moov_append_tag (moov,
2888       build_atom_info_wrapper ((Atom *) data_atom, atom_data_copy_data,
2889           atom_data_free));
2890 }
2891
2892 guint16
2893 language_code (const char *lang)
2894 {
2895   g_return_val_if_fail (lang != NULL, 0);
2896   g_return_val_if_fail (strlen (lang) == 3, 0);
2897
2898   return (((lang[0] - 0x60) & 0x1F) << 10) + (((lang[1] - 0x60) & 0x1F) << 5) +
2899       ((lang[2] - 0x60) & 0x1F);
2900 }
2901
2902 void
2903 atom_moov_add_3gp_str_int_tag (AtomMOOV * moov, guint32 fourcc,
2904     const gchar * value, gint16 ivalue)
2905 {
2906   gint len = 0, size = 0;
2907   guint8 *data;
2908
2909   if (value) {
2910     len = strlen (value);
2911     size = len + 3;
2912   }
2913
2914   if (ivalue >= 0)
2915     size += 2;
2916
2917   data = g_malloc (size + 3);
2918   /* language tag and null-terminated UTF-8 string */
2919   if (value) {
2920     GST_WRITE_UINT16_BE (data, language_code (GST_QT_MUX_DEFAULT_TAG_LANGUAGE));
2921     /* include 0 terminator */
2922     memcpy (data + 2, value, len + 1);
2923   }
2924   /* 16-bit unsigned int if standalone, otherwise 8-bit */
2925   if (ivalue >= 0) {
2926     if (size == 2)
2927       GST_WRITE_UINT16_BE (data + size - 2, ivalue);
2928     else {
2929       GST_WRITE_UINT8 (data + size - 2, ivalue & 0xFF);
2930       size--;
2931     }
2932   }
2933
2934   atom_moov_add_3gp_tag (moov, fourcc, data, size);
2935   g_free (data);
2936 }
2937
2938 void
2939 atom_moov_add_3gp_str_tag (AtomMOOV * moov, guint32 fourcc, const gchar * value)
2940 {
2941   atom_moov_add_3gp_str_int_tag (moov, fourcc, value, -1);
2942 }
2943
2944 void
2945 atom_moov_add_3gp_uint_tag (AtomMOOV * moov, guint32 fourcc, guint16 value)
2946 {
2947   atom_moov_add_3gp_str_int_tag (moov, fourcc, NULL, value);
2948 }
2949
2950 void
2951 atom_moov_add_xmp_tags (AtomMOOV * moov, GstBuffer * xmpbuffer)
2952 {
2953   AtomData *data_atom = NULL;
2954
2955   if (moov->context.flavor == ATOMS_TREE_FLAVOR_MOV) {
2956     if (xmpbuffer) {
2957       data_atom = atom_data_new_from_gst_buffer (FOURCC_XMP_, xmpbuffer);
2958       atom_moov_init_metatags (moov, &moov->context);
2959       moov->udta->entries = g_list_append (moov->udta->entries,
2960           build_atom_info_wrapper ((Atom *) data_atom, atom_data_copy_data,
2961               atom_data_free));
2962     }
2963   } else {
2964     GST_DEBUG ("Not adding xmp to moov atom, it is only used in 'mov' format");
2965   }
2966
2967 }
2968
2969 /*
2970  * Functions for specifying media types
2971  */
2972
2973 static void
2974 atom_minf_set_audio (AtomMINF * minf)
2975 {
2976   atom_minf_clear_handlers (minf);
2977   minf->smhd = atom_smhd_new ();
2978 }
2979
2980 static void
2981 atom_minf_set_video (AtomMINF * minf, AtomsContext * context)
2982 {
2983   atom_minf_clear_handlers (minf);
2984   minf->vmhd = atom_vmhd_new (context);
2985 }
2986
2987 static void
2988 atom_hdlr_set_type (AtomHDLR * hdlr, AtomsContext * context, guint32 comp_type,
2989     guint32 hdlr_type)
2990 {
2991   if (context->flavor == ATOMS_TREE_FLAVOR_MOV) {
2992     hdlr->component_type = comp_type;
2993   }
2994   hdlr->handler_type = hdlr_type;
2995 }
2996
2997 static void
2998 atom_hdlr_set_name (AtomHDLR * hdlr, const char *name)
2999 {
3000   if (hdlr->name)
3001     g_free (hdlr->name);
3002   hdlr->name = g_strdup (name);
3003 }
3004
3005 static void
3006 atom_mdia_set_hdlr_type_audio (AtomMDIA * mdia, AtomsContext * context)
3007 {
3008   atom_hdlr_set_type (&mdia->hdlr, context, FOURCC_mhlr, FOURCC_soun);
3009   /* Some players (low-end hardware) check for this name, which is what
3010    * QuickTime itself sets */
3011   atom_hdlr_set_name (&mdia->hdlr, "SoundHandler");
3012 }
3013
3014 static void
3015 atom_mdia_set_hdlr_type_video (AtomMDIA * mdia, AtomsContext * context)
3016 {
3017   atom_hdlr_set_type (&mdia->hdlr, context, FOURCC_mhlr, FOURCC_vide);
3018   /* Some players (low-end hardware) check for this name, which is what
3019    * QuickTime itself sets */
3020   atom_hdlr_set_name (&mdia->hdlr, "VideoHandler");
3021 }
3022
3023 static void
3024 atom_mdia_set_audio (AtomMDIA * mdia, AtomsContext * context)
3025 {
3026   atom_mdia_set_hdlr_type_audio (mdia, context);
3027   atom_minf_set_audio (&mdia->minf);
3028 }
3029
3030 static void
3031 atom_mdia_set_video (AtomMDIA * mdia, AtomsContext * context)
3032 {
3033   atom_mdia_set_hdlr_type_video (mdia, context);
3034   atom_minf_set_video (&mdia->minf, context);
3035 }
3036
3037 static void
3038 atom_tkhd_set_audio (AtomTKHD * tkhd)
3039 {
3040   tkhd->volume = 0x0100;
3041   tkhd->width = tkhd->height = 0;
3042 }
3043
3044 static void
3045 atom_tkhd_set_video (AtomTKHD * tkhd, AtomsContext * context, guint32 width,
3046     guint32 height)
3047 {
3048   tkhd->volume = 0;
3049
3050   /* qt and ISO base media do not contradict, and examples agree */
3051   tkhd->width = width;
3052   tkhd->height = height;
3053 }
3054
3055 static void
3056 atom_edts_add_entry (AtomEDTS * edts, EditListEntry * entry)
3057 {
3058   edts->elst.entries = g_slist_append (edts->elst.entries, entry);
3059 }
3060
3061 /* 
3062  * Adds a new entry to this trak edits list
3063  * duration is in the moov's timescale
3064  * media_time is the offset in the media time to start from (media's timescale)
3065  * rate is a 32 bits fixed-point
3066  */
3067 void
3068 atom_trak_add_elst_entry (AtomTRAK * trak, guint32 duration, guint32 media_time,
3069     guint32 rate)
3070 {
3071   EditListEntry *entry = g_new (EditListEntry, 1);
3072
3073   entry->duration = duration;
3074   entry->media_time = media_time;
3075   entry->media_rate = rate;
3076
3077   if (trak->edts == NULL) {
3078     trak->edts = atom_edts_new ();
3079   }
3080   atom_edts_add_entry (trak->edts, entry);
3081 }
3082
3083 /* re-negotiation is prevented at top-level, so only 1 entry expected.
3084  * Quite some more care here and elsewhere may be needed to
3085  * support several entries */
3086 static SampleTableEntryMP4A *
3087 atom_trak_add_audio_entry (AtomTRAK * trak, AtomsContext * context,
3088     guint32 type)
3089 {
3090   AtomSTSD *stsd = &trak->mdia.minf.stbl.stsd;
3091   SampleTableEntryMP4A *mp4a = sample_entry_mp4a_new ();
3092
3093   mp4a->se.header.type = type;
3094   mp4a->se.kind = AUDIO;
3095   mp4a->compression_id = -1;
3096   mp4a->se.data_reference_index = 1;
3097
3098   stsd->entries = g_list_prepend (stsd->entries, mp4a);
3099   stsd->n_entries++;
3100   return mp4a;
3101 }
3102
3103 static SampleTableEntryMP4V *
3104 atom_trak_add_video_entry (AtomTRAK * trak, AtomsContext * context,
3105     guint32 type)
3106 {
3107   SampleTableEntryMP4V *mp4v = sample_entry_mp4v_new (context);
3108   AtomSTSD *stsd = &trak->mdia.minf.stbl.stsd;
3109
3110   mp4v->se.header.type = type;
3111   mp4v->se.kind = VIDEO;
3112   mp4v->se.data_reference_index = 1;
3113   mp4v->horizontal_resolution = 72 << 16;
3114   mp4v->vertical_resolution = 72 << 16;
3115   if (context->flavor == ATOMS_TREE_FLAVOR_MOV) {
3116     mp4v->spatial_quality = 512;
3117     mp4v->temporal_quality = 512;
3118   }
3119
3120   stsd->entries = g_list_prepend (stsd->entries, mp4v);
3121   stsd->n_entries++;
3122   return mp4v;
3123 }
3124
3125 static void
3126 atom_trak_set_constant_size_samples (AtomTRAK * trak, guint32 sample_size)
3127 {
3128   trak->mdia.minf.stbl.stsz.sample_size = sample_size;
3129 }
3130
3131 static void
3132 atom_trak_set_audio (AtomTRAK * trak, AtomsContext * context)
3133 {
3134   atom_tkhd_set_audio (&trak->tkhd);
3135   atom_mdia_set_audio (&trak->mdia, context);
3136 }
3137
3138 static void
3139 atom_trak_set_video (AtomTRAK * trak, AtomsContext * context, guint32 width,
3140     guint32 height)
3141 {
3142   atom_tkhd_set_video (&trak->tkhd, context, width, height);
3143   atom_mdia_set_video (&trak->mdia, context);
3144 }
3145
3146 static void
3147 atom_trak_set_audio_commons (AtomTRAK * trak, AtomsContext * context,
3148     guint32 rate)
3149 {
3150   atom_trak_set_audio (trak, context);
3151   trak->mdia.mdhd.time_info.timescale = rate;
3152 }
3153
3154 static void
3155 atom_trak_set_video_commons (AtomTRAK * trak, AtomsContext * context,
3156     guint32 rate, guint32 width, guint32 height)
3157 {
3158   atom_trak_set_video (trak, context, width, height);
3159   trak->mdia.mdhd.time_info.timescale = rate;
3160   trak->tkhd.width = width << 16;
3161   trak->tkhd.height = height << 16;
3162 }
3163
3164 void
3165 atom_trak_set_audio_type (AtomTRAK * trak, AtomsContext * context,
3166     AudioSampleEntry * entry, guint32 scale, AtomInfo * ext, gint sample_size)
3167 {
3168   SampleTableEntryMP4A *ste;
3169
3170   atom_trak_set_audio_commons (trak, context, scale);
3171   atom_stsd_remove_entries (&trak->mdia.minf.stbl.stsd);
3172   ste = atom_trak_add_audio_entry (trak, context, entry->fourcc);
3173
3174   trak->is_video = FALSE;
3175   trak->is_h264 = FALSE;
3176
3177   ste->version = entry->version;
3178   ste->compression_id = entry->compression_id;
3179   ste->sample_size = entry->sample_size;
3180   ste->sample_rate = entry->sample_rate << 16;
3181   ste->channels = entry->channels;
3182
3183   ste->samples_per_packet = entry->samples_per_packet;
3184   ste->bytes_per_sample = entry->bytes_per_sample;
3185   ste->bytes_per_packet = entry->bytes_per_packet;
3186   ste->bytes_per_frame = entry->bytes_per_frame;
3187
3188   if (ext)
3189     ste->extension_atoms = g_list_prepend (ste->extension_atoms, ext);
3190
3191   /* 0 size means variable size */
3192   atom_trak_set_constant_size_samples (trak, sample_size);
3193 }
3194
3195 static AtomInfo *
3196 build_pasp_extension (AtomTRAK * trak, gint par_width, gint par_height)
3197 {
3198   AtomData *atom_data;
3199   GstBuffer *buf;
3200   guint8 *data;
3201
3202   data = g_malloc (8);
3203   /* ihdr = image header box */
3204   GST_WRITE_UINT32_BE (data, par_width);
3205   GST_WRITE_UINT32_BE (data + 4, par_height);
3206
3207   buf = _gst_buffer_new_wrapped (data, 8, g_free);
3208   atom_data = atom_data_new_from_gst_buffer (FOURCC_pasp, buf);
3209   gst_buffer_unref (buf);
3210
3211   return build_atom_info_wrapper ((Atom *) atom_data, atom_data_copy_data,
3212       atom_data_free);
3213 }
3214
3215 void
3216 atom_trak_set_video_type (AtomTRAK * trak, AtomsContext * context,
3217     VisualSampleEntry * entry, guint32 scale, GList * ext_atoms_list)
3218 {
3219   SampleTableEntryMP4V *ste;
3220   gint dwidth, dheight;
3221   gint par_n = 0, par_d = 0;
3222
3223   if ((entry->par_n != 1 || entry->par_d != 1) &&
3224       (entry->par_n != entry->par_d)) {
3225     par_n = entry->par_n;
3226     par_d = entry->par_d;
3227   }
3228
3229   dwidth = entry->width;
3230   dheight = entry->height;
3231   /* ISO file spec says track header w/h indicates track's visual presentation
3232    * (so this together with pixels w/h implicitly defines PAR) */
3233   if (par_n && (context->flavor != ATOMS_TREE_FLAVOR_MOV)) {
3234     if (par_n > par_d) {
3235       dwidth = entry->width * par_n / par_d;
3236       dheight = entry->height;
3237     } else {
3238       dwidth = entry->width * par_n / par_d;
3239       dheight = entry->height;
3240     }
3241   }
3242
3243   atom_trak_set_video_commons (trak, context, scale, dwidth, dheight);
3244   atom_stsd_remove_entries (&trak->mdia.minf.stbl.stsd);
3245   ste = atom_trak_add_video_entry (trak, context, entry->fourcc);
3246
3247   trak->is_video = TRUE;
3248   trak->is_h264 = (entry->fourcc == FOURCC_avc1);
3249
3250   ste->version = entry->version;
3251   ste->width = entry->width;
3252   ste->height = entry->height;
3253   ste->depth = entry->depth;
3254   ste->color_table_id = entry->color_table_id;
3255   ste->frame_count = entry->frame_count;
3256
3257   if (ext_atoms_list)
3258     ste->extension_atoms = g_list_concat (ste->extension_atoms, ext_atoms_list);
3259
3260   /* QT spec has a pasp extension atom in stsd that can hold PAR */
3261   if (par_n && (context->flavor == ATOMS_TREE_FLAVOR_MOV)) {
3262     ste->extension_atoms = g_list_append (ste->extension_atoms,
3263         build_pasp_extension (trak, par_n, par_d));
3264   }
3265 }
3266
3267 static void
3268 atom_mfhd_init (AtomMFHD * mfhd, guint32 sequence_number)
3269 {
3270   guint8 flags[3] = { 0, 0, 0 };
3271
3272   atom_full_init (&(mfhd->header), FOURCC_mfhd, 0, 0, 0, flags);
3273   mfhd->sequence_number = sequence_number;
3274 }
3275
3276 static void
3277 atom_moof_init (AtomMOOF * moof, AtomsContext * context,
3278     guint32 sequence_number)
3279 {
3280   atom_header_set (&moof->header, FOURCC_moof, 0, 0);
3281   atom_mfhd_init (&moof->mfhd, sequence_number);
3282   moof->trafs = NULL;
3283 }
3284
3285 AtomMOOF *
3286 atom_moof_new (AtomsContext * context, guint32 sequence_number)
3287 {
3288   AtomMOOF *moof = g_new0 (AtomMOOF, 1);
3289
3290   atom_moof_init (moof, context, sequence_number);
3291   return moof;
3292 }
3293
3294 static void
3295 atom_trun_free (AtomTRUN * trun)
3296 {
3297   atom_full_clear (&trun->header);
3298   atom_array_clear (&trun->entries);
3299   g_free (trun);
3300 }
3301
3302 static void
3303 atom_sdtp_free (AtomSDTP * sdtp)
3304 {
3305   atom_full_clear (&sdtp->header);
3306   atom_array_clear (&sdtp->entries);
3307   g_free (sdtp);
3308 }
3309
3310 void
3311 atom_traf_free (AtomTRAF * traf)
3312 {
3313   GList *walker;
3314
3315   walker = traf->truns;
3316   while (walker) {
3317     atom_trun_free ((AtomTRUN *) walker->data);
3318     walker = g_list_next (walker);
3319   }
3320   g_list_free (traf->truns);
3321   traf->truns = NULL;
3322
3323   walker = traf->sdtps;
3324   while (walker) {
3325     atom_sdtp_free ((AtomSDTP *) walker->data);
3326     walker = g_list_next (walker);
3327   }
3328   g_list_free (traf->sdtps);
3329   traf->sdtps = NULL;
3330
3331   g_free (traf);
3332 }
3333
3334 void
3335 atom_moof_free (AtomMOOF * moof)
3336 {
3337   GList *walker;
3338
3339   walker = moof->trafs;
3340   while (walker) {
3341     atom_traf_free ((AtomTRAF *) walker->data);
3342     walker = g_list_next (walker);
3343   }
3344   g_list_free (moof->trafs);
3345   moof->trafs = NULL;
3346
3347   g_free (moof);
3348 }
3349
3350 static guint64
3351 atom_mfhd_copy_data (AtomMFHD * mfhd, guint8 ** buffer, guint64 * size,
3352     guint64 * offset)
3353 {
3354   guint64 original_offset = *offset;
3355
3356   if (!atom_full_copy_data (&mfhd->header, buffer, size, offset)) {
3357     return 0;
3358   }
3359
3360   prop_copy_uint32 (mfhd->sequence_number, buffer, size, offset);
3361
3362   atom_write_size (buffer, size, offset, original_offset);
3363   return *offset - original_offset;
3364 }
3365
3366 static guint64
3367 atom_tfhd_copy_data (AtomTFHD * tfhd, guint8 ** buffer, guint64 * size,
3368     guint64 * offset)
3369 {
3370   guint64 original_offset = *offset;
3371   guint32 flags;
3372
3373   if (!atom_full_copy_data (&tfhd->header, buffer, size, offset)) {
3374     return 0;
3375   }
3376
3377   prop_copy_uint32 (tfhd->track_ID, buffer, size, offset);
3378
3379   flags = atom_full_get_flags_as_uint (&tfhd->header);
3380
3381   if (flags & TF_BASE_DATA_OFFSET)
3382     prop_copy_uint64 (tfhd->base_data_offset, buffer, size, offset);
3383   if (flags & TF_SAMPLE_DESCRIPTION_INDEX)
3384     prop_copy_uint32 (tfhd->sample_description_index, buffer, size, offset);
3385   if (flags & TF_DEFAULT_SAMPLE_DURATION)
3386     prop_copy_uint32 (tfhd->default_sample_duration, buffer, size, offset);
3387   if (flags & TF_DEFAULT_SAMPLE_SIZE)
3388     prop_copy_uint32 (tfhd->default_sample_size, buffer, size, offset);
3389   if (flags & TF_DEFAULT_SAMPLE_FLAGS)
3390     prop_copy_uint32 (tfhd->default_sample_flags, buffer, size, offset);
3391
3392   atom_write_size (buffer, size, offset, original_offset);
3393   return *offset - original_offset;
3394 }
3395
3396 static guint64
3397 atom_trun_copy_data (AtomTRUN * trun, guint8 ** buffer, guint64 * size,
3398     guint64 * offset, guint32 * data_offset)
3399 {
3400   guint64 original_offset = *offset;
3401   guint32 flags, i;
3402
3403   flags = atom_full_get_flags_as_uint (&trun->header);
3404
3405   /* if first trun in moof, forcibly add data_offset and record
3406    * where it must be written later on */
3407   if (data_offset && !*data_offset) {
3408     flags |= TR_DATA_OFFSET;
3409   } else {
3410     flags &= ~TR_DATA_OFFSET;
3411   }
3412
3413   atom_full_set_flags_as_uint (&trun->header, flags);
3414
3415   if (!atom_full_copy_data (&trun->header, buffer, size, offset)) {
3416     return 0;
3417   }
3418
3419   prop_copy_uint32 (trun->sample_count, buffer, size, offset);
3420
3421   if (flags & TR_DATA_OFFSET) {
3422     *data_offset = *offset;
3423     prop_copy_int32 (trun->data_offset, buffer, size, offset);
3424   }
3425   if (flags & TR_FIRST_SAMPLE_FLAGS)
3426     prop_copy_uint32 (trun->first_sample_flags, buffer, size, offset);
3427
3428   for (i = 0; i < atom_array_get_len (&trun->entries); i++) {
3429     TRUNSampleEntry *entry = &atom_array_index (&trun->entries, i);
3430
3431     if (flags & TR_SAMPLE_DURATION)
3432       prop_copy_uint32 (entry->sample_duration, buffer, size, offset);
3433     if (flags & TR_SAMPLE_SIZE)
3434       prop_copy_uint32 (entry->sample_size, buffer, size, offset);
3435     if (flags & TR_SAMPLE_FLAGS)
3436       prop_copy_uint32 (entry->sample_flags, buffer, size, offset);
3437     if (flags & TR_COMPOSITION_TIME_OFFSETS)
3438       prop_copy_uint32 (entry->sample_composition_time_offset,
3439           buffer, size, offset);
3440   }
3441
3442   atom_write_size (buffer, size, offset, original_offset);
3443   return *offset - original_offset;
3444 }
3445
3446 static guint64
3447 atom_sdtp_copy_data (AtomSDTP * sdtp, guint8 ** buffer, guint64 * size,
3448     guint64 * offset)
3449 {
3450   guint64 original_offset = *offset;
3451
3452   if (!atom_full_copy_data (&sdtp->header, buffer, size, offset)) {
3453     return 0;
3454   }
3455
3456   /* all entries at once */
3457   prop_copy_fixed_size_string (&atom_array_index (&sdtp->entries, 0),
3458       atom_array_get_len (&sdtp->entries), buffer, size, offset);
3459
3460   atom_write_size (buffer, size, offset, original_offset);
3461   return *offset - original_offset;
3462 }
3463
3464 static guint64
3465 atom_traf_copy_data (AtomTRAF * traf, guint8 ** buffer, guint64 * size,
3466     guint64 * offset, guint32 * data_offset)
3467 {
3468   guint64 original_offset = *offset;
3469   GList *walker;
3470
3471   if (!atom_copy_data (&traf->header, buffer, size, offset)) {
3472     return 0;
3473   }
3474   if (!atom_tfhd_copy_data (&traf->tfhd, buffer, size, offset)) {
3475     return 0;
3476   }
3477
3478   walker = g_list_first (traf->truns);
3479   while (walker != NULL) {
3480     if (!atom_trun_copy_data ((AtomTRUN *) walker->data, buffer, size, offset,
3481             data_offset)) {
3482       return 0;
3483     }
3484     walker = g_list_next (walker);
3485   }
3486
3487   walker = g_list_first (traf->sdtps);
3488   while (walker != NULL) {
3489     if (!atom_sdtp_copy_data ((AtomSDTP *) walker->data, buffer, size, offset)) {
3490       return 0;
3491     }
3492     walker = g_list_next (walker);
3493   }
3494
3495   atom_write_size (buffer, size, offset, original_offset);
3496   return *offset - original_offset;
3497 }
3498
3499 /* creates moof atom; metadata is written expecting actual buffer data
3500  * is in mdata directly after moof, and is consecutively written per trak */
3501 guint64
3502 atom_moof_copy_data (AtomMOOF * moof, guint8 ** buffer,
3503     guint64 * size, guint64 * offset)
3504 {
3505   guint64 original_offset = *offset;
3506   GList *walker;
3507   guint32 data_offset = 0;
3508
3509   if (!atom_copy_data (&moof->header, buffer, size, offset))
3510     return 0;
3511
3512   if (!atom_mfhd_copy_data (&moof->mfhd, buffer, size, offset))
3513     return 0;
3514
3515   walker = g_list_first (moof->trafs);
3516   while (walker != NULL) {
3517     if (!atom_traf_copy_data ((AtomTRAF *) walker->data, buffer, size, offset,
3518             &data_offset)) {
3519       return 0;
3520     }
3521     walker = g_list_next (walker);
3522   }
3523
3524   atom_write_size (buffer, size, offset, original_offset);
3525
3526   if (*buffer && data_offset) {
3527     /* first trun needs a data-offset relative to moof start
3528      *   = moof size + mdat prefix */
3529     GST_WRITE_UINT32_BE (*buffer + data_offset, *offset - original_offset + 8);
3530   }
3531
3532   return *offset - original_offset;
3533 }
3534
3535 static void
3536 atom_tfhd_init (AtomTFHD * tfhd, guint32 track_ID)
3537 {
3538   guint8 flags[3] = { 0, 0, 0 };
3539
3540   atom_full_init (&tfhd->header, FOURCC_tfhd, 0, 0, 0, flags);
3541   tfhd->track_ID = track_ID;
3542   tfhd->base_data_offset = 0;
3543   tfhd->sample_description_index = 1;
3544   tfhd->default_sample_duration = 0;
3545   tfhd->default_sample_size = 0;
3546   tfhd->default_sample_flags = 0;
3547 }
3548
3549 static void
3550 atom_trun_init (AtomTRUN * trun)
3551 {
3552   guint8 flags[3] = { 0, 0, 0 };
3553
3554   atom_full_init (&trun->header, FOURCC_trun, 0, 0, 0, flags);
3555   trun->sample_count = 0;
3556   trun->data_offset = 0;
3557   trun->first_sample_flags = 0;
3558   atom_array_init (&trun->entries, 512);
3559 }
3560
3561 static AtomTRUN *
3562 atom_trun_new (void)
3563 {
3564   AtomTRUN *trun = g_new0 (AtomTRUN, 1);
3565
3566   atom_trun_init (trun);
3567   return trun;
3568 }
3569
3570 static void
3571 atom_sdtp_init (AtomSDTP * sdtp)
3572 {
3573   guint8 flags[3] = { 0, 0, 0 };
3574
3575   atom_full_init (&sdtp->header, FOURCC_sdtp, 0, 0, 0, flags);
3576   atom_array_init (&sdtp->entries, 512);
3577 }
3578
3579 static AtomSDTP *
3580 atom_sdtp_new (AtomsContext * context)
3581 {
3582   AtomSDTP *sdtp = g_new0 (AtomSDTP, 1);
3583
3584   atom_sdtp_init (sdtp);
3585   return sdtp;
3586 }
3587
3588 static void
3589 atom_traf_add_sdtp (AtomTRAF * traf, AtomSDTP * sdtp)
3590 {
3591   traf->sdtps = g_list_append (traf->sdtps, sdtp);
3592 }
3593
3594 static void
3595 atom_sdtp_add_samples (AtomSDTP * sdtp, guint8 val)
3596 {
3597   /* it does not make much/any sense according to specs,
3598    * but that's how MS isml samples seem to do it */
3599   atom_array_append (&sdtp->entries, val, 256);
3600 }
3601
3602 static void
3603 atom_trun_add_samples (AtomTRUN * trun, guint32 delta, guint32 size,
3604     guint32 flags, gint64 pts_offset)
3605 {
3606   TRUNSampleEntry nentry;
3607
3608   if (pts_offset != 0)
3609     trun->header.flags[1] |= TR_COMPOSITION_TIME_OFFSETS;
3610
3611   nentry.sample_duration = delta;
3612   nentry.sample_size = size;
3613   nentry.sample_flags = flags;
3614   nentry.sample_composition_time_offset = pts_offset;
3615   atom_array_append (&trun->entries, nentry, 256);
3616   trun->sample_count++;
3617 }
3618
3619 static void
3620 atom_traf_init (AtomTRAF * traf, AtomsContext * context, guint32 track_ID)
3621 {
3622   atom_header_set (&traf->header, FOURCC_traf, 0, 0);
3623   atom_tfhd_init (&traf->tfhd, track_ID);
3624   traf->truns = NULL;
3625
3626   if (context->flavor == ATOMS_TREE_FLAVOR_ISML)
3627     atom_traf_add_sdtp (traf, atom_sdtp_new (context));
3628 }
3629
3630 AtomTRAF *
3631 atom_traf_new (AtomsContext * context, guint32 track_ID)
3632 {
3633   AtomTRAF *traf = g_new0 (AtomTRAF, 1);
3634
3635   atom_traf_init (traf, context, track_ID);
3636   return traf;
3637 }
3638
3639 static void
3640 atom_traf_add_trun (AtomTRAF * traf, AtomTRUN * trun)
3641 {
3642   traf->truns = g_list_append (traf->truns, trun);
3643 }
3644
3645 void
3646 atom_traf_add_samples (AtomTRAF * traf, guint32 delta, guint32 size,
3647     gboolean sync, gint64 pts_offset, gboolean sdtp_sync)
3648 {
3649   AtomTRUN *trun;
3650   guint32 flags;
3651
3652   /* 0x10000 is sample-is-difference-sample flag
3653    * low byte stuff is what ismv uses */
3654   flags = (sync ? 0x0 : 0x10000) | (sdtp_sync ? 0x40 : 0xc0);
3655
3656   if (G_UNLIKELY (!traf->truns)) {
3657     trun = atom_trun_new ();
3658     atom_traf_add_trun (traf, trun);
3659     /* optimistic; indicate all defaults present in tfhd */
3660     traf->tfhd.header.flags[2] = TF_DEFAULT_SAMPLE_DURATION |
3661         TF_DEFAULT_SAMPLE_SIZE | TF_DEFAULT_SAMPLE_FLAGS;
3662     traf->tfhd.default_sample_duration = delta;
3663     traf->tfhd.default_sample_size = size;
3664     traf->tfhd.default_sample_flags = flags;
3665     trun->first_sample_flags = flags;
3666   }
3667
3668   trun = traf->truns->data;
3669
3670   /* check if still matching defaults,
3671    * if not, abandon default and need entry for each sample */
3672   if (traf->tfhd.default_sample_duration != delta) {
3673     traf->tfhd.header.flags[2] &= ~TF_DEFAULT_SAMPLE_DURATION;
3674     trun->header.flags[1] |= (TR_SAMPLE_DURATION >> 8);
3675   }
3676   if (traf->tfhd.default_sample_size != size) {
3677     traf->tfhd.header.flags[2] &= ~TF_DEFAULT_SAMPLE_SIZE;
3678     trun->header.flags[1] |= (TR_SAMPLE_SIZE >> 8);
3679   }
3680   if (traf->tfhd.default_sample_flags != flags) {
3681     if (trun->sample_count == 1) {
3682       /* at least will need first sample flag */
3683       traf->tfhd.default_sample_flags = flags;
3684       trun->header.flags[2] |= TR_FIRST_SAMPLE_FLAGS;
3685     } else {
3686       /* now we need sample flags for each sample */
3687       traf->tfhd.header.flags[2] &= ~TF_DEFAULT_SAMPLE_FLAGS;
3688       trun->header.flags[1] |= (TR_SAMPLE_FLAGS >> 8);
3689       trun->header.flags[2] &= ~TR_FIRST_SAMPLE_FLAGS;
3690     }
3691   }
3692
3693   atom_trun_add_samples (traf->truns->data, delta, size, flags, pts_offset);
3694
3695   if (traf->sdtps)
3696     atom_sdtp_add_samples (traf->sdtps->data, 0x10 | ((flags & 0xff) >> 4));
3697 }
3698
3699 guint32
3700 atom_traf_get_sample_num (AtomTRAF * traf)
3701 {
3702   AtomTRUN *trun;
3703
3704   if (G_UNLIKELY (!traf->truns))
3705     return 0;
3706
3707   trun = traf->truns->data;
3708   return atom_array_get_len (&trun->entries);
3709 }
3710
3711 void
3712 atom_moof_add_traf (AtomMOOF * moof, AtomTRAF * traf)
3713 {
3714   moof->trafs = g_list_append (moof->trafs, traf);
3715 }
3716
3717 static void
3718 atom_tfra_free (AtomTFRA * tfra)
3719 {
3720   atom_full_clear (&tfra->header);
3721   atom_array_clear (&tfra->entries);
3722   g_free (tfra);
3723 }
3724
3725 AtomMFRA *
3726 atom_mfra_new (AtomsContext * context)
3727 {
3728   AtomMFRA *mfra = g_new0 (AtomMFRA, 1);
3729
3730   atom_header_set (&mfra->header, FOURCC_mfra, 0, 0);
3731   return mfra;
3732 }
3733
3734 void
3735 atom_mfra_add_tfra (AtomMFRA * mfra, AtomTFRA * tfra)
3736 {
3737   mfra->tfras = g_list_append (mfra->tfras, tfra);
3738 }
3739
3740 void
3741 atom_mfra_free (AtomMFRA * mfra)
3742 {
3743   GList *walker;
3744
3745   walker = mfra->tfras;
3746   while (walker) {
3747     atom_tfra_free ((AtomTFRA *) walker->data);
3748     walker = g_list_next (walker);
3749   }
3750   g_list_free (mfra->tfras);
3751   mfra->tfras = NULL;
3752
3753   atom_clear (&mfra->header);
3754   g_free (mfra);
3755 }
3756
3757 static void
3758 atom_tfra_init (AtomTFRA * tfra, guint32 track_ID)
3759 {
3760   guint8 flags[3] = { 0, 0, 0 };
3761
3762   atom_full_init (&tfra->header, FOURCC_tfra, 0, 0, 0, flags);
3763   tfra->track_ID = track_ID;
3764   atom_array_init (&tfra->entries, 512);
3765 }
3766
3767 AtomTFRA *
3768 atom_tfra_new (AtomsContext * context, guint32 track_ID)
3769 {
3770   AtomTFRA *tfra = g_new0 (AtomTFRA, 1);
3771
3772   atom_tfra_init (tfra, track_ID);
3773   return tfra;
3774
3775 }
3776
3777 static inline gint
3778 need_bytes (guint32 num)
3779 {
3780   gint n = 0;
3781
3782   while (num >>= 8)
3783     n++;
3784
3785   return n;
3786 }
3787
3788 void
3789 atom_tfra_add_entry (AtomTFRA * tfra, guint64 dts, guint32 sample_num)
3790 {
3791   TFRAEntry entry;
3792
3793   entry.time = dts;
3794   /* fill in later */
3795   entry.moof_offset = 0;
3796   /* always write a single trun in a single traf */
3797   entry.traf_number = 1;
3798   entry.trun_number = 1;
3799   entry.sample_number = sample_num;
3800
3801   /* auto-use 64 bits if needed */
3802   if (dts > G_MAXUINT32)
3803     tfra->header.version = 1;
3804
3805   /* 1 byte will always do for traf and trun number,
3806    * check how much sample_num needs */
3807   tfra->lengths = (tfra->lengths & 0xfc) ||
3808       MAX (tfra->lengths, need_bytes (sample_num));
3809
3810   atom_array_append (&tfra->entries, entry, 256);
3811 }
3812
3813 void
3814 atom_tfra_update_offset (AtomTFRA * tfra, guint64 offset)
3815 {
3816   gint i;
3817
3818   /* auto-use 64 bits if needed */
3819   if (offset > G_MAXUINT32)
3820     tfra->header.version = 1;
3821
3822   for (i = atom_array_get_len (&tfra->entries) - 1; i >= 0; i--) {
3823     TFRAEntry *entry = &atom_array_index (&tfra->entries, i);
3824
3825     if (entry->moof_offset)
3826       break;
3827     entry->moof_offset = offset;
3828   }
3829 }
3830
3831 static guint64
3832 atom_tfra_copy_data (AtomTFRA * tfra, guint8 ** buffer, guint64 * size,
3833     guint64 * offset)
3834 {
3835   guint64 original_offset = *offset;
3836   guint32 i;
3837   TFRAEntry *entry;
3838   guint32 data;
3839   guint bytes;
3840   guint version;
3841
3842   if (!atom_full_copy_data (&tfra->header, buffer, size, offset)) {
3843     return 0;
3844   }
3845
3846   prop_copy_uint32 (tfra->track_ID, buffer, size, offset);
3847   prop_copy_uint32 (tfra->lengths, buffer, size, offset);
3848   prop_copy_uint32 (atom_array_get_len (&tfra->entries), buffer, size, offset);
3849
3850   version = tfra->header.version;
3851   for (i = 0; i < atom_array_get_len (&tfra->entries); ++i) {
3852     entry = &atom_array_index (&tfra->entries, i);
3853     if (version) {
3854       prop_copy_uint64 (entry->time, buffer, size, offset);
3855       prop_copy_uint64 (entry->moof_offset, buffer, size, offset);
3856     } else {
3857       prop_copy_uint32 (entry->time, buffer, size, offset);
3858       prop_copy_uint32 (entry->moof_offset, buffer, size, offset);
3859     }
3860
3861     bytes = (tfra->lengths & (0x3 << 4)) + 1;
3862     data = GUINT32_TO_BE (entry->traf_number);
3863     prop_copy_fixed_size_string (((guint8 *) & data) + 4 - bytes, bytes,
3864         buffer, size, offset);
3865
3866     bytes = (tfra->lengths & (0x3 << 2)) + 1;
3867     data = GUINT32_TO_BE (entry->trun_number);
3868     prop_copy_fixed_size_string (((guint8 *) & data) + 4 - bytes, bytes,
3869         buffer, size, offset);
3870
3871     bytes = (tfra->lengths & (0x3)) + 1;
3872     data = GUINT32_TO_BE (entry->sample_number);
3873     prop_copy_fixed_size_string (((guint8 *) & data) + 4 - bytes, bytes,
3874         buffer, size, offset);
3875
3876   }
3877
3878   atom_write_size (buffer, size, offset, original_offset);
3879   return *offset - original_offset;
3880 }
3881
3882 static guint64
3883 atom_mfro_copy_data (guint32 s, guint8 ** buffer, guint64 * size,
3884     guint64 * offset)
3885 {
3886   guint64 original_offset = *offset;
3887   guint8 flags[3] = { 0, 0, 0 };
3888   AtomFull mfro;
3889
3890   atom_full_init (&mfro, FOURCC_mfro, 0, 0, 0, flags);
3891
3892   if (!atom_full_copy_data (&mfro, buffer, size, offset)) {
3893     return 0;
3894   }
3895
3896   prop_copy_uint32 (s, buffer, size, offset);
3897
3898   atom_write_size (buffer, size, offset, original_offset);
3899
3900   return *offset - original_offset;
3901 }
3902
3903
3904 guint64
3905 atom_mfra_copy_data (AtomMFRA * mfra, guint8 ** buffer, guint64 * size,
3906     guint64 * offset)
3907 {
3908   guint64 original_offset = *offset;
3909   GList *walker;
3910
3911   if (!atom_copy_data (&mfra->header, buffer, size, offset))
3912     return 0;
3913
3914   walker = g_list_first (mfra->tfras);
3915   while (walker != NULL) {
3916     if (!atom_tfra_copy_data ((AtomTFRA *) walker->data, buffer, size, offset)) {
3917       return 0;
3918     }
3919     walker = g_list_next (walker);
3920   }
3921
3922   /* 16 is the size of the mfro atom */
3923   if (!atom_mfro_copy_data (*offset - original_offset + 16, buffer,
3924           size, offset))
3925     return 0;
3926
3927   atom_write_size (buffer, size, offset, original_offset);
3928   return *offset - original_offset;
3929 }
3930
3931 /* some sample description construction helpers */
3932
3933 AtomInfo *
3934 build_esds_extension (AtomTRAK * trak, guint8 object_type, guint8 stream_type,
3935     const GstBuffer * codec_data, guint32 avg_bitrate, guint32 max_bitrate)
3936 {
3937   guint32 track_id;
3938   AtomESDS *esds;
3939
3940   track_id = trak->tkhd.track_ID;
3941
3942   esds = atom_esds_new ();
3943   esds->es.id = track_id & 0xFFFF;
3944   esds->es.dec_conf_desc.object_type = object_type;
3945   esds->es.dec_conf_desc.stream_type = stream_type << 2 | 0x01;
3946
3947   if (avg_bitrate > 0)
3948     esds->es.dec_conf_desc.avg_bitrate = avg_bitrate;
3949   if (max_bitrate > 0)
3950     esds->es.dec_conf_desc.max_bitrate = max_bitrate;
3951
3952   /* optional DecoderSpecificInfo */
3953   if (codec_data) {
3954     DecoderSpecificInfoDescriptor *desc;
3955     gsize size;
3956
3957     esds->es.dec_conf_desc.dec_specific_info = desc =
3958         desc_dec_specific_info_new ();
3959     size = gst_buffer_get_size ((GstBuffer *) codec_data);
3960     desc_dec_specific_info_alloc_data (desc, size);
3961     gst_buffer_extract ((GstBuffer *) codec_data, 0, desc->data, size);
3962   }
3963
3964   return build_atom_info_wrapper ((Atom *) esds, atom_esds_copy_data,
3965       atom_esds_free);
3966 }
3967
3968 AtomInfo *
3969 build_btrt_extension (guint32 buffer_size_db, guint32 avg_bitrate,
3970     guint32 max_bitrate)
3971 {
3972   AtomData *atom_data;
3973   GstBuffer *buf;
3974   guint8 *data;
3975
3976   if (buffer_size_db == 0 && avg_bitrate == 0 && max_bitrate == 0)
3977     return 0;
3978
3979   data = g_malloc (12);
3980   GST_WRITE_UINT32_BE (data, buffer_size_db);
3981   GST_WRITE_UINT32_BE (data + 4, max_bitrate);
3982   GST_WRITE_UINT32_BE (data + 8, avg_bitrate);
3983
3984   buf = _gst_buffer_new_wrapped (data, 12, g_free);
3985   atom_data =
3986       atom_data_new_from_gst_buffer (GST_MAKE_FOURCC ('b', 't', 'r', 't'), buf);
3987   gst_buffer_unref (buf);
3988
3989   return build_atom_info_wrapper ((Atom *) atom_data, atom_data_copy_data,
3990       atom_data_free);
3991 }
3992
3993 static AtomInfo *
3994 build_mov_wave_extension (AtomTRAK * trak, guint32 fourcc, AtomInfo * atom1,
3995     AtomInfo * atom2, gboolean terminator)
3996 {
3997   AtomWAVE *wave;
3998   AtomFRMA *frma;
3999   Atom *ext_atom;
4000
4001   /* Build WAVE atom for sample table entry */
4002   wave = atom_wave_new ();
4003
4004   /* Prepend Terminator atom to the WAVE list first, so it ends up last */
4005   if (terminator) {
4006     ext_atom = (Atom *) atom_data_new (FOURCC_null);
4007     wave->extension_atoms =
4008         atom_info_list_prepend_atom (wave->extension_atoms, (Atom *) ext_atom,
4009         (AtomCopyDataFunc) atom_data_copy_data, (AtomFreeFunc) atom_data_free);
4010   }
4011
4012   /* Add supplied atoms to WAVE */
4013   if (atom2)
4014     wave->extension_atoms = g_list_prepend (wave->extension_atoms, atom2);
4015   if (atom1)
4016     wave->extension_atoms = g_list_prepend (wave->extension_atoms, atom1);
4017
4018   /* Add FRMA to the WAVE */
4019   frma = atom_frma_new ();
4020   frma->media_type = fourcc;
4021
4022   wave->extension_atoms =
4023       atom_info_list_prepend_atom (wave->extension_atoms, (Atom *) frma,
4024       (AtomCopyDataFunc) atom_frma_copy_data, (AtomFreeFunc) atom_frma_free);
4025
4026   return build_atom_info_wrapper ((Atom *) wave, atom_wave_copy_data,
4027       atom_wave_free);
4028 }
4029
4030 AtomInfo *
4031 build_mov_aac_extension (AtomTRAK * trak, const GstBuffer * codec_data,
4032     guint32 avg_bitrate, guint32 max_bitrate)
4033 {
4034   AtomInfo *esds, *mp4a;
4035   GstBuffer *buf;
4036   guint32 tmp = 0;
4037
4038   /* Add ESDS atom to WAVE */
4039   esds = build_esds_extension (trak, ESDS_OBJECT_TYPE_MPEG4_P3,
4040       ESDS_STREAM_TYPE_AUDIO, codec_data, avg_bitrate, max_bitrate);
4041
4042   /* Add MP4A atom to the WAVE:
4043    * not really in spec, but makes offset based players happy */
4044   buf = _gst_buffer_new_wrapped (&tmp, 4, NULL);
4045   mp4a = build_codec_data_extension (FOURCC_mp4a, buf);
4046   gst_buffer_unref (buf);
4047
4048   return build_mov_wave_extension (trak, FOURCC_mp4a, mp4a, esds, TRUE);
4049 }
4050
4051 AtomInfo *
4052 build_mov_alac_extension (AtomTRAK * trak, const GstBuffer * codec_data)
4053 {
4054   AtomInfo *alac;
4055
4056   alac = build_codec_data_extension (FOURCC_alac, codec_data);
4057
4058   return build_mov_wave_extension (trak, FOURCC_alac, NULL, alac, TRUE);
4059 }
4060
4061 AtomInfo *
4062 build_fiel_extension (gint fields)
4063 {
4064   AtomData *atom_data;
4065   GstBuffer *buf;
4066   guint8 f = fields;
4067
4068   if (fields == 1) {
4069     return NULL;
4070   }
4071
4072   buf = _gst_buffer_new_wrapped (&f, 1, NULL);
4073   atom_data =
4074       atom_data_new_from_gst_buffer (GST_MAKE_FOURCC ('f', 'i', 'e', 'l'), buf);
4075   gst_buffer_unref (buf);
4076
4077   return build_atom_info_wrapper ((Atom *) atom_data, atom_data_copy_data,
4078       atom_data_free);
4079 }
4080
4081 AtomInfo *
4082 build_jp2x_extension (const GstBuffer * prefix)
4083 {
4084   AtomData *atom_data;
4085
4086   if (!prefix) {
4087     return NULL;
4088   }
4089
4090   atom_data =
4091       atom_data_new_from_gst_buffer (GST_MAKE_FOURCC ('j', 'p', '2', 'x'),
4092       prefix);
4093
4094   return build_atom_info_wrapper ((Atom *) atom_data, atom_data_copy_data,
4095       atom_data_free);
4096 }
4097
4098 AtomInfo *
4099 build_jp2h_extension (AtomTRAK * trak, gint width, gint height,
4100     const gchar * colorspace, gint ncomp, const GValue * cmap_array,
4101     const GValue * cdef_array)
4102 {
4103   AtomData *atom_data;
4104   GstBuffer *buf;
4105   guint8 cenum;
4106   gint i;
4107   gint idhr_size = 22;
4108   gint colr_size = 15;
4109   gint cmap_size = 0, cdef_size = 0;
4110   gint cmap_array_size = 0;
4111   gint cdef_array_size = 0;
4112   GstByteWriter writer;
4113
4114   g_return_val_if_fail (cmap_array == NULL ||
4115       GST_VALUE_HOLDS_ARRAY (cmap_array), NULL);
4116   g_return_val_if_fail (cdef_array == NULL ||
4117       GST_VALUE_HOLDS_ARRAY (cdef_array), NULL);
4118
4119   if (g_str_equal (colorspace, "sRGB")) {
4120     cenum = 0x10;
4121     if (ncomp == 0)
4122       ncomp = 3;
4123   } else if (g_str_equal (colorspace, "GRAY")) {
4124     cenum = 0x11;
4125     if (ncomp == 0)
4126       ncomp = 1;
4127   } else if (g_str_equal (colorspace, "sYUV")) {
4128     cenum = 0x12;
4129     if (ncomp == 0)
4130       ncomp = 3;
4131   } else
4132     return NULL;
4133
4134   if (cmap_array) {
4135     cmap_array_size = gst_value_array_get_size (cmap_array);
4136     cmap_size = 8 + cmap_array_size * 4;
4137   }
4138   if (cdef_array) {
4139     cdef_array_size = gst_value_array_get_size (cdef_array);
4140     cdef_size = 8 + 2 + cdef_array_size * 6;
4141   }
4142
4143   gst_byte_writer_init_with_size (&writer,
4144       idhr_size + colr_size + cmap_size + cdef_size, TRUE);
4145
4146   /* ihdr = image header box */
4147   gst_byte_writer_put_uint32_be (&writer, 22);
4148   gst_byte_writer_put_uint32_le (&writer, GST_MAKE_FOURCC ('i', 'h', 'd', 'r'));
4149   gst_byte_writer_put_uint32_be (&writer, height);
4150   gst_byte_writer_put_uint32_be (&writer, width);
4151   gst_byte_writer_put_uint16_be (&writer, ncomp);
4152   /* 8 bits per component, unsigned */
4153   gst_byte_writer_put_uint8 (&writer, 0x7);
4154   /* compression type; reserved */
4155   gst_byte_writer_put_uint8 (&writer, 0x7);
4156   /* colour space (un)known */
4157   gst_byte_writer_put_uint8 (&writer, 0x0);
4158   /* intellectual property right (box present) */
4159   gst_byte_writer_put_uint8 (&writer, 0x0);
4160
4161   /* colour specification box */
4162   gst_byte_writer_put_uint32_be (&writer, 15);
4163   gst_byte_writer_put_uint32_le (&writer, GST_MAKE_FOURCC ('c', 'o', 'l', 'r'));
4164
4165   /* specification method: enumerated */
4166   gst_byte_writer_put_uint8 (&writer, 0x1);
4167   /* precedence; reserved */
4168   gst_byte_writer_put_uint8 (&writer, 0x0);
4169   /* approximation; reserved */
4170   gst_byte_writer_put_uint8 (&writer, 0x0);
4171   /* enumerated colourspace */
4172   gst_byte_writer_put_uint32_be (&writer, cenum);
4173
4174   if (cmap_array) {
4175     gst_byte_writer_put_uint32_be (&writer, cmap_size);
4176     gst_byte_writer_put_uint32_le (&writer,
4177         GST_MAKE_FOURCC ('c', 'm', 'a', 'p'));
4178     for (i = 0; i < cmap_array_size; i++) {
4179       const GValue *item;
4180       gint value;
4181       guint16 cmp;
4182       guint8 mtyp;
4183       guint8 pcol;
4184       item = gst_value_array_get_value (cmap_array, i);
4185       value = g_value_get_int (item);
4186
4187       /* value is '(mtyp << 24) | (pcol << 16) | cmp' */
4188       cmp = value & 0xFFFF;
4189       mtyp = value >> 24;
4190       pcol = (value >> 16) & 0xFF;
4191
4192       if (mtyp == 1)
4193         GST_WARNING ("MTYP of cmap atom signals Pallete Mapping, but we don't "
4194             "handle Pallete mapping atoms yet");
4195
4196       gst_byte_writer_put_uint16_be (&writer, cmp);
4197       gst_byte_writer_put_uint8 (&writer, mtyp);
4198       gst_byte_writer_put_uint8 (&writer, pcol);
4199     }
4200   }
4201
4202   if (cdef_array) {
4203     gst_byte_writer_put_uint32_be (&writer, cdef_size);
4204     gst_byte_writer_put_uint32_le (&writer,
4205         GST_MAKE_FOURCC ('c', 'd', 'e', 'f'));
4206     gst_byte_writer_put_uint16_be (&writer, cdef_array_size);
4207     for (i = 0; i < cdef_array_size; i++) {
4208       const GValue *item;
4209       gint value;
4210       item = gst_value_array_get_value (cdef_array, i);
4211       value = g_value_get_int (item);
4212
4213       gst_byte_writer_put_uint16_be (&writer, i);
4214       if (value > 0) {
4215         gst_byte_writer_put_uint16_be (&writer, 0);
4216         gst_byte_writer_put_uint16_be (&writer, value);
4217       } else if (value < 0) {
4218         gst_byte_writer_put_uint16_be (&writer, -value);
4219         gst_byte_writer_put_uint16_be (&writer, 0);     /* TODO what here? */
4220       } else {
4221         gst_byte_writer_put_uint16_be (&writer, 1);
4222         gst_byte_writer_put_uint16_be (&writer, 0);
4223       }
4224     }
4225   }
4226
4227   g_assert (gst_byte_writer_get_remaining (&writer) == 0);
4228   buf = gst_byte_writer_reset_and_get_buffer (&writer);
4229
4230   atom_data = atom_data_new_from_gst_buffer (FOURCC_jp2h, buf);
4231   gst_buffer_unref (buf);
4232
4233   return build_atom_info_wrapper ((Atom *) atom_data, atom_data_copy_data,
4234       atom_data_free);
4235 }
4236
4237 AtomInfo *
4238 build_codec_data_extension (guint32 fourcc, const GstBuffer * codec_data)
4239 {
4240   AtomData *data;
4241   AtomInfo *result = NULL;
4242
4243   if (codec_data) {
4244     data = atom_data_new_from_gst_buffer (fourcc, codec_data);
4245     result = build_atom_info_wrapper ((Atom *) data, atom_data_copy_data,
4246         atom_data_free);
4247   }
4248
4249   return result;
4250 }
4251
4252 AtomInfo *
4253 build_amr_extension (void)
4254 {
4255   guint8 ext[9];
4256   GstBuffer *buf;
4257   AtomInfo *res;
4258
4259   /* vendor */
4260   GST_WRITE_UINT32_LE (ext, 0);
4261   /* decoder version */
4262   GST_WRITE_UINT8 (ext + 4, 0);
4263   /* mode set (all modes) */
4264   GST_WRITE_UINT16_BE (ext + 5, 0x81FF);
4265   /* mode change period (no restriction) */
4266   GST_WRITE_UINT8 (ext + 7, 0);
4267   /* frames per sample */
4268   GST_WRITE_UINT8 (ext + 8, 1);
4269
4270   buf = _gst_buffer_new_wrapped (ext, sizeof (ext), NULL);
4271   res = build_codec_data_extension (GST_MAKE_FOURCC ('d', 'a', 'm', 'r'), buf);
4272   gst_buffer_unref (buf);
4273   return res;
4274 }
4275
4276 AtomInfo *
4277 build_h263_extension (void)
4278 {
4279   guint8 ext[7];
4280   GstBuffer *buf;
4281   AtomInfo *res;
4282
4283   /* vendor */
4284   GST_WRITE_UINT32_LE (ext, 0);
4285   /* decoder version */
4286   GST_WRITE_UINT8 (ext + 4, 0);
4287   /* level / profile */
4288   /* FIXME ? maybe ? obtain somewhere; baseline for now */
4289   GST_WRITE_UINT8 (ext + 5, 10);
4290   GST_WRITE_UINT8 (ext + 6, 0);
4291
4292   buf = _gst_buffer_new_wrapped (ext, sizeof (ext), NULL);
4293   res = build_codec_data_extension (GST_MAKE_FOURCC ('d', '2', '6', '3'), buf);
4294   gst_buffer_unref (buf);
4295   return res;
4296 }
4297
4298 AtomInfo *
4299 build_gama_atom (gdouble gamma)
4300 {
4301   AtomInfo *res;
4302   guint32 gamma_fp;
4303   GstBuffer *buf;
4304
4305   /* convert to uint32 from fixed point */
4306   gamma_fp = (guint32) 65536 *gamma;
4307
4308   gamma_fp = GUINT32_TO_BE (gamma_fp);
4309   buf = _gst_buffer_new_wrapped (&gamma_fp, 4, NULL);
4310   res = build_codec_data_extension (FOURCC_gama, buf);
4311   gst_buffer_unref (buf);
4312   return res;
4313 }
4314
4315 AtomInfo *
4316 build_SMI_atom (const GstBuffer * seqh)
4317 {
4318   AtomInfo *res;
4319   GstBuffer *buf;
4320   gsize size;
4321   guint8 *data;
4322
4323   /* the seqh plus its size and fourcc */
4324   size = gst_buffer_get_size ((GstBuffer *) seqh);
4325   data = g_malloc (size + 8);
4326
4327   GST_WRITE_UINT32_LE (data, FOURCC_SEQH);
4328   GST_WRITE_UINT32_BE (data + 4, size + 8);
4329   gst_buffer_extract ((GstBuffer *) seqh, 0, data + 8, size);
4330   buf = _gst_buffer_new_wrapped (data, size + 8, g_free);
4331   res = build_codec_data_extension (FOURCC_SMI_, buf);
4332   gst_buffer_unref (buf);
4333   return res;
4334 }
4335
4336 static AtomInfo *
4337 build_ima_adpcm_atom (gint channels, gint rate, gint blocksize)
4338 {
4339   AtomData *atom_data;
4340   GstBuffer *buf;
4341   guint8 *data;
4342   const gint ima_adpcm_atom_size = 20;
4343   guint32 fourcc;
4344   gint samplesperblock;
4345   gint bytespersec;
4346
4347   /* The FOURCC for WAV codecs in QT is 'ms' followed by the 16 bit wave codec
4348      identifier. Note that the identifier here is big-endian, but when used
4349      within the WAVE header (below), it's little endian. */
4350   fourcc = MS_WAVE_FOURCC (0x11);
4351
4352   data = g_malloc (ima_adpcm_atom_size);
4353
4354   /* This atom's content is a WAVE header, including 2 bytes of extra data.
4355      Note that all of this is little-endian, unlike most stuff in qt. */
4356   /* 4 bytes header per channel (including 1 sample). Then 2 samples per byte
4357      for the rest. Simplifies to this. */
4358   samplesperblock = 2 * blocksize / channels - 7;
4359   bytespersec = rate * blocksize / samplesperblock;
4360   GST_WRITE_UINT16_LE (data, 0x11);
4361   GST_WRITE_UINT16_LE (data + 2, channels);
4362   GST_WRITE_UINT32_LE (data + 4, rate);
4363   GST_WRITE_UINT32_LE (data + 8, bytespersec);
4364   GST_WRITE_UINT16_LE (data + 12, blocksize);
4365   GST_WRITE_UINT16_LE (data + 14, 4);
4366   GST_WRITE_UINT16_LE (data + 16, 2);   /* Two extra bytes */
4367   GST_WRITE_UINT16_LE (data + 18, samplesperblock);
4368
4369   buf = _gst_buffer_new_wrapped (data, ima_adpcm_atom_size, g_free);
4370   atom_data = atom_data_new_from_gst_buffer (fourcc, buf);
4371   gst_buffer_unref (buf);
4372
4373   return build_atom_info_wrapper ((Atom *) atom_data, atom_data_copy_data,
4374       atom_data_free);
4375 }
4376
4377 AtomInfo *
4378 build_ima_adpcm_extension (gint channels, gint rate, gint blocksize)
4379 {
4380   AtomWAVE *wave;
4381   AtomFRMA *frma;
4382   Atom *ext_atom;
4383
4384   /* Add WAVE atom */
4385   wave = atom_wave_new ();
4386
4387   /* Prepend Terminator atom to the WAVE list first, so it ends up last */
4388   ext_atom = (Atom *) atom_data_new (FOURCC_null);
4389   wave->extension_atoms =
4390       atom_info_list_prepend_atom (wave->extension_atoms, (Atom *) ext_atom,
4391       (AtomCopyDataFunc) atom_data_copy_data, (AtomFreeFunc) atom_data_free);
4392
4393   /* Add wave ima adpcm atom to WAVE */
4394   wave->extension_atoms = g_list_prepend (wave->extension_atoms,
4395       build_ima_adpcm_atom (channels, rate, blocksize));
4396
4397   /* Add FRMA to the WAVE */
4398   frma = atom_frma_new ();
4399   frma->media_type = MS_WAVE_FOURCC (0x11);
4400
4401   wave->extension_atoms =
4402       atom_info_list_prepend_atom (wave->extension_atoms, (Atom *) frma,
4403       (AtomCopyDataFunc) atom_frma_copy_data, (AtomFreeFunc) atom_frma_free);
4404
4405   return build_atom_info_wrapper ((Atom *) wave, atom_wave_copy_data,
4406       atom_wave_free);
4407 }
4408
4409 AtomInfo *
4410 build_uuid_xmp_atom (GstBuffer * xmp_data)
4411 {
4412   AtomUUID *uuid;
4413   gsize size;
4414   static guint8 xmp_uuid[] = { 0xBE, 0x7A, 0xCF, 0xCB,
4415     0x97, 0xA9, 0x42, 0xE8,
4416     0x9C, 0x71, 0x99, 0x94,
4417     0x91, 0xE3, 0xAF, 0xAC
4418   };
4419
4420   if (xmp_data == NULL)
4421     return NULL;
4422
4423   uuid = atom_uuid_new ();
4424   memcpy (uuid->uuid, xmp_uuid, 16);
4425
4426   size = gst_buffer_get_size (xmp_data);
4427   uuid->data = g_malloc (size);
4428   uuid->datalen = size;
4429   gst_buffer_extract (xmp_data, 0, uuid->data, size);
4430
4431   return build_atom_info_wrapper ((Atom *) uuid, atom_uuid_copy_data,
4432       atom_uuid_free);
4433 }