1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27#include <stdlib.h>
28#include <string.h>
29
30#define CONTAINER_IS_BIG_ENDIAN
31#include "containers/core/containers_private.h"
32#include "containers/core/containers_io_helpers.h"
33#include "containers/core/containers_utils.h"
34#include "containers/core/containers_writer_utils.h"
35#include "containers/core/containers_logging.h"
36#include "containers/mp4/mp4_common.h"
37#undef CONTAINER_HELPER_LOG_INDENT
38#define CONTAINER_HELPER_LOG_INDENT(a) (a)->priv->module->box_level
39
40VC_CONTAINER_STATUS_T mp4_writer_open( VC_CONTAINER_T *p_ctx );
41
42/******************************************************************************
43Defines.
44******************************************************************************/
45#define MP4_TRACKS_MAX 16
46#define MP4_TIMESCALE 1000
47
48#define MP4_64BITS_TIME 0 /* 0 to disable / 1 to enable */
49
50/******************************************************************************
51Type definitions.
52******************************************************************************/
53typedef struct VC_CONTAINER_TRACK_MODULE_T
54{
55 uint32_t fourcc;
56 uint32_t samples;
57 uint32_t chunks;
58
59 int64_t offset;
60 int64_t timestamp;
61 int64_t delta_timestamp;
62 int64_t samples_in_chunk;
63 int64_t samples_in_prev_chunk;
64 struct {
65 uint32_t entries;
66 uint32_t entry_size;
67 } sample_table[MP4_SAMPLE_TABLE_NUM];
68
69 int64_t first_pts;
70 int64_t last_pts;
71
72} VC_CONTAINER_TRACK_MODULE_T;
73
74typedef struct VC_CONTAINER_MODULE_T
75{
76 int box_level;
77 MP4_BRAND_T brand;
78
79 VC_CONTAINER_TRACK_T *tracks[MP4_TRACKS_MAX];
80 bool tracks_add_done;
81
82 VC_CONTAINER_WRITER_EXTRAIO_T null;
83
84 unsigned int current_track;
85
86 unsigned moov_size;
87 int64_t mdat_offset;
88 int64_t data_offset;
89
90 uint32_t samples;
91 VC_CONTAINER_WRITER_EXTRAIO_T temp;
92 VC_CONTAINER_PACKET_T sample;
93 int64_t sample_offset;
94 int64_t prev_sample_dts;
95
96 int64_t duration;
97 /**/
98
99} VC_CONTAINER_MODULE_T;
100
101/******************************************************************************
102Static functions within this file.
103******************************************************************************/
104static VC_CONTAINER_STATUS_T mp4_write_box_extended( VC_CONTAINER_T *p_ctx, MP4_BOX_TYPE_T box_type, uint32_t fourcc );
105static VC_CONTAINER_STATUS_T mp4_write_box( VC_CONTAINER_T *p_ctx, MP4_BOX_TYPE_T box_type );
106static VC_CONTAINER_STATUS_T mp4_write_box_ftyp( VC_CONTAINER_T *p_ctx );
107static VC_CONTAINER_STATUS_T mp4_write_box_moov( VC_CONTAINER_T *p_ctx );
108static VC_CONTAINER_STATUS_T mp4_write_box_mvhd( VC_CONTAINER_T *p_ctx );
109static VC_CONTAINER_STATUS_T mp4_write_box_trak( VC_CONTAINER_T *p_ctx );
110static VC_CONTAINER_STATUS_T mp4_write_box_tkhd( VC_CONTAINER_T *p_ctx );
111static VC_CONTAINER_STATUS_T mp4_write_box_mdia( VC_CONTAINER_T *p_ctx );
112static VC_CONTAINER_STATUS_T mp4_write_box_mdhd( VC_CONTAINER_T *p_ctx );
113static VC_CONTAINER_STATUS_T mp4_write_box_hdlr( VC_CONTAINER_T *p_ctx );
114static VC_CONTAINER_STATUS_T mp4_write_box_minf( VC_CONTAINER_T *p_ctx );
115static VC_CONTAINER_STATUS_T mp4_write_box_vmhd( VC_CONTAINER_T *p_ctx );
116static VC_CONTAINER_STATUS_T mp4_write_box_smhd( VC_CONTAINER_T *p_ctx );
117static VC_CONTAINER_STATUS_T mp4_write_box_dinf( VC_CONTAINER_T *p_ctx );
118static VC_CONTAINER_STATUS_T mp4_write_box_dref( VC_CONTAINER_T *p_ctx );
119static VC_CONTAINER_STATUS_T mp4_write_box_stbl( VC_CONTAINER_T *p_ctx );
120static VC_CONTAINER_STATUS_T mp4_write_box_stsd( VC_CONTAINER_T *p_ctx );
121static VC_CONTAINER_STATUS_T mp4_write_box_stts( VC_CONTAINER_T *p_ctx );
122static VC_CONTAINER_STATUS_T mp4_write_box_ctts( VC_CONTAINER_T *p_ctx );
123static VC_CONTAINER_STATUS_T mp4_write_box_stsc( VC_CONTAINER_T *p_ctx );
124static VC_CONTAINER_STATUS_T mp4_write_box_stsz( VC_CONTAINER_T *p_ctx );
125static VC_CONTAINER_STATUS_T mp4_write_box_stco( VC_CONTAINER_T *p_ctx );
126static VC_CONTAINER_STATUS_T mp4_write_box_co64( VC_CONTAINER_T *p_ctx );
127static VC_CONTAINER_STATUS_T mp4_write_box_stss( VC_CONTAINER_T *p_ctx );
128static VC_CONTAINER_STATUS_T mp4_write_box_vide( VC_CONTAINER_T *p_ctx );
129static VC_CONTAINER_STATUS_T mp4_write_box_soun( VC_CONTAINER_T *p_ctx );
130static VC_CONTAINER_STATUS_T mp4_write_box_esds( VC_CONTAINER_T *p_ctx );
131
132static struct {
133 const MP4_BOX_TYPE_T type;
134 VC_CONTAINER_STATUS_T (*pf_func)( VC_CONTAINER_T * );
135} mp4_box_list[] =
136{
137 {MP4_BOX_TYPE_FTYP, mp4_write_box_ftyp},
138 {MP4_BOX_TYPE_MOOV, mp4_write_box_moov},
139 {MP4_BOX_TYPE_MVHD, mp4_write_box_mvhd},
140 {MP4_BOX_TYPE_TRAK, mp4_write_box_trak},
141 {MP4_BOX_TYPE_TKHD, mp4_write_box_tkhd},
142 {MP4_BOX_TYPE_MDIA, mp4_write_box_mdia},
143 {MP4_BOX_TYPE_MDHD, mp4_write_box_mdhd},
144 {MP4_BOX_TYPE_HDLR, mp4_write_box_hdlr},
145 {MP4_BOX_TYPE_MINF, mp4_write_box_minf},
146 {MP4_BOX_TYPE_VMHD, mp4_write_box_vmhd},
147 {MP4_BOX_TYPE_SMHD, mp4_write_box_smhd},
148 {MP4_BOX_TYPE_DINF, mp4_write_box_dinf},
149 {MP4_BOX_TYPE_DREF, mp4_write_box_dref},
150 {MP4_BOX_TYPE_STBL, mp4_write_box_stbl},
151 {MP4_BOX_TYPE_STSD, mp4_write_box_stsd},
152 {MP4_BOX_TYPE_STTS, mp4_write_box_stts},
153 {MP4_BOX_TYPE_CTTS, mp4_write_box_ctts},
154 {MP4_BOX_TYPE_STSC, mp4_write_box_stsc},
155 {MP4_BOX_TYPE_STSZ, mp4_write_box_stsz},
156 {MP4_BOX_TYPE_STCO, mp4_write_box_stco},
157 {MP4_BOX_TYPE_CO64, mp4_write_box_co64},
158 {MP4_BOX_TYPE_STSS, mp4_write_box_stss},
159 {MP4_BOX_TYPE_VIDE, mp4_write_box_vide},
160 {MP4_BOX_TYPE_SOUN, mp4_write_box_soun},
161 {MP4_BOX_TYPE_ESDS, mp4_write_box_esds},
162 {MP4_BOX_TYPE_UNKNOWN, 0}
163};
164
165/*****************************************************************************/
166static VC_CONTAINER_STATUS_T mp4_write_box_extended( VC_CONTAINER_T *p_ctx, MP4_BOX_TYPE_T type, uint32_t fourcc )
167{
168 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
169 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
170 int64_t box_size = 0;
171 unsigned int i;
172
173 /* Find out which object we want to write */
174 for( i = 0; mp4_box_list[i].type && mp4_box_list[i].type != type; i++ );
175
176 /* Check we found the requested type */
177 if(!mp4_box_list[i].type)
178 {
179 vc_container_assert(0);
180 return VC_CONTAINER_ERROR_CORRUPTED;
181 }
182
183 /* We need to find out the size of the object we're going to write it. */
184 if(!vc_container_writer_extraio_enable(p_ctx, &module->null))
185 {
186 status = mp4_write_box_extended( p_ctx, type, fourcc );
187 box_size = STREAM_POSITION(p_ctx);
188 }
189 vc_container_writer_extraio_disable(p_ctx, &module->null);
190 if(status != VC_CONTAINER_SUCCESS) return status;
191
192 /* Write the object header */
193 LOG_FORMAT(p_ctx, "- Box %4.4s, size: %"PRIi64, (const char *)&fourcc, box_size);
194 _WRITE_U32(p_ctx, (uint32_t)box_size);
195 _WRITE_FOURCC(p_ctx, fourcc);
196
197 module->box_level++;
198
199 /* Call the object specific writing function */
200 status = mp4_box_list[i].pf_func(p_ctx);
201
202 module->box_level--;
203
204 if(status != VC_CONTAINER_SUCCESS)
205 LOG_DEBUG(p_ctx, "box %4.4s appears to be corrupted", (char *)mp4_box_list[i].type);
206
207 return status;
208}
209
210/*****************************************************************************/
211static VC_CONTAINER_STATUS_T mp4_write_box( VC_CONTAINER_T *p_ctx, MP4_BOX_TYPE_T type )
212{
213 return mp4_write_box_extended( p_ctx, type, type );
214}
215
216/*****************************************************************************/
217static VC_CONTAINER_STATUS_T mp4_write_box_ftyp( VC_CONTAINER_T *p_ctx )
218{
219 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
220
221 WRITE_FOURCC(p_ctx, module->brand, "major_brand");
222 WRITE_U32(p_ctx, 512, "minor_version");
223 if(module->brand == MP4_BRAND_QT)
224 {
225 WRITE_FOURCC(p_ctx, MP4_BRAND_QT, "compatible_brands");
226 return STREAM_STATUS(p_ctx);
227 }
228
229 if(module->brand == MP4_BRAND_SKM2)
230 WRITE_FOURCC(p_ctx, MP4_BRAND_SKM2, "compatible_brands");
231 WRITE_FOURCC(p_ctx, MP4_BRAND_ISOM, "compatible_brands");
232 WRITE_FOURCC(p_ctx, MP4_BRAND_MP42, "compatible_brands");
233 WRITE_FOURCC(p_ctx, MP4_BRAND_3GP4, "compatible_brands");
234
235 return STREAM_STATUS(p_ctx);
236}
237
238/*****************************************************************************/
239static VC_CONTAINER_STATUS_T mp4_write_box_moov( VC_CONTAINER_T *p_ctx )
240{
241 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
242 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
243 unsigned int i;
244
245 status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MVHD);
246 if(status != VC_CONTAINER_SUCCESS) return status;
247
248 for(i = 0; i < p_ctx->tracks_num; i++)
249 {
250 module->current_track = i;
251 status = mp4_write_box(p_ctx, MP4_BOX_TYPE_TRAK);
252 if(status != VC_CONTAINER_SUCCESS) return status;
253 }
254
255 return status;
256}
257
258/*****************************************************************************/
259static VC_CONTAINER_STATUS_T mp4_write_box_mvhd( VC_CONTAINER_T *p_ctx )
260{
261 static uint32_t matrix[] = { 0x10000,0,0,0,0x10000,0,0,0,0x40000000 };
262 unsigned int version = MP4_64BITS_TIME;
263 unsigned int i;
264
265 WRITE_U8(p_ctx, version, "version");
266 WRITE_U24(p_ctx, 0, "flags");
267
268 /**/
269 p_ctx->duration = 0;
270 for(i = 0; i < p_ctx->tracks_num; i++)
271 {
272 VC_CONTAINER_TRACK_T *track = p_ctx->tracks[i];
273 VC_CONTAINER_TRACK_MODULE_T *track_module = track->priv->module;
274 int64_t track_duration = track_module->last_pts - track_module->first_pts;
275 if(track_duration > p_ctx->duration)
276 p_ctx->duration = track_duration;
277 }
278
279 if(version)
280 {
281 WRITE_U64(p_ctx, 0, "creation_time");
282 WRITE_U64(p_ctx, 0, "modification_time");
283 WRITE_U32(p_ctx, MP4_TIMESCALE, "timescale");
284 WRITE_U64(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration");
285 }
286 else
287 {
288 WRITE_U32(p_ctx, 0, "creation_time");
289 WRITE_U32(p_ctx, 0, "modification_time");
290 WRITE_U32(p_ctx, MP4_TIMESCALE, "timescale");
291 WRITE_U32(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration");
292 }
293
294 WRITE_U32(p_ctx, 0x10000, "rate"); /* 1.0 */
295 WRITE_U16(p_ctx, 0x100, "volume"); /* full volume */
296 WRITE_U16(p_ctx, 0, "reserved");
297 for(i = 0; i < 2; i++)
298 WRITE_U32(p_ctx, 0, "reserved");
299 for(i = 0; i < 9; i++) /* unity matrix */
300 WRITE_U32(p_ctx, matrix[i], "matrix");
301 for(i = 0; i < 6; i++)
302 WRITE_U32(p_ctx, 0, "pre_defined");
303 WRITE_U32(p_ctx, p_ctx->tracks_num + 1, "next_track_ID");
304
305 return STREAM_STATUS(p_ctx);
306}
307
308/*****************************************************************************/
309static VC_CONTAINER_STATUS_T mp4_write_box_trak( VC_CONTAINER_T *p_ctx )
310{
311 VC_CONTAINER_STATUS_T status;
312
313 status = mp4_write_box(p_ctx, MP4_BOX_TYPE_TKHD);
314 if(status != VC_CONTAINER_SUCCESS) return status;
315
316 status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MDIA);
317 if(status != VC_CONTAINER_SUCCESS) return status;
318
319 return status;
320}
321
322/*****************************************************************************/
323static VC_CONTAINER_STATUS_T mp4_write_box_tkhd( VC_CONTAINER_T *p_ctx )
324{
325 static uint32_t matrix[] = { 0x10000,0,0,0,0x10000,0,0,0,0x40000000 };
326 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
327 VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
328 unsigned int version = MP4_64BITS_TIME;
329 uint32_t i, width = 0, height = 0;
330
331 WRITE_U8(p_ctx, version, "version");
332 WRITE_U24(p_ctx, 0x7, "flags"); /* track enabled */
333
334 if(version)
335 {
336 WRITE_U64(p_ctx, 0, "creation_time");
337 WRITE_U64(p_ctx, 0, "modification_time");
338 WRITE_U32(p_ctx, module->current_track + 1, "track_ID");
339 WRITE_U32(p_ctx, 0, "reserved");
340 WRITE_U64(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration");
341 }
342 else
343 {
344 WRITE_U32(p_ctx, 0, "creation_time");
345 WRITE_U32(p_ctx, 0, "modification_time");
346 WRITE_U32(p_ctx, module->current_track + 1, "track_ID");
347 WRITE_U32(p_ctx, 0, "reserved");
348 WRITE_U32(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration");
349 }
350
351 for(i = 0; i < 2; i++)
352 WRITE_U32(p_ctx, 0, "reserved");
353 WRITE_U16(p_ctx, 0, "layer");
354 WRITE_U16(p_ctx, 0, "alternate_group");
355 WRITE_U16(p_ctx, track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO ? 0x100 : 0, "volume");
356 WRITE_U16(p_ctx, 0, "reserved");
357 for(i = 0; i < 9; i++) /* unity matrix */
358 WRITE_U32(p_ctx, matrix[i], "matrix");
359
360 if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
361 {
362 width = track->format->type->video.width << 16;
363 height = track->format->type->video.height << 16;
364 if(track->format->type->video.par_num && track->format->type->video.par_den)
365 width = width * (uint64_t)track->format->type->video.par_num /
366 track->format->type->video.par_den;
367 }
368 else if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE)
369 {
370 /* FIXME */
371 }
372
373 WRITE_U32(p_ctx, width, "width");
374 WRITE_U32(p_ctx, height, "height");
375
376 return STREAM_STATUS(p_ctx);
377}
378
379/*****************************************************************************/
380static VC_CONTAINER_STATUS_T mp4_write_box_mdia( VC_CONTAINER_T *p_ctx )
381{
382 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
383
384 status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MDHD);
385 if(status != VC_CONTAINER_SUCCESS) return status;
386
387 status = mp4_write_box(p_ctx, MP4_BOX_TYPE_HDLR);
388 if(status != VC_CONTAINER_SUCCESS) return status;
389
390 status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MINF);
391 if(status != VC_CONTAINER_SUCCESS) return status;
392
393 return status;
394}
395
396/*****************************************************************************/
397static VC_CONTAINER_STATUS_T mp4_write_box_mdhd( VC_CONTAINER_T *p_ctx )
398{
399 unsigned int version = MP4_64BITS_TIME;
400
401 WRITE_U8(p_ctx, version, "version");
402 WRITE_U24(p_ctx, 0, "flags");
403
404 // FIXME: take a better timescale ??
405 if(version)
406 {
407 WRITE_U64(p_ctx, 0, "creation_time");
408 WRITE_U64(p_ctx, 0, "modification_time");
409 WRITE_U32(p_ctx, MP4_TIMESCALE, "timescale");
410 WRITE_U64(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration");
411 }
412 else
413 {
414 WRITE_U32(p_ctx, 0, "creation_time");
415 WRITE_U32(p_ctx, 0, "modification_time");
416 WRITE_U32(p_ctx, MP4_TIMESCALE, "timescale");
417 WRITE_U32(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration");
418 }
419
420 WRITE_U16(p_ctx, 0x55c4, "language"); /* ISO-639-2/T language code */
421 WRITE_U16(p_ctx, 0, "pre_defined");
422
423 return STREAM_STATUS(p_ctx);
424}
425
426/*****************************************************************************/
427static VC_CONTAINER_STATUS_T mp4_write_box_hdlr( VC_CONTAINER_T *p_ctx )
428{
429 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
430 VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
431 uint32_t i, handler_size, fourcc = 0;
432 const char *handler_name;
433
434 if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) fourcc = VC_FOURCC('v','i','d','e');
435 if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) fourcc = VC_FOURCC('s','o','u','n');
436 if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE) fourcc = VC_FOURCC('t','e','x','t');
437
438 WRITE_U8(p_ctx, 0, "version");
439 WRITE_U24(p_ctx, 0, "flags");
440
441 if(module->brand == MP4_BRAND_QT)
442 WRITE_FOURCC(p_ctx, VC_FOURCC('m','h','l','r'), "component_type");
443 else
444 WRITE_U32(p_ctx, 0, "pre-defined");
445
446 WRITE_FOURCC(p_ctx, fourcc, "handler_type");
447 for(i = 0; i < 3; i++)
448 WRITE_U32(p_ctx, 0, "reserved");
449
450 if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
451 { handler_name = "Video Media Handler"; handler_size = sizeof("Video Media Handler"); }
452 else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO)
453 { handler_name = "Audio Media Handler"; handler_size = sizeof("Audio Media Handler"); }
454 else if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE)
455 { handler_name = "Text Media Handler"; handler_size = sizeof("Text Media Handler"); }
456 else { handler_name = ""; handler_size = sizeof(""); }
457
458 if(module->brand == MP4_BRAND_QT)
459 { handler_size--; WRITE_U8(p_ctx, handler_size, "string_size"); }
460
461 WRITE_STRING(p_ctx, handler_name, handler_size, "name");
462
463 return STREAM_STATUS(p_ctx);
464}
465
466/*****************************************************************************/
467static VC_CONTAINER_STATUS_T mp4_write_box_minf( VC_CONTAINER_T *p_ctx )
468{
469 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
470 VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
471 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
472
473 if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
474 status = mp4_write_box(p_ctx, MP4_BOX_TYPE_VMHD);
475 else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO)
476 status = mp4_write_box(p_ctx, MP4_BOX_TYPE_SMHD);
477#if 0
478 else if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE)
479 /*FIXME */;
480#endif
481 if(status != VC_CONTAINER_SUCCESS) return status;
482
483 status = mp4_write_box(p_ctx, MP4_BOX_TYPE_DINF);
484 if(status != VC_CONTAINER_SUCCESS) return status;
485
486 status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STBL);
487
488 return status;
489}
490
491/*****************************************************************************/
492static VC_CONTAINER_STATUS_T mp4_write_box_vmhd( VC_CONTAINER_T *p_ctx )
493{
494 WRITE_U8(p_ctx, 0, "version");
495 WRITE_U24(p_ctx, 1, "flags");
496
497 WRITE_U16(p_ctx, 0, "graphicsmode");
498 WRITE_U16(p_ctx, 0, "opcolor");
499 WRITE_U16(p_ctx, 0, "opcolor");
500 WRITE_U16(p_ctx, 0, "opcolor");
501
502 return STREAM_STATUS(p_ctx);
503}
504
505/*****************************************************************************/
506static VC_CONTAINER_STATUS_T mp4_write_box_smhd( VC_CONTAINER_T *p_ctx )
507{
508 WRITE_U8(p_ctx, 0, "version");
509 WRITE_U24(p_ctx, 0, "flags");
510
511 WRITE_U16(p_ctx, 0, "balance");
512 WRITE_U16(p_ctx, 0, "reserved");
513
514 return STREAM_STATUS(p_ctx);
515}
516
517/*****************************************************************************/
518static VC_CONTAINER_STATUS_T mp4_write_box_dinf( VC_CONTAINER_T *p_ctx )
519{
520 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
521
522 status = mp4_write_box(p_ctx, MP4_BOX_TYPE_DREF);
523 if(status != VC_CONTAINER_SUCCESS) return status;
524
525 return status;
526}
527
528/*****************************************************************************/
529static VC_CONTAINER_STATUS_T mp4_write_box_dref( VC_CONTAINER_T *p_ctx )
530{
531 WRITE_U8(p_ctx, 0, "version");
532 WRITE_U24(p_ctx, 0, "flags");
533
534 WRITE_U32(p_ctx, 1, "entry_count");
535
536 /* Add a URL box */
537 WRITE_U32(p_ctx, 12, "box_size");
538 WRITE_FOURCC(p_ctx, VC_FOURCC('u','r','l',' '), "box_type");
539 WRITE_U8(p_ctx, 0, "version");
540 WRITE_U24(p_ctx, 0x1, "flags");
541
542 return STREAM_STATUS(p_ctx);
543}
544
545/*****************************************************************************/
546static VC_CONTAINER_STATUS_T mp4_write_box_stbl( VC_CONTAINER_T *p_ctx )
547{
548 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
549 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
550 VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
551
552 status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STSD);
553 if(status != VC_CONTAINER_SUCCESS) return status;
554
555 status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STTS);
556 if(status != VC_CONTAINER_SUCCESS) return status;
557
558 if( 0 && track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
559 {
560 status = mp4_write_box(p_ctx, MP4_BOX_TYPE_CTTS);
561 if(status != VC_CONTAINER_SUCCESS) return status;
562 }
563
564 status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STSC);
565 if(status != VC_CONTAINER_SUCCESS) return status;
566
567 status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STSZ);
568 if(status != VC_CONTAINER_SUCCESS) return status;
569
570 if(1)
571 status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STCO);
572 else
573 status = mp4_write_box(p_ctx, MP4_BOX_TYPE_CO64);
574
575 if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
576 {
577 status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STSS);
578 if(status != VC_CONTAINER_SUCCESS) return status;
579 }
580
581 return status;
582}
583
584/*****************************************************************************/
585static VC_CONTAINER_STATUS_T mp4_write_box_stsd( VC_CONTAINER_T *p_ctx )
586{
587 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
588 VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
589 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
590
591 WRITE_U8(p_ctx, 0, "version");
592 WRITE_U24(p_ctx, 0, "flags");
593
594 WRITE_U32(p_ctx, 1, "entry_count");
595
596 if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
597 status = mp4_write_box_extended(p_ctx, MP4_BOX_TYPE_VIDE, track->priv->module->fourcc);
598 else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO)
599 status = mp4_write_box_extended(p_ctx, MP4_BOX_TYPE_SOUN, track->priv->module->fourcc);
600#if 0
601 else if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE)
602 /*FIXME*/;
603#endif
604
605 return status;
606}
607
608/*****************************************************************************/
609static VC_CONTAINER_STATUS_T mp4_writer_write_sample_to_temp( VC_CONTAINER_T *p_ctx,
610 VC_CONTAINER_PACKET_T *packet)
611{
612 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
613 int32_t dts_diff = packet->dts - module->prev_sample_dts;
614 uint8_t keyframe = (packet->flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME) ? 0x80 : 0;
615
616 vc_container_io_write_be_uint32(module->temp.io, packet->size);
617 vc_container_io_write_be_uint32(module->temp.io, dts_diff);
618 vc_container_io_write_be_uint24(module->temp.io, (uint32_t)(packet->pts - packet->dts));
619 vc_container_io_write_uint8(module->temp.io, packet->track | keyframe);
620 module->prev_sample_dts = packet->dts;
621 return module->temp.io->status;
622}
623
624/*****************************************************************************/
625static VC_CONTAINER_STATUS_T mp4_writer_read_sample_from_temp( VC_CONTAINER_T *p_ctx,
626 VC_CONTAINER_PACKET_T *packet)
627{
628 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
629
630 packet->size = vc_container_io_read_be_uint32(module->temp.io);
631 packet->dts += (int32_t)vc_container_io_read_be_uint32(module->temp.io);
632 packet->pts = packet->dts + vc_container_io_read_be_uint24(module->temp.io);
633 packet->track = vc_container_io_read_uint8(module->temp.io);
634 packet->flags = (packet->track & 0x80) ? VC_CONTAINER_PACKET_FLAG_KEYFRAME : 0;
635 packet->track &= 0x7F;
636 return module->temp.io->status;
637}
638
639/*****************************************************************************/
640static VC_CONTAINER_STATUS_T mp4_write_box_stts( VC_CONTAINER_T *p_ctx )
641{
642 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
643 VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module;
644 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
645 VC_CONTAINER_PACKET_T sample;
646 unsigned int entries = 0;
647 int64_t last_dts = 0, delta;
648
649 WRITE_U8(p_ctx, 0, "version");
650 WRITE_U24(p_ctx, 0, "flags");
651
652 WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries, "entry_count");
653
654 if(module->null.refcount)
655 {
656 /* We're not actually writing the data, we just want the size */
657 WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries * 8);
658 return STREAM_STATUS(p_ctx);
659 }
660
661 /* Go through all the samples written */
662 vc_container_io_seek(module->temp.io, INT64_C(0));
663 sample.dts = 0;
664
665 status = mp4_writer_read_sample_from_temp(p_ctx, &sample);
666 while(status == VC_CONTAINER_SUCCESS)
667 {
668 if(sample.track != module->current_track) goto skip;
669
670 delta = sample.dts * MP4_TIMESCALE / 1000000 - last_dts;
671 if(delta < 0) delta = 0;
672 WRITE_U32(p_ctx, 1, "sample_count");
673 WRITE_U32(p_ctx, delta, "sample_delta");
674 entries++;
675 last_dts += delta;
676
677 skip:
678 status = mp4_writer_read_sample_from_temp(p_ctx, &sample);
679 }
680 vc_container_assert(entries == track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries);
681
682 return STREAM_STATUS(p_ctx);
683}
684
685/*****************************************************************************/
686static VC_CONTAINER_STATUS_T mp4_write_box_ctts( VC_CONTAINER_T *p_ctx )
687{
688 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
689 VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module;
690
691 WRITE_U8(p_ctx, 0, "version");
692 WRITE_U24(p_ctx, 0, "flags");
693 WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_CTTS].entries, "entry_count");
694 return STREAM_STATUS(p_ctx);
695}
696
697/*****************************************************************************/
698static VC_CONTAINER_STATUS_T mp4_write_box_stsc( VC_CONTAINER_T *p_ctx )
699{
700 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
701 VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module;
702 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
703 VC_CONTAINER_PACKET_T sample;
704 int64_t offset = 0, track_offset = -1;
705 unsigned int entries = 0, chunks = 0, first_chunk = 0, samples_in_chunk = 0;
706
707 memset(&sample, 0, sizeof(VC_CONTAINER_PACKET_T));
708
709 WRITE_U8(p_ctx, 0, "version");
710 WRITE_U24(p_ctx, 0, "flags");
711 WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries, "entry_count");
712
713 if(module->null.refcount)
714 {
715 /* We're not actually writing the data, we just want the size */
716 WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries * 12);
717 return STREAM_STATUS(p_ctx);
718 }
719
720 /* Go through all the samples written */
721 vc_container_io_seek(module->temp.io, INT64_C(0));
722
723 status = mp4_writer_read_sample_from_temp(p_ctx, &sample);
724 while(status == VC_CONTAINER_SUCCESS)
725 {
726 if(sample.track != module->current_track) goto skip;
727
728 /* Is it a new chunk ? */
729 if(track_offset != offset)
730 {
731 chunks++;
732 if(samples_in_chunk)
733 {
734 WRITE_U32(p_ctx, first_chunk, "first_chunk");
735 WRITE_U32(p_ctx, samples_in_chunk, "samples_per_chunk");
736 WRITE_U32(p_ctx, 1, "sample_description_index");
737 entries++;
738 }
739 first_chunk = chunks;
740 samples_in_chunk = 0;
741 }
742 track_offset = offset + sample.size;
743 samples_in_chunk++;
744
745 skip:
746 offset += sample.size;
747 status = mp4_writer_read_sample_from_temp(p_ctx, &sample);
748 }
749
750 if(samples_in_chunk)
751 {
752 WRITE_U32(p_ctx, first_chunk, "first_chunk");
753 WRITE_U32(p_ctx, samples_in_chunk, "samples_per_chunk");
754 WRITE_U32(p_ctx, 1, "sample_description_index");
755 entries++;
756 }
757
758 vc_container_assert(entries == track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries);
759
760 return STREAM_STATUS(p_ctx);
761}
762
763/*****************************************************************************/
764static VC_CONTAINER_STATUS_T mp4_write_box_stsz( VC_CONTAINER_T *p_ctx )
765{
766 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
767 VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module;
768 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
769 VC_CONTAINER_PACKET_T sample;
770 unsigned int entries = 0;
771
772 memset(&sample, 0, sizeof(VC_CONTAINER_PACKET_T));
773
774 WRITE_U8(p_ctx, 0, "version");
775 WRITE_U24(p_ctx, 0, "flags");
776
777 WRITE_U32(p_ctx, 0, "sample_size");
778 WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STSZ].entries, "sample_count");
779
780 if(module->null.refcount)
781 {
782 /* We're not actually writing the data, we just want the size */
783 WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_STSZ].entries * 4);
784 return STREAM_STATUS(p_ctx);
785 }
786
787 /* Go through all the samples written */
788 vc_container_io_seek(module->temp.io, INT64_C(0));
789
790 status = mp4_writer_read_sample_from_temp(p_ctx, &sample);
791 while(status == VC_CONTAINER_SUCCESS)
792 {
793 if(sample.track != module->current_track) goto skip;
794
795 WRITE_U32(p_ctx, sample.size, "entry_size");
796 entries++;
797
798 skip:
799 status = mp4_writer_read_sample_from_temp(p_ctx, &sample);
800 }
801 vc_container_assert(entries == track_module->sample_table[MP4_SAMPLE_TABLE_STSZ].entries);
802
803 return STREAM_STATUS(p_ctx);
804}
805
806/*****************************************************************************/
807static VC_CONTAINER_STATUS_T mp4_write_box_stco( VC_CONTAINER_T *p_ctx )
808{
809 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
810 VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module;
811 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
812 VC_CONTAINER_PACKET_T sample;
813 int64_t offset = module->data_offset, track_offset = -1;
814 unsigned int entries = 0;
815
816 memset(&sample, 0, sizeof(VC_CONTAINER_PACKET_T));
817
818 WRITE_U8(p_ctx, 0, "version");
819 WRITE_U24(p_ctx, 0, "flags");
820 WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STCO].entries, "entry_count");
821
822 if(module->null.refcount)
823 {
824 /* We're not actually writing the data, we just want the size */
825 WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_STCO].entries * 4);
826 return STREAM_STATUS(p_ctx);
827 }
828
829 /* Go through all the samples written */
830 vc_container_io_seek(module->temp.io, INT64_C(0));
831
832 status = mp4_writer_read_sample_from_temp(p_ctx, &sample);
833 while(status == VC_CONTAINER_SUCCESS)
834 {
835 if(sample.track != module->current_track) goto skip;
836
837 /* Is it a new chunk ? */
838 if(track_offset != offset)
839 {
840 WRITE_U32(p_ctx, offset, "chunk_offset");
841 entries++;
842 }
843 track_offset = offset + sample.size;
844
845 skip:
846 offset += sample.size;
847 status = mp4_writer_read_sample_from_temp(p_ctx, &sample);
848 }
849 vc_container_assert(entries == track_module->sample_table[MP4_SAMPLE_TABLE_STCO].entries);
850
851 return STREAM_STATUS(p_ctx);
852}
853
854/*****************************************************************************/
855static VC_CONTAINER_STATUS_T mp4_write_box_co64( VC_CONTAINER_T *p_ctx )
856{
857 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
858 VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module;
859
860 WRITE_U8(p_ctx, 0, "version");
861 WRITE_U24(p_ctx, 0, "flags");
862 WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_CO64].entries, "entry_count");
863
864 if(module->null.refcount)
865 {
866 /* We're not actually writing the data, we just want the size */
867 WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_CO64].entries * 8);
868 return STREAM_STATUS(p_ctx);
869 }
870
871 return STREAM_STATUS(p_ctx);
872}
873
874/*****************************************************************************/
875static VC_CONTAINER_STATUS_T mp4_write_box_stss( VC_CONTAINER_T *p_ctx )
876{
877 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
878 VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module;
879 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
880 VC_CONTAINER_PACKET_T sample;
881 unsigned int entries = 0, samples = 0;
882
883 memset(&sample, 0, sizeof(VC_CONTAINER_PACKET_T));
884
885 WRITE_U8(p_ctx, 0, "version");
886 WRITE_U24(p_ctx, 0, "flags");
887 WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries, "entry_count");
888
889 if(module->null.refcount)
890 {
891 /* We're not actually writing the data, we just want the size */
892 WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries * 4);
893 return STREAM_STATUS(p_ctx);
894 }
895
896 /* Go through all the samples written */
897 vc_container_io_seek(module->temp.io, INT64_C(0));
898
899 status = mp4_writer_read_sample_from_temp(p_ctx, &sample);
900 while(status == VC_CONTAINER_SUCCESS)
901 {
902 if(sample.track != module->current_track) goto skip;
903
904 samples++;
905 if(sample.flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME)
906 {
907 WRITE_U32(p_ctx, samples, "sample_number");
908 entries++;
909 }
910
911 skip:
912 status = mp4_writer_read_sample_from_temp(p_ctx, &sample);
913 }
914 vc_container_assert(entries == track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries);
915
916 return STREAM_STATUS(p_ctx);
917}
918
919/*****************************************************************************/
920static VC_CONTAINER_STATUS_T mp4_write_box_vide_avcC( VC_CONTAINER_T *p_ctx )
921{
922 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
923 VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
924
925 WRITE_U32(p_ctx, track->format->extradata_size + 8, "size");
926 WRITE_FOURCC(p_ctx, VC_FOURCC('a','v','c','C'), "type");
927 WRITE_BYTES(p_ctx, track->format->extradata, track->format->extradata_size);
928
929 return STREAM_STATUS(p_ctx);
930}
931
932/*****************************************************************************/
933static VC_CONTAINER_STATUS_T mp4_write_box_vide_d263( VC_CONTAINER_T *p_ctx )
934{
935 WRITE_U32(p_ctx, 8 + 7, "size");
936 WRITE_FOURCC(p_ctx, VC_FOURCC('d','2','6','3'), "type");
937 WRITE_FOURCC(p_ctx, VC_FOURCC('B','R','C','M'), "vendor");
938 WRITE_U8(p_ctx, 0, "version");
939 WRITE_U8(p_ctx, 10, "level");
940 WRITE_U8(p_ctx, 0, "profile");
941
942 return STREAM_STATUS(p_ctx);
943}
944
945/*****************************************************************************/
946static VC_CONTAINER_STATUS_T mp4_write_box_vide( VC_CONTAINER_T *p_ctx )
947{
948 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
949 VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
950 unsigned int i;
951
952 for(i = 0; i < 6; i++) WRITE_U8(p_ctx, 0, "reserved");
953 WRITE_U16(p_ctx, 1, "data_reference_index");
954
955 WRITE_U16(p_ctx, 0, "pre_defined");
956 WRITE_U16(p_ctx, 0, "reserved");
957 for(i = 0; i < 3; i++) WRITE_U32(p_ctx, 0, "pre_defined");
958 WRITE_U16(p_ctx, track->format->type->video.width, "width");
959 WRITE_U16(p_ctx, track->format->type->video.height, "height");
960 WRITE_U32(p_ctx, 0x480000, "horizresolution"); /* 72 dpi */
961 WRITE_U32(p_ctx, 0x480000, "vertresolution"); /* 72 dpi */
962 WRITE_U32(p_ctx, 0, "reserved");
963 WRITE_U16(p_ctx, 1, "frame_count");
964 for(i = 0; i < 32; i++) _WRITE_U8(p_ctx, 0);
965 WRITE_U16(p_ctx, 0x18, "depth");
966 WRITE_U16(p_ctx, -1, "pre_defined");
967
968 switch(track->format->codec)
969 {
970 case VC_CONTAINER_CODEC_H264: return mp4_write_box_vide_avcC(p_ctx);
971 case VC_CONTAINER_CODEC_H263: return mp4_write_box_vide_d263(p_ctx);
972 case VC_CONTAINER_CODEC_MP4V: return mp4_write_box(p_ctx, MP4_BOX_TYPE_ESDS);
973 default: break;
974 }
975
976 return STREAM_STATUS(p_ctx);
977}
978
979/*****************************************************************************/
980static VC_CONTAINER_STATUS_T mp4_write_box_soun_damr( VC_CONTAINER_T *p_ctx )
981{
982 WRITE_U32(p_ctx, 8 + 8, "size");
983 WRITE_FOURCC(p_ctx, VC_FOURCC('d','a','m','r'), "type");
984 WRITE_FOURCC(p_ctx, VC_FOURCC('B','R','C','M'), "vendor");
985 WRITE_U8(p_ctx, 0, "version");
986 WRITE_U8(p_ctx, 0x80, "mode_set");
987 WRITE_U8(p_ctx, 0, "mode_change_period");
988 WRITE_U8(p_ctx, 1, "frame_per_second");
989
990 return STREAM_STATUS(p_ctx);
991}
992
993/*****************************************************************************/
994static VC_CONTAINER_STATUS_T mp4_write_box_soun_dawp( VC_CONTAINER_T *p_ctx )
995{
996 WRITE_U32(p_ctx, 8 + 5, "size");
997 WRITE_FOURCC(p_ctx, VC_FOURCC('d','a','w','p'), "type");
998 WRITE_FOURCC(p_ctx, VC_FOURCC('B','R','C','M'), "vendor");
999 WRITE_U8(p_ctx, 0, "version");
1000
1001 return STREAM_STATUS(p_ctx);
1002}
1003
1004/*****************************************************************************/
1005static VC_CONTAINER_STATUS_T mp4_write_box_soun_devc( VC_CONTAINER_T *p_ctx )
1006{
1007 WRITE_U32(p_ctx, 8 + 6, "size");
1008 WRITE_FOURCC(p_ctx, VC_FOURCC('d','e','v','c'), "type");
1009 WRITE_FOURCC(p_ctx, VC_FOURCC('B','R','C','M'), "vendor");
1010 WRITE_U8(p_ctx, 0, "version");
1011 WRITE_U8(p_ctx, 1, "samples_per_frame");
1012
1013 return STREAM_STATUS(p_ctx);
1014}
1015
1016/*****************************************************************************/
1017static VC_CONTAINER_STATUS_T mp4_write_box_soun( VC_CONTAINER_T *p_ctx )
1018{
1019 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
1020 VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
1021 unsigned int i, version = 0;
1022
1023 for(i = 0; i < 6; i++) WRITE_U8(p_ctx, 0, "reserved");
1024 WRITE_U16(p_ctx, 1, "data_reference_index");
1025
1026 if(module->brand == MP4_BRAND_QT)
1027 {
1028 if(track->format->codec == VC_CONTAINER_CODEC_MP4A) version = 1;
1029 WRITE_U16(p_ctx, version, "version");
1030 WRITE_U16(p_ctx, 0, "revision_level");
1031 WRITE_U32(p_ctx, 0, "vendor");
1032 }
1033 else
1034 {
1035 for(i = 0; i < 2; i++) WRITE_U32(p_ctx, 0, "reserved");
1036 }
1037
1038 WRITE_U16(p_ctx, track->format->type->audio.channels, "channelcount");
1039 WRITE_U16(p_ctx, 0, "samplesize");
1040 WRITE_U16(p_ctx, 0, "pre_defined");
1041 WRITE_U16(p_ctx, 0, "reserved");
1042 WRITE_U32(p_ctx, track->format->type->audio.sample_rate << 16, "samplerate");
1043
1044 if(module->brand == MP4_BRAND_QT && version == 1) /* FIXME */
1045 {
1046 WRITE_U32(p_ctx, 1024, "samples_per_packet");
1047 WRITE_U32(p_ctx, 1536, "bytes_per_packet");
1048 WRITE_U32(p_ctx, 2, "bytes_per_frame");
1049 WRITE_U32(p_ctx, 2, "bytes_per_sample");
1050 }
1051
1052 switch(track->format->codec)
1053 {
1054 case VC_CONTAINER_CODEC_AMRNB:
1055 case VC_CONTAINER_CODEC_AMRWB:
1056 return mp4_write_box_soun_damr(p_ctx);
1057 case VC_CONTAINER_CODEC_AMRWBP:
1058 return mp4_write_box_soun_dawp(p_ctx);
1059 case VC_CONTAINER_CODEC_EVRC:
1060 return mp4_write_box_soun_devc(p_ctx);
1061 case VC_CONTAINER_CODEC_MP4A:
1062 case VC_CONTAINER_CODEC_MPGA:
1063 return mp4_write_box(p_ctx, MP4_BOX_TYPE_ESDS);
1064 default: break;
1065 }
1066
1067 return STREAM_STATUS(p_ctx);
1068}
1069
1070/*****************************************************************************/
1071static VC_CONTAINER_STATUS_T mp4_write_box_esds( VC_CONTAINER_T *p_ctx )
1072{
1073 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
1074 VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
1075 unsigned int decoder_specific_size = 0, decoder_config_size, sl_size;
1076 unsigned int stream_type, object_type;
1077
1078#define MP4_GET_DESCRIPTOR_SIZE(size) \
1079 ((size) < 0x0080) ? 2 + (size) : ((size) < 0x4000) ? 3 + (size) : 4 + (size)
1080#define MP4_WRITE_DESCRIPTOR_HEADER(type, size) \
1081 LOG_FORMAT(p_ctx, "descriptor %x, size %i", type, size); _WRITE_U8(p_ctx, type); \
1082 if((size) >= 0x4000) _WRITE_U8(p_ctx, (((size) >> 14) & 0x7F) | 0x80); \
1083 if((size) >= 0x80 ) _WRITE_U8(p_ctx, (((size) >> 7 ) & 0x7F) | 0x80); \
1084 _WRITE_U8(p_ctx, (size) & 0x7F)
1085
1086 /* We only support small size descriptors */
1087 if(track->format->extradata_size > 0x200000 - 100)
1088 return VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED;
1089
1090 switch(track->format->es_type)
1091 {
1092 case VC_CONTAINER_ES_TYPE_VIDEO: stream_type = 0x4; break;
1093 case VC_CONTAINER_ES_TYPE_AUDIO: stream_type = 0x5; break;
1094 case VC_CONTAINER_ES_TYPE_SUBPICTURE: stream_type = 0x20; break;
1095 default: return VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED;
1096 }
1097 switch(track->format->codec)
1098 {
1099 case VC_CONTAINER_CODEC_MP4V: object_type = 0x20; break;
1100 case VC_CONTAINER_CODEC_MP1V: object_type = 0x6B; break;
1101 case VC_CONTAINER_CODEC_MP2V: object_type = 0x60; break;
1102 case VC_CONTAINER_CODEC_JPEG: object_type = 0x6C; break;
1103 case VC_CONTAINER_CODEC_MP4A: object_type = 0x40; break;
1104 case VC_CONTAINER_CODEC_MPGA:
1105 object_type = track->format->type->audio.sample_rate < 32000 ? 0x69 : 0x6B; break;
1106 default: return VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED;
1107 }
1108
1109 decoder_specific_size = MP4_GET_DESCRIPTOR_SIZE(track->format->extradata_size);
1110 decoder_config_size = MP4_GET_DESCRIPTOR_SIZE(13 + decoder_specific_size);
1111 sl_size = MP4_GET_DESCRIPTOR_SIZE(1);
1112
1113 WRITE_U8(p_ctx, 0, "version");
1114 WRITE_U24(p_ctx, 0, "flags");
1115
1116 /* Write the ES descriptor */
1117 MP4_WRITE_DESCRIPTOR_HEADER(0x3, 3 + decoder_config_size + sl_size);
1118 WRITE_U16(p_ctx, module->current_track + 1, "es_id");
1119 WRITE_U8(p_ctx, 0x1f, "flags"); /* stream_priority = 0x1f */
1120
1121 /* Write the Decoder Config descriptor */
1122 MP4_WRITE_DESCRIPTOR_HEADER(0x4, 13 + decoder_specific_size);
1123 WRITE_U8(p_ctx, object_type, "object_type_indication");
1124 WRITE_U8(p_ctx, (stream_type << 2) | 1, "stream_type");
1125 WRITE_U24(p_ctx, 8000, "buffer_size_db");
1126 WRITE_U32(p_ctx, track->format->bitrate, "max_bitrate");
1127 WRITE_U32(p_ctx, track->format->bitrate, "avg_bitrate");
1128 if(track->format->extradata_size)
1129 {
1130 MP4_WRITE_DESCRIPTOR_HEADER(0x5, track->format->extradata_size);
1131 WRITE_BYTES(p_ctx, track->format->extradata, track->format->extradata_size);
1132 }
1133
1134 /* Write the SL descriptor */
1135 MP4_WRITE_DESCRIPTOR_HEADER(0x6, 1);
1136 WRITE_U8(p_ctx, 0x2, "flags");
1137
1138 return STREAM_STATUS(p_ctx);
1139}
1140
1141/*****************************************************************************/
1142static VC_CONTAINER_STATUS_T mp4_writer_close( VC_CONTAINER_T *p_ctx )
1143{
1144 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
1145 VC_CONTAINER_STATUS_T status;
1146 int64_t mdat_size;
1147
1148 mdat_size = STREAM_POSITION(p_ctx) - module->mdat_offset;
1149
1150 /* Write the moov box */
1151 status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MOOV);
1152
1153 /* Finalise the mdat box */
1154 SEEK(p_ctx, module->mdat_offset);
1155 WRITE_U32(p_ctx, (uint32_t)mdat_size, "mdat size" );
1156
1157 for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--)
1158 vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]);
1159
1160 vc_container_writer_extraio_delete(p_ctx, &module->temp);
1161 vc_container_writer_extraio_delete(p_ctx, &module->null);
1162 free(module);
1163
1164 return status;
1165}
1166
1167/*****************************************************************************/
1168static VC_CONTAINER_STATUS_T mp4_writer_add_track( VC_CONTAINER_T *p_ctx, VC_CONTAINER_ES_FORMAT_T *format )
1169{
1170 VC_CONTAINER_STATUS_T status;
1171 VC_CONTAINER_TRACK_T *track;
1172 uint32_t type = 0;
1173
1174 if(!(format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED))
1175 return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
1176
1177 /* Check we support this format */
1178 switch(format->codec)
1179 {
1180 case VC_CONTAINER_CODEC_AMRNB: type = VC_FOURCC('s','a','m','r'); break;
1181 case VC_CONTAINER_CODEC_AMRWB: type = VC_FOURCC('s','a','w','b'); break;
1182 case VC_CONTAINER_CODEC_AMRWBP: type = VC_FOURCC('s','a','w','p'); break;
1183 case VC_CONTAINER_CODEC_EVRC: type = VC_FOURCC('s','e','v','c'); break;
1184 case VC_CONTAINER_CODEC_MP4A: type = VC_FOURCC('m','p','4','a'); break;
1185 case VC_CONTAINER_CODEC_MPGA: type = VC_FOURCC('m','p','4','a'); break;
1186
1187 case VC_CONTAINER_CODEC_MP4V: type = VC_FOURCC('m','p','4','v'); break;
1188 case VC_CONTAINER_CODEC_JPEG: type = VC_FOURCC('m','p','4','v'); break;
1189 case VC_CONTAINER_CODEC_H263: type = VC_FOURCC('s','2','6','3'); break;
1190 case VC_CONTAINER_CODEC_H264:
1191 if(format->codec_variant == VC_FOURCC('a','v','c','C')) type = VC_FOURCC('a','v','c','1'); break;
1192 case VC_CONTAINER_CODEC_MJPEG: type = VC_FOURCC('j','p','e','g'); break;
1193 case VC_CONTAINER_CODEC_MJPEGA: type = VC_FOURCC('m','j','p','a'); break;
1194 case VC_CONTAINER_CODEC_MJPEGB: type = VC_FOURCC('m','j','p','b'); break;
1195 case VC_CONTAINER_CODEC_MP1V: type = VC_FOURCC('m','p','e','g'); break;
1196 case VC_CONTAINER_CODEC_MP2V: type = VC_FOURCC('m','p','e','g'); break;
1197
1198 default: type = 0; break;
1199 }
1200
1201 if(!type) return VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED;
1202
1203 /* Allocate and initialise track data */
1204 if(p_ctx->tracks_num >= MP4_TRACKS_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
1205 p_ctx->tracks[p_ctx->tracks_num] = track =
1206 vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module));
1207 if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
1208
1209 if(format->extradata_size)
1210 {
1211 status = vc_container_track_allocate_extradata( p_ctx, track, format->extradata_size );
1212 if(status) goto error;
1213 }
1214
1215 vc_container_format_copy(track->format, format, format->extradata_size);
1216 track->priv->module->fourcc = type;
1217 track->priv->module->offset = -1;
1218 track->priv->module->sample_table[MP4_SAMPLE_TABLE_STTS].entry_size = 8;
1219 track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSZ].entry_size = 4;
1220 track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSC].entry_size = 12;
1221 track->priv->module->sample_table[MP4_SAMPLE_TABLE_STCO].entry_size = 4;
1222 track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSS].entry_size = 4;
1223 track->priv->module->sample_table[MP4_SAMPLE_TABLE_CO64].entry_size = 8;
1224 track->priv->module->sample_table[MP4_SAMPLE_TABLE_CTTS].entry_size = 8;
1225
1226 p_ctx->tracks_num++;
1227 return VC_CONTAINER_SUCCESS;
1228
1229 error:
1230 vc_container_free_track(p_ctx, track);
1231 return status;
1232}
1233
1234/*****************************************************************************/
1235static VC_CONTAINER_STATUS_T mp4_writer_add_track_done( VC_CONTAINER_T *p_ctx )
1236{
1237 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
1238 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
1239 if(module->tracks_add_done) return status;
1240
1241 /* We need to find out the size of the object we're going to write it. */
1242 if(!vc_container_writer_extraio_enable(p_ctx, &module->null))
1243 {
1244 status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MOOV);
1245 module->moov_size = STREAM_POSITION(p_ctx);
1246 p_ctx->size = module->moov_size;
1247 }
1248 vc_container_writer_extraio_disable(p_ctx, &module->null);
1249
1250 if(status == VC_CONTAINER_SUCCESS) module->tracks_add_done = true;
1251 return status;
1252}
1253
1254/*****************************************************************************/
1255static VC_CONTAINER_STATUS_T mp4_writer_control( VC_CONTAINER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, va_list args )
1256{
1257 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
1258
1259 switch(operation)
1260 {
1261 case VC_CONTAINER_CONTROL_TRACK_ADD:
1262 {
1263 VC_CONTAINER_ES_FORMAT_T *p_format =
1264 (VC_CONTAINER_ES_FORMAT_T *)va_arg( args, VC_CONTAINER_ES_FORMAT_T * );
1265 if(module->tracks_add_done) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
1266 return mp4_writer_add_track(p_ctx, p_format);
1267 }
1268
1269 case VC_CONTAINER_CONTROL_TRACK_ADD_DONE:
1270 return mp4_writer_add_track_done(p_ctx);
1271
1272 default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
1273 }
1274}
1275
1276/*****************************************************************************/
1277static VC_CONTAINER_STATUS_T mp4_writer_add_sample( VC_CONTAINER_T *p_ctx,
1278 VC_CONTAINER_PACKET_T *packet )
1279{
1280 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
1281 VC_CONTAINER_TRACK_T *track = p_ctx->tracks[packet->track];
1282 VC_CONTAINER_TRACK_MODULE_T *track_module = track->priv->module;
1283
1284 track_module->last_pts = packet->pts;
1285 if(!track_module->samples) track_module->first_pts = packet->pts;
1286
1287 track_module->samples++;
1288 track_module->sample_table[MP4_SAMPLE_TABLE_STSZ].entries++; /* sample size */
1289 p_ctx->size += track_module->sample_table[MP4_SAMPLE_TABLE_STSZ].entry_size;
1290 track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries++; /* time to sample */
1291 p_ctx->size += track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entry_size;
1292
1293 #if 0
1294 delta_ts = packet->dts - track_module->timestamp;
1295 track_module->timestamp = packet->dts;
1296 if(!track_module->samples) track_module->delta_ts =
1297 if()
1298#endif
1299
1300 /* Is it a new chunk ? */
1301 if(module->sample_offset != track_module->offset)
1302 {
1303 track_module->chunks++;
1304 track_module->sample_table[MP4_SAMPLE_TABLE_STCO].entries++; /* chunk offset */
1305 p_ctx->size += track_module->sample_table[MP4_SAMPLE_TABLE_STCO].entry_size;
1306 track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries++; /* sample to chunk */
1307 p_ctx->size += track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entry_size;
1308 }
1309 track_module->offset = module->sample_offset + packet->size;
1310
1311 if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO &&
1312 (packet->flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME))
1313 {
1314 track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries++; /* sync sample */
1315 p_ctx->size += track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entry_size;
1316 }
1317
1318 return VC_CONTAINER_SUCCESS;
1319}
1320
1321/*****************************************************************************/
1322static VC_CONTAINER_STATUS_T mp4_writer_write( VC_CONTAINER_T *p_ctx,
1323 VC_CONTAINER_PACKET_T *packet )
1324{
1325 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
1326 VC_CONTAINER_PACKET_T *sample = &module->sample;
1327 VC_CONTAINER_STATUS_T status;
1328
1329 if(!module->tracks_add_done)
1330 {
1331 status = mp4_writer_add_track_done(p_ctx);
1332 if(status != VC_CONTAINER_SUCCESS) return status;
1333 }
1334
1335 if(packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_START)
1336 ++module->samples; /* Switching to a new sample */
1337
1338 if(packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_START)
1339 {
1340 module->sample_offset = STREAM_POSITION(p_ctx);
1341 sample->size = packet->size;
1342 sample->pts = packet->pts;
1343 sample->dts = packet->pts;
1344 sample->track = packet->track;
1345 sample->flags = packet->flags;
1346 }
1347 else
1348 {
1349 sample->size += packet->size;
1350 sample->flags |= packet->flags;
1351 }
1352
1353 if(WRITE_BYTES(p_ctx, packet->data, packet->size) != packet->size)
1354 return STREAM_STATUS(p_ctx); // TODO do something
1355 p_ctx->size += packet->size;
1356
1357 //
1358 if(packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_END)
1359 {
1360 status = mp4_writer_write_sample_to_temp(p_ctx, sample);
1361 status = mp4_writer_add_sample(p_ctx, sample);
1362 }
1363
1364 return VC_CONTAINER_SUCCESS;
1365}
1366
1367/******************************************************************************
1368Global function definitions.
1369******************************************************************************/
1370
1371VC_CONTAINER_STATUS_T mp4_writer_open( VC_CONTAINER_T *p_ctx )
1372{
1373 VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
1374 const char *extension = vc_uri_path_extension(p_ctx->priv->uri);
1375 VC_CONTAINER_MODULE_T *module = 0;
1376 MP4_BRAND_T brand;
1377
1378 /* Check if the user has specified a container */
1379 vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension);
1380
1381 /* Check we're the right writer for this */
1382 if(!extension)
1383 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
1384 if(strcasecmp(extension, "3gp") && strcasecmp(extension, "skm") &&
1385 strcasecmp(extension, "mov") && strcasecmp(extension, "mp4") &&
1386 strcasecmp(extension, "m4v") && strcasecmp(extension, "m4a"))
1387 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
1388
1389 /* Allocate our context */
1390 module = malloc(sizeof(*module));
1391 if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
1392 memset(module, 0, sizeof(*module));
1393 p_ctx->priv->module = module;
1394 p_ctx->tracks = module->tracks;
1395
1396 /* Find out which brand we're going write */
1397 if(!strcasecmp(extension, "3gp")) brand = MP4_BRAND_3GP5;
1398 else if(!strcasecmp(extension, "skm")) brand = MP4_BRAND_SKM2;
1399 else if(!strcasecmp(extension, "mov")) brand = MP4_BRAND_QT;
1400 else brand = MP4_BRAND_ISOM;
1401 module->brand = brand;
1402
1403 /* Create a null i/o writer to help us out in writing our data */
1404 status = vc_container_writer_extraio_create_null(p_ctx, &module->null);
1405 if(status != VC_CONTAINER_SUCCESS) goto error;
1406
1407 /* Create a temporary i/o writer to help us out in writing our data */
1408 status = vc_container_writer_extraio_create_temp(p_ctx, &module->temp);
1409 if(status != VC_CONTAINER_SUCCESS) goto error;
1410
1411 status = mp4_write_box(p_ctx, MP4_BOX_TYPE_FTYP);
1412 if(status != VC_CONTAINER_SUCCESS) goto error;
1413
1414 /* Start the mdat box */
1415 module->mdat_offset = STREAM_POSITION(p_ctx);
1416 WRITE_U32(p_ctx, 0, "size");
1417 WRITE_FOURCC(p_ctx, VC_FOURCC('m','d','a','t'), "type");
1418 module->data_offset = STREAM_POSITION(p_ctx);
1419
1420 p_ctx->priv->pf_close = mp4_writer_close;
1421 p_ctx->priv->pf_write = mp4_writer_write;
1422 p_ctx->priv->pf_control = mp4_writer_control;
1423 return VC_CONTAINER_SUCCESS;
1424
1425 error:
1426 LOG_DEBUG(p_ctx, "mp4: error opening stream");
1427 if(module)
1428 {
1429 if(module->null.io) vc_container_writer_extraio_delete(p_ctx, &module->null);
1430 free(module);
1431 }
1432 return status;
1433}
1434
1435/********************************************************************************
1436 Entrypoint function
1437 ********************************************************************************/
1438
1439#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
1440# pragma weak writer_open mp4_writer_open
1441#endif
1442