Commit | Line | Data |
---|---|---|
1cb5f7fd MN |
1 | /* |
2 | * MOV, 3GP, MP4 encoder. | |
3 | * Copyright (c) 2003 Thomas Raivio. | |
69dde1ad | 4 | * Copyright (c) 2004 Gildas Bazin <gbazin at videolan dot org>. |
1cb5f7fd MN |
5 | * |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
5509bffa | 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
1cb5f7fd MN |
19 | */ |
20 | #include "avformat.h" | |
f578f938 | 21 | #include "avi.h" |
1cb5f7fd | 22 | #include "avio.h" |
1cb5f7fd | 23 | |
6e6d6dc0 MN |
24 | #undef NDEBUG |
25 | #include <assert.h> | |
26 | ||
1cb5f7fd MN |
27 | #define MOV_INDEX_CLUSTER_SIZE 16384 |
28 | #define globalTimescale 1000 | |
29 | ||
69dde1ad GB |
30 | #define MODE_MP4 0 |
31 | #define MODE_MOV 1 | |
32 | #define MODE_3GP 2 | |
115329f1 | 33 | #define MODE_PSP 3 // example working PSP command line: |
8af18154 | 34 | // ffmpeg -i testinput.avi -f psp -r 14.985 -s 320x240 -b 768 -ar 24000 -ab 32 M4V00001.MP4 |
8536ab89 | 35 | #define MODE_3G2 4 |
69dde1ad | 36 | |
1cb5f7fd | 37 | typedef struct MOVIentry { |
b29af723 MN |
38 | unsigned int flags, size; |
39 | uint64_t pos; | |
e45ccf79 | 40 | unsigned int samplesInChunk; |
f578f938 | 41 | char key_frame; |
1cb5f7fd MN |
42 | unsigned int entries; |
43 | } MOVIentry; | |
44 | ||
45 | typedef struct MOVIndex { | |
69dde1ad | 46 | int mode; |
1cb5f7fd | 47 | int entry; |
b29af723 | 48 | uint64_t mdat_size; |
1cb5f7fd MN |
49 | int ents_allocated; |
50 | long timescale; | |
51 | long time; | |
f578f938 | 52 | long trackDuration; |
e45ccf79 GB |
53 | long sampleCount; |
54 | long sampleDuration; | |
f578f938 | 55 | int hasKeyframes; |
ab561df9 | 56 | int language; |
1cb5f7fd MN |
57 | int trackID; |
58 | AVCodecContext *enc; | |
59 | ||
60 | int vosLen; | |
6e6d6dc0 | 61 | uint8_t *vosData; |
1cb5f7fd MN |
62 | MOVIentry** cluster; |
63 | } MOVTrack; | |
64 | ||
8af18154 | 65 | typedef struct MOVContext { |
69dde1ad | 66 | int mode; |
1cb5f7fd MN |
67 | long time; |
68 | int nb_streams; | |
f578f938 TR |
69 | int mdat_written; |
70 | offset_t mdat_pos; | |
1cb5f7fd MN |
71 | long timescale; |
72 | MOVTrack tracks[MAX_STREAMS]; | |
73 | } MOVContext; | |
74 | ||
e45ccf79 GB |
75 | static int mov_write_esds_tag(ByteIOContext *pb, MOVTrack* track); |
76 | ||
ab561df9 FR |
77 | /* output language code from iso639 language name */ |
78 | extern int ff_mov_iso639_to_lang(const char *lang, int mp4); | |
79 | ||
6c13c880 MN |
80 | const CodecTag ff_mov_obj_type[] = { |
81 | { CODEC_ID_MPEG4 , 32 }, | |
82 | { CODEC_ID_AAC , 64 }, | |
83 | { CODEC_ID_MPEG1VIDEO, 106 }, | |
84 | { CODEC_ID_MPEG2VIDEO, 96 },//mpeg2 profiles | |
85 | { CODEC_ID_MP2 , 107 },//FIXME mpeg2 mpeg audio -> 105 | |
86 | { CODEC_ID_MP3 , 107 },//FIXME mpeg2 mpeg audio -> 105 | |
5cad192d | 87 | { CODEC_ID_H264 , 33 }, |
6c13c880 MN |
88 | { CODEC_ID_H263 , 242 }, |
89 | { CODEC_ID_H261 , 243 }, | |
90 | { CODEC_ID_MJPEG , 108 }, | |
91 | { CODEC_ID_PCM_S16LE , 224 }, | |
92 | { CODEC_ID_VORBIS , 225 }, | |
93 | { CODEC_ID_AC3 , 226 }, | |
94 | { CODEC_ID_PCM_ALAW , 227 }, | |
95 | { CODEC_ID_PCM_MULAW , 228 }, | |
96 | { CODEC_ID_PCM_S16BE , 230 }, | |
97 | { 0,0 }, | |
98 | }; | |
99 | ||
6e6d6dc0 | 100 | //FIXME supprt 64bit varaint with wide placeholders |
b29af723 | 101 | static offset_t updateSize (ByteIOContext *pb, offset_t pos) |
1cb5f7fd | 102 | { |
b29af723 | 103 | offset_t curpos = url_ftell(pb); |
1cb5f7fd | 104 | url_fseek(pb, pos, SEEK_SET); |
6e6d6dc0 | 105 | put_be32(pb, curpos - pos); /* rewrite size */ |
1cb5f7fd | 106 | url_fseek(pb, curpos, SEEK_SET); |
6e6d6dc0 MN |
107 | |
108 | return curpos - pos; | |
1cb5f7fd MN |
109 | } |
110 | ||
e45ccf79 | 111 | /* Chunk offset atom */ |
6e6d6dc0 | 112 | static int mov_write_stco_tag(ByteIOContext *pb, MOVTrack* track) |
1cb5f7fd MN |
113 | { |
114 | int i; | |
ed06cf6d | 115 | int mode64 = 0; // use 32 bit size variant if possible |
b29af723 | 116 | offset_t pos = url_ftell(pb); |
f578f938 | 117 | put_be32(pb, 0); /* size */ |
b29af723 MN |
118 | if (pos > UINT32_MAX) { |
119 | mode64 = 1; | |
120 | put_tag(pb, "co64"); | |
121 | } else | |
122 | put_tag(pb, "stco"); | |
1cb5f7fd MN |
123 | put_be32(pb, 0); /* version & flags */ |
124 | put_be32(pb, track->entry); /* entry count */ | |
125 | for (i=0; i<track->entry; i++) { | |
126 | int cl = i / MOV_INDEX_CLUSTER_SIZE; | |
127 | int id = i % MOV_INDEX_CLUSTER_SIZE; | |
b29af723 MN |
128 | if(mode64 == 1) |
129 | put_be64(pb, track->cluster[cl][id].pos); | |
130 | else | |
131 | put_be32(pb, track->cluster[cl][id].pos); | |
1cb5f7fd | 132 | } |
f578f938 | 133 | return updateSize (pb, pos); |
1cb5f7fd MN |
134 | } |
135 | ||
e45ccf79 | 136 | /* Sample size atom */ |
6e6d6dc0 | 137 | static int mov_write_stsz_tag(ByteIOContext *pb, MOVTrack* track) |
1cb5f7fd | 138 | { |
f578f938 | 139 | int equalChunks = 1; |
e45ccf79 | 140 | int i, j, entries = 0, tst = -1, oldtst = -1; |
1cb5f7fd | 141 | |
b29af723 | 142 | offset_t pos = url_ftell(pb); |
f578f938 | 143 | put_be32(pb, 0); /* size */ |
1cb5f7fd MN |
144 | put_tag(pb, "stsz"); |
145 | put_be32(pb, 0); /* version & flags */ | |
146 | ||
f578f938 TR |
147 | for (i=0; i<track->entry; i++) { |
148 | int cl = i / MOV_INDEX_CLUSTER_SIZE; | |
149 | int id = i % MOV_INDEX_CLUSTER_SIZE; | |
e45ccf79 GB |
150 | tst = track->cluster[cl][id].size/track->cluster[cl][id].entries; |
151 | if(oldtst != -1 && tst != oldtst) { | |
152 | equalChunks = 0; | |
f578f938 TR |
153 | } |
154 | oldtst = tst; | |
e45ccf79 | 155 | entries += track->cluster[cl][id].entries; |
f578f938 | 156 | } |
e45ccf79 GB |
157 | if (equalChunks) { |
158 | int sSize = track->cluster[0][0].size/track->cluster[0][0].entries; | |
115329f1 | 159 | put_be32(pb, sSize); // sample size |
e45ccf79 | 160 | put_be32(pb, entries); // sample count |
1cb5f7fd | 161 | } |
f578f938 | 162 | else { |
115329f1 DB |
163 | put_be32(pb, 0); // sample size |
164 | put_be32(pb, entries); // sample count | |
f578f938 | 165 | for (i=0; i<track->entry; i++) { |
1cb5f7fd MN |
166 | int cl = i / MOV_INDEX_CLUSTER_SIZE; |
167 | int id = i % MOV_INDEX_CLUSTER_SIZE; | |
e45ccf79 GB |
168 | for ( j=0; j<track->cluster[cl][id].entries; j++) { |
169 | put_be32(pb, track->cluster[cl][id].size / | |
170 | track->cluster[cl][id].entries); | |
171 | } | |
1cb5f7fd MN |
172 | } |
173 | } | |
f578f938 | 174 | return updateSize (pb, pos); |
1cb5f7fd MN |
175 | } |
176 | ||
e45ccf79 | 177 | /* Sample to chunk atom */ |
6e6d6dc0 | 178 | static int mov_write_stsc_tag(ByteIOContext *pb, MOVTrack* track) |
1cb5f7fd | 179 | { |
b29af723 MN |
180 | int index = 0, oldval = -1, i; |
181 | offset_t entryPos, curpos; | |
f578f938 | 182 | |
b29af723 | 183 | offset_t pos = url_ftell(pb); |
f578f938 | 184 | put_be32(pb, 0); /* size */ |
1cb5f7fd | 185 | put_tag(pb, "stsc"); |
115329f1 | 186 | put_be32(pb, 0); // version & flags |
f578f938 | 187 | entryPos = url_ftell(pb); |
115329f1 | 188 | put_be32(pb, track->entry); // entry count |
f578f938 TR |
189 | for (i=0; i<track->entry; i++) { |
190 | int cl = i / MOV_INDEX_CLUSTER_SIZE; | |
191 | int id = i % MOV_INDEX_CLUSTER_SIZE; | |
e45ccf79 | 192 | if(oldval != track->cluster[cl][id].samplesInChunk) |
f578f938 | 193 | { |
115329f1 | 194 | put_be32(pb, i+1); // first chunk |
e45ccf79 | 195 | put_be32(pb, track->cluster[cl][id].samplesInChunk); // samples per chunk |
115329f1 | 196 | put_be32(pb, 0x1); // sample description index |
e45ccf79 | 197 | oldval = track->cluster[cl][id].samplesInChunk; |
f578f938 | 198 | index++; |
1cb5f7fd MN |
199 | } |
200 | } | |
f578f938 TR |
201 | curpos = url_ftell(pb); |
202 | url_fseek(pb, entryPos, SEEK_SET); | |
115329f1 | 203 | put_be32(pb, index); // rewrite size |
f578f938 | 204 | url_fseek(pb, curpos, SEEK_SET); |
1cb5f7fd | 205 | |
f578f938 | 206 | return updateSize (pb, pos); |
1cb5f7fd MN |
207 | } |
208 | ||
e45ccf79 | 209 | /* Sync sample atom */ |
f578f938 | 210 | static int mov_write_stss_tag(ByteIOContext *pb, MOVTrack* track) |
1cb5f7fd | 211 | { |
b29af723 MN |
212 | offset_t curpos, entryPos; |
213 | int i, index = 0; | |
214 | offset_t pos = url_ftell(pb); | |
115329f1 | 215 | put_be32(pb, 0); // size |
1cb5f7fd | 216 | put_tag(pb, "stss"); |
115329f1 | 217 | put_be32(pb, 0); // version & flags |
f578f938 | 218 | entryPos = url_ftell(pb); |
115329f1 | 219 | put_be32(pb, track->entry); // entry count |
f578f938 TR |
220 | for (i=0; i<track->entry; i++) { |
221 | int cl = i / MOV_INDEX_CLUSTER_SIZE; | |
222 | int id = i % MOV_INDEX_CLUSTER_SIZE; | |
223 | if(track->cluster[cl][id].key_frame == 1) { | |
224 | put_be32(pb, i+1); | |
225 | index++; | |
226 | } | |
227 | } | |
228 | curpos = url_ftell(pb); | |
229 | url_fseek(pb, entryPos, SEEK_SET); | |
115329f1 | 230 | put_be32(pb, index); // rewrite size |
f578f938 TR |
231 | url_fseek(pb, curpos, SEEK_SET); |
232 | return updateSize (pb, pos); | |
1cb5f7fd MN |
233 | } |
234 | ||
6e6d6dc0 | 235 | static int mov_write_damr_tag(ByteIOContext *pb) |
1cb5f7fd MN |
236 | { |
237 | put_be32(pb, 0x11); /* size */ | |
238 | put_tag(pb, "damr"); | |
239 | put_tag(pb, "FFMP"); | |
240 | put_byte(pb, 0); | |
f578f938 TR |
241 | |
242 | put_be16(pb, 0x80); /* Mode set (all modes for AMR_NB) */ | |
243 | put_be16(pb, 0xa); /* Mode change period (no restriction) */ | |
244 | //put_be16(pb, 0x81ff); /* Mode set (all modes for AMR_NB) */ | |
245 | //put_be16(pb, 1); /* Mode change period (no restriction) */ | |
1cb5f7fd MN |
246 | return 0x11; |
247 | } | |
248 | ||
69dde1ad GB |
249 | static int mov_write_wave_tag(ByteIOContext *pb, MOVTrack* track) |
250 | { | |
b29af723 | 251 | offset_t pos = url_ftell(pb); |
69dde1ad GB |
252 | |
253 | put_be32(pb, 0); /* size */ | |
254 | put_tag(pb, "wave"); | |
255 | ||
256 | put_be32(pb, 12); /* size */ | |
257 | put_tag(pb, "frma"); | |
258 | put_tag(pb, "mp4a"); | |
259 | ||
260 | put_be32(pb, 12); /* size */ | |
261 | put_tag(pb, "mp4a"); | |
262 | put_be32(pb, 0); | |
263 | ||
264 | mov_write_esds_tag(pb, track); | |
265 | ||
266 | put_be32(pb, 12); /* size */ | |
267 | put_tag(pb, "srcq"); | |
268 | put_be32(pb, 0x40); | |
269 | ||
270 | put_be32(pb, 8); /* size */ | |
271 | put_be32(pb, 0); /* null tag */ | |
272 | ||
273 | return updateSize (pb, pos); | |
274 | } | |
275 | ||
0c1e0bab | 276 | static const CodecTag codec_movaudio_tags[] = { |
8cc7a34d AB |
277 | { CODEC_ID_PCM_MULAW, MKTAG('u', 'l', 'a', 'w') }, |
278 | { CODEC_ID_PCM_ALAW, MKTAG('a', 'l', 'a', 'w') }, | |
279 | { CODEC_ID_ADPCM_IMA_QT, MKTAG('i', 'm', 'a', '4') }, | |
280 | { CODEC_ID_MACE3, MKTAG('M', 'A', 'C', '3') }, | |
281 | { CODEC_ID_MACE6, MKTAG('M', 'A', 'C', '6') }, | |
282 | { CODEC_ID_AAC, MKTAG('m', 'p', '4', 'a') }, | |
283 | { CODEC_ID_AMR_NB, MKTAG('s', 'a', 'm', 'r') }, | |
11bec294 | 284 | { CODEC_ID_AMR_WB, MKTAG('s', 'a', 'w', 'b') }, |
8cc7a34d AB |
285 | { CODEC_ID_PCM_S16BE, MKTAG('t', 'w', 'o', 's') }, |
286 | { CODEC_ID_PCM_S16LE, MKTAG('s', 'o', 'w', 't') }, | |
287 | { CODEC_ID_MP3, MKTAG('.', 'm', 'p', '3') }, | |
288 | { 0, 0 }, | |
289 | }; | |
290 | ||
f578f938 | 291 | static int mov_write_audio_tag(ByteIOContext *pb, MOVTrack* track) |
1cb5f7fd | 292 | { |
b29af723 | 293 | offset_t pos = url_ftell(pb); |
ffdd57d4 | 294 | int tag; |
115329f1 | 295 | |
1cb5f7fd | 296 | put_be32(pb, 0); /* size */ |
f578f938 | 297 | |
caacd4de RG |
298 | tag = track->enc->codec_tag; |
299 | if (!tag) | |
8cc7a34d AB |
300 | tag = codec_get_tag(codec_movaudio_tags, track->enc->codec_id); |
301 | // if no mac fcc found, try with Microsoft tags | |
302 | if (!tag) | |
303 | { | |
bb270c08 | 304 | int tmp = codec_get_tag(codec_wav_tags, track->enc->codec_id); |
ffdd57d4 | 305 | tag = MKTAG('m', 's', ((tmp >> 8) & 0xff), (tmp & 0xff)); |
8cc7a34d | 306 | } |
ffdd57d4 | 307 | put_le32(pb, tag); // store it byteswapped |
f578f938 | 308 | |
1cb5f7fd MN |
309 | put_be32(pb, 0); /* Reserved */ |
310 | put_be16(pb, 0); /* Reserved */ | |
311 | put_be16(pb, 1); /* Data-reference index, XXX == 1 */ | |
69dde1ad | 312 | |
e45ccf79 | 313 | /* SoundDescription */ |
69dde1ad GB |
314 | if(track->mode == MODE_MOV && track->enc->codec_id == CODEC_ID_AAC) |
315 | put_be16(pb, 1); /* Version 1 */ | |
316 | else | |
317 | put_be16(pb, 0); /* Version 0 */ | |
e45ccf79 | 318 | put_be16(pb, 0); /* Revision level */ |
1cb5f7fd MN |
319 | put_be32(pb, 0); /* Reserved */ |
320 | ||
f578f938 TR |
321 | put_be16(pb, track->enc->channels); /* Number of channels */ |
322 | /* TODO: Currently hard-coded to 16-bit, there doesn't seem | |
e45ccf79 | 323 | to be a good way to get number of bits of audio */ |
1cb5f7fd | 324 | put_be16(pb, 0x10); /* Reserved */ |
ef19c7eb GB |
325 | |
326 | if(track->enc->codec_id == CODEC_ID_AAC || | |
327 | track->enc->codec_id == CODEC_ID_MP3) | |
328 | { | |
329 | put_be16(pb, 0xfffe); /* compression ID (vbr)*/ | |
330 | } | |
331 | else | |
332 | { | |
333 | put_be16(pb, 0); /* compression ID (= 0) */ | |
334 | } | |
9a4d9388 | 335 | put_be16(pb, 0); /* packet size (= 0) */ |
1cb5f7fd MN |
336 | put_be16(pb, track->timescale); /* Time scale */ |
337 | put_be16(pb, 0); /* Reserved */ | |
338 | ||
69dde1ad GB |
339 | if(track->mode == MODE_MOV && track->enc->codec_id == CODEC_ID_AAC) |
340 | { | |
341 | /* SoundDescription V1 extended info */ | |
342 | put_be32(pb, track->enc->frame_size); /* Samples per packet */ | |
343 | put_be32(pb, 1536); /* Bytes per packet */ | |
344 | put_be32(pb, 2); /* Bytes per frame */ | |
345 | put_be32(pb, 2); /* Bytes per sample */ | |
346 | } | |
347 | ||
348 | if(track->enc->codec_id == CODEC_ID_AAC) { | |
349 | if( track->mode == MODE_MOV ) mov_write_wave_tag(pb, track); | |
350 | else mov_write_esds_tag(pb, track); | |
351 | } | |
f578f938 TR |
352 | if(track->enc->codec_id == CODEC_ID_AMR_NB) |
353 | mov_write_damr_tag(pb); | |
6e6d6dc0 | 354 | return updateSize (pb, pos); |
1cb5f7fd MN |
355 | } |
356 | ||
6e6d6dc0 | 357 | static int mov_write_d263_tag(ByteIOContext *pb) |
1cb5f7fd MN |
358 | { |
359 | put_be32(pb, 0xf); /* size */ | |
360 | put_tag(pb, "d263"); | |
361 | put_tag(pb, "FFMP"); | |
362 | put_be16(pb, 0x0a); | |
363 | put_byte(pb, 0); | |
364 | return 0xf; | |
365 | } | |
366 | ||
f578f938 TR |
367 | /* TODO: No idea about these values */ |
368 | static int mov_write_svq3_tag(ByteIOContext *pb) | |
1cb5f7fd | 369 | { |
f578f938 TR |
370 | put_be32(pb, 0x15); |
371 | put_tag(pb, "SMI "); | |
372 | put_tag(pb, "SEQH"); | |
373 | put_be32(pb, 0x5); | |
374 | put_be32(pb, 0xe2c0211d); | |
375 | put_be32(pb, 0xc0000000); | |
115329f1 | 376 | put_byte(pb, 0); |
f578f938 | 377 | return 0x15; |
1cb5f7fd MN |
378 | } |
379 | ||
e45ccf79 GB |
380 | static unsigned int descrLength(unsigned int len) |
381 | { | |
382 | if (len < 0x00000080) | |
383 | return 2 + len; | |
384 | else if (len < 0x00004000) | |
385 | return 3 + len; | |
386 | else if(len < 0x00200000) | |
387 | return 4 + len; | |
388 | else | |
389 | return 5 + len; | |
390 | } | |
391 | ||
392 | static void putDescr(ByteIOContext *pb, int tag, int size) | |
1cb5f7fd | 393 | { |
e45ccf79 GB |
394 | uint32_t len; |
395 | uint8_t vals[4]; | |
396 | ||
397 | len = size; | |
398 | vals[3] = (uint8_t)(len & 0x7f); | |
399 | len >>= 7; | |
115329f1 | 400 | vals[2] = (uint8_t)((len & 0x7f) | 0x80); |
e45ccf79 | 401 | len >>= 7; |
115329f1 | 402 | vals[1] = (uint8_t)((len & 0x7f) | 0x80); |
e45ccf79 GB |
403 | len >>= 7; |
404 | vals[0] = (uint8_t)((len & 0x7f) | 0x80); | |
405 | ||
406 | put_byte(pb, tag); // DescriptorTag | |
407 | ||
408 | if (size < 0x00000080) | |
409 | { | |
410 | put_byte(pb, vals[3]); | |
411 | } | |
412 | else if (size < 0x00004000) | |
413 | { | |
414 | put_byte(pb, vals[2]); | |
415 | put_byte(pb, vals[3]); | |
416 | } | |
417 | else if (size < 0x00200000) | |
418 | { | |
419 | put_byte(pb, vals[1]); | |
420 | put_byte(pb, vals[2]); | |
421 | put_byte(pb, vals[3]); | |
422 | } | |
423 | else if (size < 0x10000000) | |
424 | { | |
425 | put_byte(pb, vals[0]); | |
426 | put_byte(pb, vals[1]); | |
427 | put_byte(pb, vals[2]); | |
428 | put_byte(pb, vals[3]); | |
429 | } | |
1cb5f7fd MN |
430 | } |
431 | ||
6e6d6dc0 | 432 | static int mov_write_esds_tag(ByteIOContext *pb, MOVTrack* track) // Basic |
1cb5f7fd | 433 | { |
46103f6b | 434 | int decoderSpecificInfoLen; |
b29af723 | 435 | offset_t pos = url_ftell(pb); |
8af18154 | 436 | void *vosDataBackup=track->vosData; |
437 | int vosLenBackup=track->vosLen; | |
115329f1 | 438 | |
8af18154 | 439 | // we should be able to have these passed in, via vosData, then we wouldn't need to attack this routine at all |
440 | static const char PSPAACData[]={0x13,0x10}; | |
441 | static const char PSPMP4Data[]={0x00,0x00,0x01,0xB0,0x03,0x00,0x00,0x01,0xB5,0x09,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x20,0x00,0x84,0x5D,0x4C,0x28,0x50,0x20,0xF0,0xA3,0x1F }; | |
115329f1 DB |
442 | |
443 | ||
8af18154 | 444 | if (track->mode == MODE_PSP) // fails on psp if this is not here |
445 | { | |
446 | if (track->enc->codec_id == CODEC_ID_AAC) | |
447 | { | |
448 | track->vosLen = 2; | |
79396ac6 | 449 | track->vosData = (uint8_t *) PSPAACData; |
8af18154 | 450 | } |
451 | ||
452 | if (track->enc->codec_id == CODEC_ID_MPEG4) | |
453 | { | |
454 | track->vosLen = 28; | |
79396ac6 | 455 | track->vosData = (uint8_t *) PSPMP4Data; |
8af18154 | 456 | } |
457 | } | |
e45ccf79 | 458 | |
46103f6b FR |
459 | decoderSpecificInfoLen = track->vosLen ? descrLength(track->vosLen):0; |
460 | ||
e45ccf79 | 461 | put_be32(pb, 0); // size |
1cb5f7fd | 462 | put_tag(pb, "esds"); |
e45ccf79 | 463 | put_be32(pb, 0); // Version |
1cb5f7fd | 464 | |
e45ccf79 GB |
465 | // ES descriptor |
466 | putDescr(pb, 0x03, 3 + descrLength(13 + decoderSpecificInfoLen) + | |
467 | descrLength(1)); | |
6c13c880 | 468 | put_be16(pb, track->trackID); |
1cb5f7fd MN |
469 | put_byte(pb, 0x00); // flags (= no flags) |
470 | ||
e45ccf79 GB |
471 | // DecoderConfig descriptor |
472 | putDescr(pb, 0x04, 13 + decoderSpecificInfoLen); | |
473 | ||
6c13c880 MN |
474 | // Object type indication |
475 | put_byte(pb, codec_get_tag(ff_mov_obj_type, track->enc->codec_id)); | |
e45ccf79 | 476 | |
5cad192d NS |
477 | // the following fields is made of 6 bits to identify the streamtype (4 for video, 5 for audio) |
478 | // plus 1 bit to indicate upstream and 1 bit set to 1 (reserved) | |
e45ccf79 GB |
479 | if(track->enc->codec_type == CODEC_TYPE_AUDIO) |
480 | put_byte(pb, 0x15); // flags (= Audiostream) | |
481 | else | |
482 | put_byte(pb, 0x11); // flags (= Visualstream) | |
483 | ||
6c13c880 MN |
484 | put_byte(pb, track->enc->rc_buffer_size>>(3+16)); // Buffersize DB (24 bits) |
485 | put_be16(pb, (track->enc->rc_buffer_size>>3)&0xFFFF); // Buffersize DB | |
1cb5f7fd | 486 | |
6c13c880 MN |
487 | put_be32(pb, FFMAX(track->enc->bit_rate, track->enc->rc_max_rate)); // maxbitrate (FIXME should be max rate in any 1 sec window) |
488 | if(track->enc->rc_max_rate != track->enc->rc_min_rate || track->enc->rc_min_rate==0) | |
489 | put_be32(pb, 0); // vbr | |
490 | else | |
491 | put_be32(pb, track->enc->rc_max_rate); // avg bitrate | |
1cb5f7fd | 492 | |
e45ccf79 GB |
493 | if (track->vosLen) |
494 | { | |
495 | // DecoderSpecific info descriptor | |
496 | putDescr(pb, 0x05, track->vosLen); | |
497 | put_buffer(pb, track->vosData, track->vosLen); | |
498 | } | |
499 | ||
8af18154 | 500 | track->vosData = vosDataBackup; |
501 | track->vosLen = vosLenBackup; | |
502 | ||
e45ccf79 | 503 | // SL descriptor |
4bfc029f | 504 | putDescr(pb, 0x06, 1); |
1cb5f7fd | 505 | put_byte(pb, 0x02); |
e45ccf79 | 506 | return updateSize (pb, pos); |
1cb5f7fd MN |
507 | } |
508 | ||
0c1e0bab | 509 | static const CodecTag codec_movvideo_tags[] = { |
8cc7a34d AB |
510 | { CODEC_ID_SVQ1, MKTAG('S', 'V', 'Q', '1') }, |
511 | { CODEC_ID_SVQ3, MKTAG('S', 'V', 'Q', '3') }, | |
512 | { CODEC_ID_MPEG4, MKTAG('m', 'p', '4', 'v') }, | |
513 | { CODEC_ID_H263, MKTAG('s', '2', '6', '3') }, | |
0c1e0bab | 514 | { CODEC_ID_H264, MKTAG('a', 'v', 'c', '1') }, |
8cc7a34d AB |
515 | { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'c', ' ') }, |
516 | { 0, 0 }, | |
517 | }; | |
518 | ||
f578f938 | 519 | static int mov_write_video_tag(ByteIOContext *pb, MOVTrack* track) |
1cb5f7fd | 520 | { |
b29af723 | 521 | offset_t pos = url_ftell(pb); |
53ffdd14 | 522 | char compressor_name[32]; |
ffdd57d4 | 523 | int tag; |
8cc7a34d | 524 | |
f578f938 | 525 | put_be32(pb, 0); /* size */ |
8cc7a34d | 526 | |
caacd4de RG |
527 | tag = track->enc->codec_tag; |
528 | if (!tag) | |
8cc7a34d AB |
529 | tag = codec_get_tag(codec_movvideo_tags, track->enc->codec_id); |
530 | // if no mac fcc found, try with Microsoft tags | |
531 | if (!tag) | |
bb270c08 | 532 | tag = codec_get_tag(codec_bmp_tags, track->enc->codec_id); |
ffdd57d4 | 533 | put_le32(pb, tag); // store it byteswapped |
6e6d6dc0 | 534 | |
f578f938 TR |
535 | put_be32(pb, 0); /* Reserved */ |
536 | put_be16(pb, 0); /* Reserved */ | |
537 | put_be16(pb, 1); /* Data-reference index */ | |
538 | ||
53ffdd14 RG |
539 | put_be16(pb, 0); /* Codec stream version */ |
540 | put_be16(pb, 0); /* Codec stream revision (=0) */ | |
541 | put_tag(pb, "FFMP"); /* Vendor */ | |
542 | if(track->enc->codec_id == CODEC_ID_RAWVIDEO) { | |
543 | put_be32(pb, 0); /* Temporal Quality */ | |
544 | put_be32(pb, 0x400); /* Spatial Quality = lossless*/ | |
545 | } else { | |
546 | put_be32(pb, 0x200); /* Temporal Quality = normal */ | |
547 | put_be32(pb, 0x200); /* Spatial Quality = normal */ | |
548 | } | |
f578f938 TR |
549 | put_be16(pb, track->enc->width); /* Video width */ |
550 | put_be16(pb, track->enc->height); /* Video height */ | |
53ffdd14 RG |
551 | put_be32(pb, 0x00480000); /* Horizontal resolution 72dpi */ |
552 | put_be32(pb, 0x00480000); /* Vertical resolution 72dpi */ | |
9a4d9388 RS |
553 | put_be32(pb, 0); /* Data size (= 0) */ |
554 | put_be16(pb, 1); /* Frame count (= 1) */ | |
115329f1 | 555 | |
53ffdd14 | 556 | memset(compressor_name,0,32); |
25cf9062 | 557 | if (track->enc->codec && track->enc->codec->name) |
53ffdd14 | 558 | strncpy(compressor_name,track->enc->codec->name,31); |
25cf9062 | 559 | put_byte(pb, strlen(compressor_name)); |
53ffdd14 | 560 | put_buffer(pb, compressor_name, 31); |
115329f1 | 561 | |
f578f938 TR |
562 | put_be16(pb, 0x18); /* Reserved */ |
563 | put_be16(pb, 0xffff); /* Reserved */ | |
564 | if(track->enc->codec_id == CODEC_ID_MPEG4) | |
565 | mov_write_esds_tag(pb, track); | |
566 | else if(track->enc->codec_id == CODEC_ID_H263) | |
567 | mov_write_d263_tag(pb); | |
568 | else if(track->enc->codec_id == CODEC_ID_SVQ3) | |
115329f1 | 569 | mov_write_svq3_tag(pb); |
f578f938 TR |
570 | |
571 | return updateSize (pb, pos); | |
1cb5f7fd MN |
572 | } |
573 | ||
6e6d6dc0 | 574 | static int mov_write_stsd_tag(ByteIOContext *pb, MOVTrack* track) |
1cb5f7fd | 575 | { |
b29af723 | 576 | offset_t pos = url_ftell(pb); |
1cb5f7fd MN |
577 | put_be32(pb, 0); /* size */ |
578 | put_tag(pb, "stsd"); | |
579 | put_be32(pb, 0); /* version & flags */ | |
580 | put_be32(pb, 1); /* entry count */ | |
f578f938 TR |
581 | if (track->enc->codec_type == CODEC_TYPE_VIDEO) |
582 | mov_write_video_tag(pb, track); | |
583 | else if (track->enc->codec_type == CODEC_TYPE_AUDIO) | |
584 | mov_write_audio_tag(pb, track); | |
6e6d6dc0 | 585 | return updateSize(pb, pos); |
1cb5f7fd MN |
586 | } |
587 | ||
0078e9b9 | 588 | /* TODO: */ |
e45ccf79 | 589 | /* Time to sample atom */ |
6e6d6dc0 | 590 | static int mov_write_stts_tag(ByteIOContext *pb, MOVTrack* track) |
1cb5f7fd MN |
591 | { |
592 | put_be32(pb, 0x18); /* size */ | |
593 | put_tag(pb, "stts"); | |
594 | put_be32(pb, 0); /* version & flags */ | |
595 | put_be32(pb, 1); /* entry count */ | |
596 | ||
e45ccf79 GB |
597 | put_be32(pb, track->sampleCount); /* sample count */ |
598 | put_be32(pb, track->sampleDuration); /* sample duration */ | |
1cb5f7fd MN |
599 | return 0x18; |
600 | } | |
601 | ||
6e6d6dc0 | 602 | static int mov_write_dref_tag(ByteIOContext *pb) |
1cb5f7fd MN |
603 | { |
604 | put_be32(pb, 28); /* size */ | |
605 | put_tag(pb, "dref"); | |
606 | put_be32(pb, 0); /* version & flags */ | |
607 | put_be32(pb, 1); /* entry count */ | |
608 | ||
609 | put_be32(pb, 0xc); /* size */ | |
610 | put_tag(pb, "url "); | |
611 | put_be32(pb, 1); /* version & flags */ | |
612 | ||
613 | return 28; | |
614 | } | |
615 | ||
6e6d6dc0 | 616 | static int mov_write_stbl_tag(ByteIOContext *pb, MOVTrack* track) |
1cb5f7fd | 617 | { |
b29af723 | 618 | offset_t pos = url_ftell(pb); |
1cb5f7fd MN |
619 | put_be32(pb, 0); /* size */ |
620 | put_tag(pb, "stbl"); | |
6e6d6dc0 MN |
621 | mov_write_stsd_tag(pb, track); |
622 | mov_write_stts_tag(pb, track); | |
f578f938 TR |
623 | if (track->enc->codec_type == CODEC_TYPE_VIDEO && |
624 | track->hasKeyframes) | |
625 | mov_write_stss_tag(pb, track); | |
6e6d6dc0 MN |
626 | mov_write_stsc_tag(pb, track); |
627 | mov_write_stsz_tag(pb, track); | |
628 | mov_write_stco_tag(pb, track); | |
629 | return updateSize(pb, pos); | |
1cb5f7fd MN |
630 | } |
631 | ||
6e6d6dc0 | 632 | static int mov_write_dinf_tag(ByteIOContext *pb) |
1cb5f7fd | 633 | { |
b29af723 | 634 | offset_t pos = url_ftell(pb); |
1cb5f7fd MN |
635 | put_be32(pb, 0); /* size */ |
636 | put_tag(pb, "dinf"); | |
6e6d6dc0 MN |
637 | mov_write_dref_tag(pb); |
638 | return updateSize(pb, pos); | |
1cb5f7fd MN |
639 | } |
640 | ||
6e6d6dc0 | 641 | static int mov_write_smhd_tag(ByteIOContext *pb) |
1cb5f7fd MN |
642 | { |
643 | put_be32(pb, 16); /* size */ | |
644 | put_tag(pb, "smhd"); | |
645 | put_be32(pb, 0); /* version & flags */ | |
646 | put_be16(pb, 0); /* reserved (balance, normally = 0) */ | |
647 | put_be16(pb, 0); /* reserved */ | |
648 | return 16; | |
649 | } | |
650 | ||
6e6d6dc0 | 651 | static int mov_write_vmhd_tag(ByteIOContext *pb) |
1cb5f7fd MN |
652 | { |
653 | put_be32(pb, 0x14); /* size (always 0x14) */ | |
654 | put_tag(pb, "vmhd"); | |
655 | put_be32(pb, 0x01); /* version & flags */ | |
656 | put_be64(pb, 0); /* reserved (graphics mode = copy) */ | |
657 | return 0x14; | |
658 | } | |
659 | ||
6e6d6dc0 | 660 | static int mov_write_hdlr_tag(ByteIOContext *pb, MOVTrack* track) |
1cb5f7fd | 661 | { |
9a4d9388 | 662 | char *descr, *hdlr, *hdlr_type; |
b29af723 | 663 | offset_t pos = url_ftell(pb); |
115329f1 | 664 | |
9a4d9388 | 665 | if (!track) { /* no media --> data handler */ |
bb270c08 DB |
666 | hdlr = "dhlr"; |
667 | hdlr_type = "url "; | |
668 | descr = "DataHandler"; | |
9a4d9388 | 669 | } else { |
bb270c08 DB |
670 | hdlr = (track->mode == MODE_MOV) ? "mhlr" : "\0\0\0\0"; |
671 | if (track->enc->codec_type == CODEC_TYPE_VIDEO) { | |
672 | hdlr_type = "vide"; | |
673 | descr = "VideoHandler"; | |
674 | } else { | |
675 | hdlr_type = "soun"; | |
676 | descr = "SoundHandler"; | |
677 | } | |
9a4d9388 | 678 | } |
115329f1 | 679 | |
f578f938 | 680 | put_be32(pb, 0); /* size */ |
1cb5f7fd MN |
681 | put_tag(pb, "hdlr"); |
682 | put_be32(pb, 0); /* Version & flags */ | |
906b578f | 683 | put_buffer(pb, hdlr, 4); /* handler */ |
9a4d9388 | 684 | put_tag(pb, hdlr_type); /* handler type */ |
f578f938 TR |
685 | put_be32(pb ,0); /* reserved */ |
686 | put_be32(pb ,0); /* reserved */ | |
687 | put_be32(pb ,0); /* reserved */ | |
9a4d9388 RS |
688 | put_byte(pb, strlen(descr)); /* string counter */ |
689 | put_buffer(pb, descr, strlen(descr)); /* handler description */ | |
690 | return updateSize(pb, pos); | |
691 | } | |
692 | ||
693 | static int mov_write_minf_tag(ByteIOContext *pb, MOVTrack* track) | |
694 | { | |
b29af723 | 695 | offset_t pos = url_ftell(pb); |
9a4d9388 RS |
696 | put_be32(pb, 0); /* size */ |
697 | put_tag(pb, "minf"); | |
1cb5f7fd | 698 | if(track->enc->codec_type == CODEC_TYPE_VIDEO) |
9a4d9388 | 699 | mov_write_vmhd_tag(pb); |
1cb5f7fd | 700 | else |
9a4d9388 RS |
701 | mov_write_smhd_tag(pb); |
702 | if (track->mode == MODE_MOV) /* FIXME: Why do it for MODE_MOV only ? */ | |
703 | mov_write_hdlr_tag(pb, NULL); | |
704 | mov_write_dinf_tag(pb); | |
705 | mov_write_stbl_tag(pb, track); | |
f578f938 | 706 | return updateSize(pb, pos); |
1cb5f7fd MN |
707 | } |
708 | ||
6e6d6dc0 | 709 | static int mov_write_mdhd_tag(ByteIOContext *pb, MOVTrack* track) |
1cb5f7fd MN |
710 | { |
711 | put_be32(pb, 32); /* size */ | |
712 | put_tag(pb, "mdhd"); | |
713 | put_be32(pb, 0); /* Version & flags */ | |
714 | put_be32(pb, track->time); /* creation time */ | |
715 | put_be32(pb, track->time); /* modification time */ | |
115329f1 | 716 | put_be32(pb, track->timescale); /* time scale (sample rate for audio) */ |
e45ccf79 | 717 | put_be32(pb, track->trackDuration); /* duration */ |
ab561df9 | 718 | put_be16(pb, track->language); /* language */ |
1cb5f7fd MN |
719 | put_be16(pb, 0); /* reserved (quality) */ |
720 | return 32; | |
721 | } | |
722 | ||
6e6d6dc0 | 723 | static int mov_write_mdia_tag(ByteIOContext *pb, MOVTrack* track) |
1cb5f7fd | 724 | { |
b29af723 | 725 | offset_t pos = url_ftell(pb); |
1cb5f7fd MN |
726 | put_be32(pb, 0); /* size */ |
727 | put_tag(pb, "mdia"); | |
6e6d6dc0 MN |
728 | mov_write_mdhd_tag(pb, track); |
729 | mov_write_hdlr_tag(pb, track); | |
730 | mov_write_minf_tag(pb, track); | |
731 | return updateSize(pb, pos); | |
1cb5f7fd MN |
732 | } |
733 | ||
6e6d6dc0 | 734 | static int mov_write_tkhd_tag(ByteIOContext *pb, MOVTrack* track) |
1cb5f7fd MN |
735 | { |
736 | put_be32(pb, 0x5c); /* size (always 0x5c) */ | |
737 | put_tag(pb, "tkhd"); | |
f578f938 | 738 | put_be32(pb, 0xf); /* version & flags (track enabled) */ |
1cb5f7fd MN |
739 | put_be32(pb, track->time); /* creation time */ |
740 | put_be32(pb, track->time); /* modification time */ | |
741 | put_be32(pb, track->trackID); /* track-id */ | |
742 | put_be32(pb, 0); /* reserved */ | |
83fa2ef8 | 743 | put_be32(pb, av_rescale_rnd(track->trackDuration, globalTimescale, track->timescale, AV_ROUND_UP)); /* duration */ |
1cb5f7fd MN |
744 | |
745 | put_be32(pb, 0); /* reserved */ | |
746 | put_be32(pb, 0); /* reserved */ | |
747 | put_be32(pb, 0x0); /* reserved (Layer & Alternate group) */ | |
748 | /* Volume, only for audio */ | |
749 | if(track->enc->codec_type == CODEC_TYPE_AUDIO) | |
750 | put_be16(pb, 0x0100); | |
751 | else | |
752 | put_be16(pb, 0); | |
753 | put_be16(pb, 0); /* reserved */ | |
754 | ||
755 | /* Matrix structure */ | |
756 | put_be32(pb, 0x00010000); /* reserved */ | |
757 | put_be32(pb, 0x0); /* reserved */ | |
758 | put_be32(pb, 0x0); /* reserved */ | |
759 | put_be32(pb, 0x0); /* reserved */ | |
760 | put_be32(pb, 0x00010000); /* reserved */ | |
761 | put_be32(pb, 0x0); /* reserved */ | |
762 | put_be32(pb, 0x0); /* reserved */ | |
763 | put_be32(pb, 0x0); /* reserved */ | |
764 | put_be32(pb, 0x40000000); /* reserved */ | |
765 | ||
766 | /* Track width and height, for visual only */ | |
767 | if(track->enc->codec_type == CODEC_TYPE_VIDEO) { | |
69dde1ad GB |
768 | double sample_aspect_ratio = av_q2d(track->enc->sample_aspect_ratio); |
769 | if( !sample_aspect_ratio ) sample_aspect_ratio = 1; | |
770 | put_be32(pb, sample_aspect_ratio * track->enc->width*0x10000); | |
f578f938 | 771 | put_be32(pb, track->enc->height*0x10000); |
1cb5f7fd MN |
772 | } |
773 | else { | |
774 | put_be32(pb, 0); | |
775 | put_be32(pb, 0); | |
776 | } | |
777 | return 0x5c; | |
778 | } | |
779 | ||
8af18154 | 780 | // This box seems important for the psp playback ... without it the movie seems to hang |
781 | static int mov_write_edts_tag(ByteIOContext *pb, MOVTrack *track) | |
782 | { | |
8af18154 | 783 | put_be32(pb, 0x24); /* size */ |
784 | put_tag(pb, "edts"); | |
785 | put_be32(pb, 0x1c); /* size */ | |
786 | put_tag(pb, "elst"); | |
787 | put_be32(pb, 0x0); | |
788 | put_be32(pb, 0x1); | |
789 | ||
83fa2ef8 | 790 | put_be32(pb, av_rescale_rnd(track->trackDuration, globalTimescale, track->timescale, AV_ROUND_UP)); /* duration ... doesn't seem to effect psp */ |
8af18154 | 791 | |
792 | put_be32(pb, 0x0); | |
793 | put_be32(pb, 0x00010000); | |
794 | return 0x24; | |
795 | } | |
796 | ||
797 | // goes at the end of each track! ... Critical for PSP playback ("Incompatible data" without it) | |
798 | static int mov_write_uuid_tag_psp(ByteIOContext *pb, MOVTrack *mov) | |
799 | { | |
800 | put_be32(pb, 0x34); /* size ... reports as 28 in mp4box! */ | |
801 | put_tag(pb, "uuid"); | |
802 | put_tag(pb, "USMT"); | |
803 | put_be32(pb, 0x21d24fce); | |
804 | put_be32(pb, 0xbb88695c); | |
805 | put_be32(pb, 0xfac9c740); | |
806 | put_be32(pb, 0x1c); // another size here! | |
807 | put_tag(pb, "MTDT"); | |
808 | put_be32(pb, 0x00010012); | |
809 | put_be32(pb, 0x0a); | |
810 | put_be32(pb, 0x55c40000); | |
811 | put_be32(pb, 0x1); | |
812 | put_be32(pb, 0x0); | |
813 | return 0x34; | |
814 | } | |
815 | ||
6e6d6dc0 | 816 | static int mov_write_trak_tag(ByteIOContext *pb, MOVTrack* track) |
1cb5f7fd | 817 | { |
b29af723 | 818 | offset_t pos = url_ftell(pb); |
1cb5f7fd MN |
819 | put_be32(pb, 0); /* size */ |
820 | put_tag(pb, "trak"); | |
6e6d6dc0 | 821 | mov_write_tkhd_tag(pb, track); |
115329f1 | 822 | if (track->mode == MODE_PSP) |
8af18154 | 823 | mov_write_edts_tag(pb, track); // PSP Movies require edts box |
6e6d6dc0 | 824 | mov_write_mdia_tag(pb, track); |
115329f1 | 825 | if (track->mode == MODE_PSP) |
8af18154 | 826 | mov_write_uuid_tag_psp(pb,track); // PSP Movies require this uuid box |
6e6d6dc0 | 827 | return updateSize(pb, pos); |
1cb5f7fd MN |
828 | } |
829 | ||
88730be6 | 830 | #if 0 |
1cb5f7fd | 831 | /* TODO: Not sorted out, but not necessary either */ |
6e6d6dc0 | 832 | static int mov_write_iods_tag(ByteIOContext *pb, MOVContext *mov) |
1cb5f7fd MN |
833 | { |
834 | put_be32(pb, 0x15); /* size */ | |
835 | put_tag(pb, "iods"); | |
836 | put_be32(pb, 0); /* version & flags */ | |
837 | put_be16(pb, 0x1007); | |
838 | put_byte(pb, 0); | |
839 | put_be16(pb, 0x4fff); | |
840 | put_be16(pb, 0xfffe); | |
841 | put_be16(pb, 0x01ff); | |
842 | return 0x15; | |
843 | } | |
88730be6 | 844 | #endif |
1cb5f7fd | 845 | |
6e6d6dc0 | 846 | static int mov_write_mvhd_tag(ByteIOContext *pb, MOVContext *mov) |
1cb5f7fd | 847 | { |
b29af723 MN |
848 | int maxTrackID = 1, i; |
849 | int64_t maxTrackLenTemp, maxTrackLen = 0; | |
1cb5f7fd MN |
850 | |
851 | put_be32(pb, 0x6c); /* size (always 0x6c) */ | |
852 | put_tag(pb, "mvhd"); | |
853 | put_be32(pb, 0); /* version & flags */ | |
854 | put_be32(pb, mov->time); /* creation time */ | |
855 | put_be32(pb, mov->time); /* modification time */ | |
856 | put_be32(pb, mov->timescale); /* timescale */ | |
857 | for (i=0; i<MAX_STREAMS; i++) { | |
858 | if(mov->tracks[i].entry > 0) { | |
83fa2ef8 | 859 | maxTrackLenTemp = av_rescale_rnd(mov->tracks[i].trackDuration, globalTimescale, mov->tracks[i].timescale, AV_ROUND_UP); |
f578f938 TR |
860 | if(maxTrackLen < maxTrackLenTemp) |
861 | maxTrackLen = maxTrackLenTemp; | |
1cb5f7fd MN |
862 | if(maxTrackID < mov->tracks[i].trackID) |
863 | maxTrackID = mov->tracks[i].trackID; | |
864 | } | |
865 | } | |
866 | put_be32(pb, maxTrackLen); /* duration of longest track */ | |
867 | ||
868 | put_be32(pb, 0x00010000); /* reserved (preferred rate) 1.0 = normal */ | |
869 | put_be16(pb, 0x0100); /* reserved (preferred volume) 1.0 = normal */ | |
870 | put_be16(pb, 0); /* reserved */ | |
871 | put_be32(pb, 0); /* reserved */ | |
872 | put_be32(pb, 0); /* reserved */ | |
873 | ||
874 | /* Matrix structure */ | |
875 | put_be32(pb, 0x00010000); /* reserved */ | |
876 | put_be32(pb, 0x0); /* reserved */ | |
877 | put_be32(pb, 0x0); /* reserved */ | |
878 | put_be32(pb, 0x0); /* reserved */ | |
879 | put_be32(pb, 0x00010000); /* reserved */ | |
880 | put_be32(pb, 0x0); /* reserved */ | |
881 | put_be32(pb, 0x0); /* reserved */ | |
882 | put_be32(pb, 0x0); /* reserved */ | |
883 | put_be32(pb, 0x40000000); /* reserved */ | |
884 | ||
885 | put_be32(pb, 0); /* reserved (preview time) */ | |
886 | put_be32(pb, 0); /* reserved (preview duration) */ | |
887 | put_be32(pb, 0); /* reserved (poster time) */ | |
888 | put_be32(pb, 0); /* reserved (selection time) */ | |
889 | put_be32(pb, 0); /* reserved (selection duration) */ | |
890 | put_be32(pb, 0); /* reserved (current time) */ | |
891 | put_be32(pb, maxTrackID+1); /* Next track id */ | |
892 | return 0x6c; | |
893 | } | |
894 | ||
b6c50eb1 PB |
895 | static int mov_write_itunes_hdlr_tag(ByteIOContext *pb, MOVContext* mov, |
896 | AVFormatContext *s) | |
897 | { | |
b29af723 | 898 | offset_t pos = url_ftell(pb); |
b6c50eb1 PB |
899 | put_be32(pb, 0); /* size */ |
900 | put_tag(pb, "hdlr"); | |
901 | put_be32(pb, 0); | |
902 | put_be32(pb, 0); | |
903 | put_tag(pb, "mdir"); | |
904 | put_tag(pb, "appl"); | |
905 | put_be32(pb, 0); | |
906 | put_be32(pb, 0); | |
907 | put_be16(pb, 0); | |
908 | return updateSize(pb, pos); | |
909 | } | |
910 | ||
911 | /* helper function to write a data tag with the specified string as data */ | |
912 | static int mov_write_string_data_tag(ByteIOContext *pb, MOVContext* mov, | |
913 | AVFormatContext *s, const char *data) | |
914 | { | |
b29af723 | 915 | offset_t pos = url_ftell(pb); |
b6c50eb1 PB |
916 | put_be32(pb, 0); /* size */ |
917 | put_tag(pb, "data"); | |
918 | put_be32(pb, 1); | |
919 | put_be32(pb, 0); | |
920 | put_buffer(pb, data, strlen(data)); | |
921 | return updateSize(pb, pos); | |
922 | } | |
923 | ||
924 | /* iTunes name of the song/movie */ | |
925 | static int mov_write_nam_tag(ByteIOContext *pb, MOVContext* mov, | |
926 | AVFormatContext *s) | |
927 | { | |
928 | int size = 0; | |
929 | if ( s->title[0] ) { | |
b29af723 | 930 | offset_t pos = url_ftell(pb); |
b6c50eb1 PB |
931 | put_be32(pb, 0); /* size */ |
932 | put_tag(pb, "\251nam"); | |
933 | mov_write_string_data_tag(pb, mov, s, s->title); | |
934 | size = updateSize(pb, pos); | |
935 | } | |
936 | return size; | |
937 | } | |
938 | ||
939 | /* iTunes name of the artist/performer */ | |
940 | static int mov_write_ART_tag(ByteIOContext *pb, MOVContext* mov, | |
941 | AVFormatContext *s) | |
942 | { | |
943 | int size = 0; | |
944 | if ( s->author[0] ) { | |
b29af723 | 945 | offset_t pos = url_ftell(pb); |
b6c50eb1 PB |
946 | put_be32(pb, 0); /* size */ |
947 | put_tag(pb, "\251ART"); | |
948 | // we use the author here as this is the only thing that we have... | |
949 | mov_write_string_data_tag(pb, mov, s, s->author); | |
950 | size = updateSize(pb, pos); | |
951 | } | |
952 | return size; | |
953 | } | |
954 | ||
955 | /* iTunes name of the writer */ | |
956 | static int mov_write_wrt_tag(ByteIOContext *pb, MOVContext* mov, | |
957 | AVFormatContext *s) | |
958 | { | |
959 | int size = 0; | |
960 | if ( s->author[0] ) { | |
b29af723 | 961 | offset_t pos = url_ftell(pb); |
b6c50eb1 PB |
962 | put_be32(pb, 0); /* size */ |
963 | put_tag(pb, "\251wrt"); | |
964 | mov_write_string_data_tag(pb, mov, s, s->author); | |
965 | size = updateSize(pb, pos); | |
966 | } | |
967 | return size; | |
968 | } | |
969 | ||
970 | /* iTunes name of the album */ | |
971 | static int mov_write_alb_tag(ByteIOContext *pb, MOVContext* mov, | |
972 | AVFormatContext *s) | |
973 | { | |
974 | int size = 0; | |
975 | if ( s->album[0] ) { | |
b29af723 | 976 | offset_t pos = url_ftell(pb); |
b6c50eb1 PB |
977 | put_be32(pb, 0); /* size */ |
978 | put_tag(pb, "\251alb"); | |
979 | mov_write_string_data_tag(pb, mov, s, s->album); | |
980 | size = updateSize(pb, pos); | |
981 | } | |
982 | return size; | |
983 | } | |
984 | ||
985 | /* iTunes year */ | |
986 | static int mov_write_day_tag(ByteIOContext *pb, MOVContext* mov, | |
987 | AVFormatContext *s) | |
988 | { | |
989 | char year[5]; | |
990 | int size = 0; | |
991 | if ( s->year ) { | |
b29af723 | 992 | offset_t pos = url_ftell(pb); |
b6c50eb1 PB |
993 | put_be32(pb, 0); /* size */ |
994 | put_tag(pb, "\251day"); | |
995 | snprintf(year, 5, "%04d", s->year); | |
996 | mov_write_string_data_tag(pb, mov, s, year); | |
997 | size = updateSize(pb, pos); | |
998 | } | |
999 | return size; | |
1000 | } | |
1001 | ||
1002 | /* iTunes tool used to create the file */ | |
1003 | static int mov_write_too_tag(ByteIOContext *pb, MOVContext* mov, | |
1004 | AVFormatContext *s) | |
1005 | { | |
b29af723 | 1006 | offset_t pos = url_ftell(pb); |
b6c50eb1 PB |
1007 | put_be32(pb, 0); /* size */ |
1008 | put_tag(pb, "\251too"); | |
1009 | mov_write_string_data_tag(pb, mov, s, LIBAVFORMAT_IDENT); | |
1010 | return updateSize(pb, pos); | |
1011 | } | |
1012 | ||
1013 | /* iTunes comment */ | |
1014 | static int mov_write_cmt_tag(ByteIOContext *pb, MOVContext* mov, | |
1015 | AVFormatContext *s) | |
1016 | { | |
1017 | int size = 0; | |
1018 | if ( s->comment[0] ) { | |
b29af723 | 1019 | offset_t pos = url_ftell(pb); |
b6c50eb1 PB |
1020 | put_be32(pb, 0); /* size */ |
1021 | put_tag(pb, "\251cmt"); | |
1022 | mov_write_string_data_tag(pb, mov, s, s->comment); | |
1023 | size = updateSize(pb, pos); | |
1024 | } | |
1025 | return size; | |
1026 | } | |
1027 | ||
1028 | /* iTunes custom genre */ | |
1029 | static int mov_write_gen_tag(ByteIOContext *pb, MOVContext* mov, | |
1030 | AVFormatContext *s) | |
1031 | { | |
1032 | int size = 0; | |
1033 | if ( s->genre[0] ) { | |
b29af723 | 1034 | offset_t pos = url_ftell(pb); |
b6c50eb1 PB |
1035 | put_be32(pb, 0); /* size */ |
1036 | put_tag(pb, "\251gen"); | |
1037 | mov_write_string_data_tag(pb, mov, s, s->genre); | |
1038 | size = updateSize(pb, pos); | |
1039 | } | |
1040 | return size; | |
1041 | } | |
1042 | ||
1043 | /* iTunes track number */ | |
1044 | static int mov_write_trkn_tag(ByteIOContext *pb, MOVContext* mov, | |
1045 | AVFormatContext *s) | |
1046 | { | |
1047 | int size = 0; | |
1048 | if ( s->track ) { | |
b29af723 | 1049 | offset_t pos = url_ftell(pb); |
b6c50eb1 PB |
1050 | put_be32(pb, 0); /* size */ |
1051 | put_tag(pb, "trkn"); | |
1052 | { | |
b29af723 | 1053 | offset_t pos = url_ftell(pb); |
b6c50eb1 PB |
1054 | put_be32(pb, 0); /* size */ |
1055 | put_tag(pb, "data"); | |
1056 | put_be32(pb, 0); // 8 bytes empty | |
1057 | put_be32(pb, 0); | |
1058 | put_be16(pb, 0); // empty | |
1059 | put_be16(pb, s->track); // track number | |
1060 | put_be16(pb, 0); // total track number | |
1061 | put_be16(pb, 0); // empty | |
1062 | updateSize(pb, pos); | |
1063 | } | |
1064 | size = updateSize(pb, pos); | |
1065 | } | |
1066 | return size; | |
1067 | } | |
1068 | ||
1069 | /* iTunes meta data list */ | |
1070 | static int mov_write_ilst_tag(ByteIOContext *pb, MOVContext* mov, | |
1071 | AVFormatContext *s) | |
1072 | { | |
b29af723 | 1073 | offset_t pos = url_ftell(pb); |
b6c50eb1 PB |
1074 | put_be32(pb, 0); /* size */ |
1075 | put_tag(pb, "ilst"); | |
1076 | mov_write_nam_tag(pb, mov, s); | |
1077 | mov_write_ART_tag(pb, mov, s); | |
1078 | mov_write_wrt_tag(pb, mov, s); | |
1079 | mov_write_alb_tag(pb, mov, s); | |
1080 | mov_write_day_tag(pb, mov, s); | |
1081 | mov_write_too_tag(pb, mov, s); | |
1082 | mov_write_cmt_tag(pb, mov, s); | |
1083 | mov_write_gen_tag(pb, mov, s); | |
1084 | mov_write_trkn_tag(pb, mov, s); | |
1085 | return updateSize(pb, pos); | |
1086 | } | |
1087 | ||
1088 | /* iTunes meta data tag */ | |
1089 | static int mov_write_meta_tag(ByteIOContext *pb, MOVContext* mov, | |
1090 | AVFormatContext *s) | |
1091 | { | |
1092 | int size = 0; | |
1093 | ||
1094 | // only save meta tag if required | |
115329f1 | 1095 | if ( s->title[0] || s->author[0] || s->album[0] || s->year || |
b6c50eb1 | 1096 | s->comment[0] || s->genre[0] || s->track ) { |
b29af723 | 1097 | offset_t pos = url_ftell(pb); |
b6c50eb1 PB |
1098 | put_be32(pb, 0); /* size */ |
1099 | put_tag(pb, "meta"); | |
1100 | put_be32(pb, 0); | |
1101 | mov_write_itunes_hdlr_tag(pb, mov, s); | |
1102 | mov_write_ilst_tag(pb, mov, s); | |
1103 | size = updateSize(pb, pos); | |
1104 | } | |
1105 | return size; | |
1106 | } | |
115329f1 | 1107 | |
69dde1ad GB |
1108 | static int mov_write_udta_tag(ByteIOContext *pb, MOVContext* mov, |
1109 | AVFormatContext *s) | |
1110 | { | |
b29af723 | 1111 | offset_t pos = url_ftell(pb); |
69dde1ad GB |
1112 | int i; |
1113 | ||
1114 | put_be32(pb, 0); /* size */ | |
1115 | put_tag(pb, "udta"); | |
1116 | ||
b6c50eb1 PB |
1117 | /* iTunes meta data */ |
1118 | mov_write_meta_tag(pb, mov, s); | |
1119 | ||
69dde1ad GB |
1120 | /* Requirements */ |
1121 | for (i=0; i<MAX_STREAMS; i++) { | |
1122 | if(mov->tracks[i].entry <= 0) continue; | |
1123 | if (mov->tracks[i].enc->codec_id == CODEC_ID_AAC || | |
1124 | mov->tracks[i].enc->codec_id == CODEC_ID_MPEG4) { | |
b29af723 | 1125 | offset_t pos = url_ftell(pb); |
69dde1ad GB |
1126 | put_be32(pb, 0); /* size */ |
1127 | put_tag(pb, "\251req"); | |
1128 | put_be16(pb, sizeof("QuickTime 6.0 or greater") - 1); | |
1129 | put_be16(pb, 0); | |
1130 | put_buffer(pb, "QuickTime 6.0 or greater", | |
1131 | sizeof("QuickTime 6.0 or greater") - 1); | |
1132 | updateSize(pb, pos); | |
1133 | break; | |
1134 | } | |
1135 | } | |
1136 | ||
1137 | /* Encoder */ | |
501866a1 | 1138 | if(mov->tracks[0].enc && !(mov->tracks[0].enc->flags & CODEC_FLAG_BITEXACT)) |
69dde1ad | 1139 | { |
b29af723 | 1140 | offset_t pos = url_ftell(pb); |
69dde1ad GB |
1141 | put_be32(pb, 0); /* size */ |
1142 | put_tag(pb, "\251enc"); | |
1143 | put_be16(pb, sizeof(LIBAVFORMAT_IDENT) - 1); /* string length */ | |
1144 | put_be16(pb, 0); | |
1145 | put_buffer(pb, LIBAVFORMAT_IDENT, sizeof(LIBAVFORMAT_IDENT) - 1); | |
1146 | updateSize(pb, pos); | |
1147 | } | |
1148 | ||
1149 | if( s->title[0] ) | |
1150 | { | |
b29af723 | 1151 | offset_t pos = url_ftell(pb); |
69dde1ad GB |
1152 | put_be32(pb, 0); /* size */ |
1153 | put_tag(pb, "\251nam"); | |
1154 | put_be16(pb, strlen(s->title)); /* string length */ | |
1155 | put_be16(pb, 0); | |
1156 | put_buffer(pb, s->title, strlen(s->title)); | |
1157 | updateSize(pb, pos); | |
1158 | } | |
1159 | ||
1160 | if( s->author[0] ) | |
1161 | { | |
b29af723 | 1162 | offset_t pos = url_ftell(pb); |
69dde1ad GB |
1163 | put_be32(pb, 0); /* size */ |
1164 | put_tag(pb, /*"\251aut"*/ "\251day" ); | |
1165 | put_be16(pb, strlen(s->author)); /* string length */ | |
1166 | put_be16(pb, 0); | |
1167 | put_buffer(pb, s->author, strlen(s->author)); | |
1168 | updateSize(pb, pos); | |
1169 | } | |
1170 | ||
1171 | if( s->comment[0] ) | |
1172 | { | |
b29af723 | 1173 | offset_t pos = url_ftell(pb); |
69dde1ad GB |
1174 | put_be32(pb, 0); /* size */ |
1175 | put_tag(pb, "\251des"); | |
1176 | put_be16(pb, strlen(s->comment)); /* string length */ | |
1177 | put_be16(pb, 0); | |
1178 | put_buffer(pb, s->comment, strlen(s->comment)); | |
1179 | updateSize(pb, pos); | |
1180 | } | |
1181 | ||
1182 | return updateSize(pb, pos); | |
1183 | } | |
1184 | ||
1185 | static int mov_write_moov_tag(ByteIOContext *pb, MOVContext *mov, | |
1186 | AVFormatContext *s) | |
1cb5f7fd | 1187 | { |
b29af723 MN |
1188 | int i; |
1189 | offset_t pos = url_ftell(pb); | |
1cb5f7fd MN |
1190 | put_be32(pb, 0); /* size placeholder*/ |
1191 | put_tag(pb, "moov"); | |
1192 | mov->timescale = globalTimescale; | |
1193 | ||
1194 | for (i=0; i<MAX_STREAMS; i++) { | |
e45ccf79 GB |
1195 | if(mov->tracks[i].entry <= 0) continue; |
1196 | ||
1197 | if(mov->tracks[i].enc->codec_type == CODEC_TYPE_VIDEO) { | |
c0df9d75 MN |
1198 | mov->tracks[i].timescale = mov->tracks[i].enc->time_base.den; |
1199 | mov->tracks[i].sampleDuration = mov->tracks[i].enc->time_base.num; | |
e45ccf79 GB |
1200 | } |
1201 | else if(mov->tracks[i].enc->codec_type == CODEC_TYPE_AUDIO) { | |
1202 | /* If AMR, track timescale = 8000, AMR_WB = 16000 */ | |
1203 | if(mov->tracks[i].enc->codec_id == CODEC_ID_AMR_NB) { | |
1204 | mov->tracks[i].sampleDuration = 160; // Bytes per chunk | |
1205 | mov->tracks[i].timescale = 8000; | |
1cb5f7fd | 1206 | } |
e45ccf79 GB |
1207 | else { |
1208 | mov->tracks[i].timescale = mov->tracks[i].enc->sample_rate; | |
1209 | mov->tracks[i].sampleDuration = mov->tracks[i].enc->frame_size; | |
1cb5f7fd | 1210 | } |
1cb5f7fd | 1211 | } |
e45ccf79 | 1212 | |
115329f1 | 1213 | mov->tracks[i].trackDuration = |
e45ccf79 GB |
1214 | mov->tracks[i].sampleCount * mov->tracks[i].sampleDuration; |
1215 | mov->tracks[i].time = mov->time; | |
1216 | mov->tracks[i].trackID = i+1; | |
1cb5f7fd MN |
1217 | } |
1218 | ||
6e6d6dc0 MN |
1219 | mov_write_mvhd_tag(pb, mov); |
1220 | //mov_write_iods_tag(pb, mov); | |
1cb5f7fd MN |
1221 | for (i=0; i<MAX_STREAMS; i++) { |
1222 | if(mov->tracks[i].entry > 0) { | |
6e6d6dc0 | 1223 | mov_write_trak_tag(pb, &(mov->tracks[i])); |
1cb5f7fd MN |
1224 | } |
1225 | } | |
1226 | ||
69dde1ad GB |
1227 | mov_write_udta_tag(pb, mov, s); |
1228 | ||
6e6d6dc0 | 1229 | return updateSize(pb, pos); |
1cb5f7fd MN |
1230 | } |
1231 | ||
f578f938 | 1232 | int mov_write_mdat_tag(ByteIOContext *pb, MOVContext* mov) |
1cb5f7fd | 1233 | { |
b29af723 MN |
1234 | put_be32(pb, 8); // placeholder for extended size field (64 bit) |
1235 | put_tag(pb, "wide"); | |
1236 | ||
115329f1 | 1237 | mov->mdat_pos = url_ftell(pb); |
1cb5f7fd MN |
1238 | put_be32(pb, 0); /* size placeholder*/ |
1239 | put_tag(pb, "mdat"); | |
1240 | return 0; | |
1241 | } | |
1242 | ||
1243 | /* TODO: This needs to be more general */ | |
e45ccf79 | 1244 | int mov_write_ftyp_tag(ByteIOContext *pb, AVFormatContext *s) |
1cb5f7fd | 1245 | { |
69dde1ad GB |
1246 | MOVContext *mov = s->priv_data; |
1247 | ||
1cb5f7fd MN |
1248 | put_be32(pb, 0x14 ); /* size */ |
1249 | put_tag(pb, "ftyp"); | |
e45ccf79 | 1250 | |
69dde1ad | 1251 | if ( mov->mode == MODE_3GP ) |
e45ccf79 | 1252 | put_tag(pb, "3gp4"); |
8536ab89 | 1253 | else if ( mov->mode == MODE_3G2 ) |
1254 | put_tag(pb, "3g2a"); | |
8af18154 | 1255 | else if ( mov->mode == MODE_PSP ) |
1256 | put_tag(pb, "MSNV"); | |
e45ccf79 GB |
1257 | else |
1258 | put_tag(pb, "isom"); | |
1259 | ||
1cb5f7fd | 1260 | put_be32(pb, 0x200 ); |
e45ccf79 | 1261 | |
69dde1ad | 1262 | if ( mov->mode == MODE_3GP ) |
e45ccf79 | 1263 | put_tag(pb, "3gp4"); |
8536ab89 | 1264 | else if ( mov->mode == MODE_3G2 ) |
1265 | put_tag(pb, "3g2a"); | |
8af18154 | 1266 | else if ( mov->mode == MODE_PSP ) |
1267 | put_tag(pb, "MSNV"); | |
e45ccf79 GB |
1268 | else |
1269 | put_tag(pb, "mp41"); | |
1270 | ||
1cb5f7fd MN |
1271 | return 0x14; |
1272 | } | |
1273 | ||
0c716ab7 | 1274 | static void mov_write_uuidprof_tag(ByteIOContext *pb, AVFormatContext *s) |
8af18154 | 1275 | { |
01f4895c MN |
1276 | int AudioRate = s->streams[1]->codec->sample_rate; |
1277 | int FrameRate = ((s->streams[0]->codec->time_base.den) * (0x10000))/ (s->streams[0]->codec->time_base.num); | |
115329f1 | 1278 | |
8af18154 | 1279 | //printf("audiorate = %d\n",AudioRate); |
01f4895c | 1280 | //printf("framerate = %d / %d = 0x%x\n",s->streams[0]->codec->time_base.den,s->streams[0]->codec->time_base.num,FrameRate); |
8af18154 | 1281 | |
1282 | put_be32(pb, 0x94 ); /* size */ | |
1283 | put_tag(pb, "uuid"); | |
1284 | put_tag(pb, "PROF"); | |
1285 | ||
1286 | put_be32(pb, 0x21d24fce ); /* 96 bit UUID */ | |
1287 | put_be32(pb, 0xbb88695c ); | |
1288 | put_be32(pb, 0xfac9c740 ); | |
1289 | ||
1290 | put_be32(pb, 0x0 ); /* ? */ | |
1291 | put_be32(pb, 0x3 ); /* 3 sections ? */ | |
1292 | ||
1293 | put_be32(pb, 0x14 ); /* size */ | |
1294 | put_tag(pb, "FPRF"); | |
1295 | put_be32(pb, 0x0 ); /* ? */ | |
1296 | put_be32(pb, 0x0 ); /* ? */ | |
1297 | put_be32(pb, 0x0 ); /* ? */ | |
1298 | ||
1299 | put_be32(pb, 0x2c ); /* size */ | |
1300 | put_tag(pb, "APRF"); /* audio */ | |
1301 | put_be32(pb, 0x0 ); | |
1302 | put_be32(pb, 0x2 ); | |
1303 | put_tag(pb, "mp4a"); | |
1304 | put_be32(pb, 0x20f ); | |
1305 | put_be32(pb, 0x0 ); | |
1306 | put_be32(pb, 0x40 ); | |
1307 | put_be32(pb, 0x40 ); | |
1308 | put_be32(pb, AudioRate ); //24000 ... audio rate? | |
1309 | put_be32(pb, 0x2 ); | |
1310 | ||
1311 | put_be32(pb, 0x34 ); /* size */ | |
1312 | put_tag(pb, "VPRF"); /* video */ | |
1313 | put_be32(pb, 0x0 ); | |
1314 | put_be32(pb, 0x1 ); | |
1315 | put_tag(pb, "mp4v"); | |
1316 | put_be32(pb, 0x103 ); | |
1317 | put_be32(pb, 0x0 ); | |
1318 | put_be32(pb, 0xc0 ); | |
1319 | put_be32(pb, 0xc0 ); | |
115329f1 | 1320 | put_be32(pb, FrameRate); // was 0xefc29 |
8af18154 | 1321 | put_be32(pb, FrameRate ); // was 0xefc29 |
01f4895c MN |
1322 | put_be16(pb, s->streams[0]->codec->width); |
1323 | put_be16(pb, s->streams[0]->codec->height); | |
8af18154 | 1324 | put_be32(pb, 0x010001 ); |
8af18154 | 1325 | } |
1326 | ||
1cb5f7fd MN |
1327 | static int mov_write_header(AVFormatContext *s) |
1328 | { | |
1329 | ByteIOContext *pb = &s->pb; | |
69dde1ad GB |
1330 | MOVContext *mov = s->priv_data; |
1331 | int i; | |
1cb5f7fd | 1332 | |
ffdd57d4 | 1333 | for(i=0; i<s->nb_streams; i++){ |
01f4895c | 1334 | AVCodecContext *c= s->streams[i]->codec; |
ffdd57d4 MN |
1335 | |
1336 | if (c->codec_type == CODEC_TYPE_VIDEO){ | |
1337 | if (!codec_get_tag(codec_movvideo_tags, c->codec_id)){ | |
1338 | if(!codec_get_tag(codec_bmp_tags, c->codec_id)) | |
1339 | return -1; | |
1340 | else | |
1341 | av_log(s, AV_LOG_INFO, "Warning, using MS style video codec tag, the file may be unplayable!\n"); | |
1342 | } | |
1343 | }else if(c->codec_type == CODEC_TYPE_AUDIO){ | |
1344 | if (!codec_get_tag(codec_movaudio_tags, c->codec_id)){ | |
1345 | if(!codec_get_tag(codec_wav_tags, c->codec_id)) | |
1346 | return -1; | |
1347 | else | |
1348 | av_log(s, AV_LOG_INFO, "Warning, using MS style audio codec tag, the file may be unplayable!\n"); | |
1349 | } | |
1350 | } | |
ab561df9 FR |
1351 | /* don't know yet if mp4 or not */ |
1352 | mov->tracks[i].language = ff_mov_iso639_to_lang(s->streams[i]->language, 1); | |
ffdd57d4 MN |
1353 | } |
1354 | ||
69dde1ad GB |
1355 | /* Default mode == MP4 */ |
1356 | mov->mode = MODE_MP4; | |
1357 | ||
1358 | if (s->oformat != NULL) { | |
1359 | if (!strcmp("3gp", s->oformat->name)) mov->mode = MODE_3GP; | |
8536ab89 | 1360 | else if (!strcmp("3g2", s->oformat->name)) mov->mode = MODE_3G2; |
69dde1ad | 1361 | else if (!strcmp("mov", s->oformat->name)) mov->mode = MODE_MOV; |
8af18154 | 1362 | else if (!strcmp("psp", s->oformat->name)) mov->mode = MODE_PSP; |
69dde1ad | 1363 | |
8536ab89 | 1364 | if ( mov->mode == MODE_3GP || mov->mode == MODE_3G2 || |
1365 | mov->mode == MODE_MP4 || mov->mode == MODE_PSP ) | |
69dde1ad | 1366 | mov_write_ftyp_tag(pb,s); |
0c716ab7 FR |
1367 | if ( mov->mode == MODE_PSP ) { |
1368 | if ( s->nb_streams != 2 ) { | |
1369 | av_log(s, AV_LOG_ERROR, "PSP mode need one video and one audio stream\n"); | |
1370 | return -1; | |
1371 | } | |
8af18154 | 1372 | mov_write_uuidprof_tag(pb,s); |
0c716ab7 | 1373 | } |
69dde1ad GB |
1374 | } |
1375 | ||
1376 | for (i=0; i<MAX_STREAMS; i++) { | |
1377 | mov->tracks[i].mode = mov->mode; | |
f578f938 TR |
1378 | } |
1379 | ||
1cb5f7fd MN |
1380 | put_flush_packet(pb); |
1381 | ||
1382 | return 0; | |
1383 | } | |
1384 | ||
e928649b | 1385 | static int mov_write_packet(AVFormatContext *s, AVPacket *pkt) |
1cb5f7fd MN |
1386 | { |
1387 | MOVContext *mov = s->priv_data; | |
1388 | ByteIOContext *pb = &s->pb; | |
01f4895c | 1389 | AVCodecContext *enc = s->streams[pkt->stream_index]->codec; |
e928649b | 1390 | MOVTrack* trk = &mov->tracks[pkt->stream_index]; |
1a31840c | 1391 | int cl, id; |
e45ccf79 | 1392 | unsigned int samplesInChunk = 0; |
e928649b | 1393 | int size= pkt->size; |
1cb5f7fd | 1394 | |
e45ccf79 GB |
1395 | if (url_is_streamed(&s->pb)) return 0; /* Can't handle that */ |
1396 | if (!size) return 0; /* Discard 0 sized packets */ | |
1cb5f7fd | 1397 | |
e45ccf79 GB |
1398 | if (enc->codec_type == CODEC_TYPE_VIDEO ) { |
1399 | samplesInChunk = 1; | |
1400 | } | |
1401 | else if (enc->codec_type == CODEC_TYPE_AUDIO ) { | |
1402 | if( enc->codec_id == CODEC_ID_AMR_NB) { | |
f578f938 | 1403 | /* We must find out how many AMR blocks there are in one packet */ |
e45ccf79 GB |
1404 | static uint16_t packed_size[16] = |
1405 | {13, 14, 16, 18, 20, 21, 27, 32, 6, 0, 0, 0, 0, 0, 0, 0}; | |
1406 | int len = 0; | |
1407 | ||
1408 | while (len < size && samplesInChunk < 100) { | |
e928649b | 1409 | len += packed_size[(pkt->data[len] >> 3) & 0x0F]; |
e45ccf79 | 1410 | samplesInChunk++; |
f578f938 | 1411 | } |
1cb5f7fd | 1412 | } |
e45ccf79 GB |
1413 | else if(enc->codec_id == CODEC_ID_PCM_ALAW) { |
1414 | samplesInChunk = size/enc->channels; | |
1cb5f7fd | 1415 | } |
bb270c08 DB |
1416 | else if(enc->codec_id == CODEC_ID_PCM_S16BE || enc->codec_id == CODEC_ID_PCM_S16LE) { |
1417 | samplesInChunk = size/(2*enc->channels); | |
115329f1 | 1418 | } |
e45ccf79 GB |
1419 | else { |
1420 | samplesInChunk = 1; | |
1cb5f7fd | 1421 | } |
e45ccf79 GB |
1422 | } |
1423 | ||
1424 | if ((enc->codec_id == CODEC_ID_MPEG4 || enc->codec_id == CODEC_ID_AAC) | |
1425 | && trk->vosLen == 0) { | |
21f52609 | 1426 | // assert(enc->extradata_size); |
e45ccf79 GB |
1427 | |
1428 | trk->vosLen = enc->extradata_size; | |
1429 | trk->vosData = av_malloc(trk->vosLen); | |
1430 | memcpy(trk->vosData, enc->extradata, trk->vosLen); | |
1431 | } | |
1432 | ||
1433 | cl = trk->entry / MOV_INDEX_CLUSTER_SIZE; | |
1434 | id = trk->entry % MOV_INDEX_CLUSTER_SIZE; | |
1435 | ||
1436 | if (trk->ents_allocated <= trk->entry) { | |
115329f1 | 1437 | trk->cluster = av_realloc(trk->cluster, (cl+1)*sizeof(void*)); |
e45ccf79 GB |
1438 | if (!trk->cluster) |
1439 | return -1; | |
1440 | trk->cluster[cl] = av_malloc(MOV_INDEX_CLUSTER_SIZE*sizeof(MOVIentry)); | |
1441 | if (!trk->cluster[cl]) | |
1442 | return -1; | |
1443 | trk->ents_allocated += MOV_INDEX_CLUSTER_SIZE; | |
1444 | } | |
1445 | if (mov->mdat_written == 0) { | |
1446 | mov_write_mdat_tag(pb, mov); | |
1447 | mov->mdat_written = 1; | |
8272de4e | 1448 | mov->time = s->timestamp + 0x7C25B080; //1970 based -> 1904 based |
e45ccf79 GB |
1449 | } |
1450 | ||
69dde1ad | 1451 | trk->cluster[cl][id].pos = url_ftell(pb); |
e45ccf79 GB |
1452 | trk->cluster[cl][id].samplesInChunk = samplesInChunk; |
1453 | trk->cluster[cl][id].size = size; | |
1454 | trk->cluster[cl][id].entries = samplesInChunk; | |
1455 | if(enc->codec_type == CODEC_TYPE_VIDEO) { | |
e928649b MN |
1456 | trk->cluster[cl][id].key_frame = !!(pkt->flags & PKT_FLAG_KEY); |
1457 | if(trk->cluster[cl][id].key_frame) | |
f578f938 | 1458 | trk->hasKeyframes = 1; |
1cb5f7fd | 1459 | } |
e45ccf79 GB |
1460 | trk->enc = enc; |
1461 | trk->entry++; | |
1462 | trk->sampleCount += samplesInChunk; | |
1463 | trk->mdat_size += size; | |
1464 | ||
e928649b | 1465 | put_buffer(pb, pkt->data, size); |
1cb5f7fd MN |
1466 | |
1467 | put_flush_packet(pb); | |
1468 | return 0; | |
1469 | } | |
1470 | ||
1471 | static int mov_write_trailer(AVFormatContext *s) | |
1472 | { | |
1473 | MOVContext *mov = s->priv_data; | |
1474 | ByteIOContext *pb = &s->pb; | |
1475 | int res = 0; | |
b29af723 MN |
1476 | int i; |
1477 | uint64_t j; | |
1cb5f7fd | 1478 | |
69dde1ad | 1479 | offset_t moov_pos = url_ftell(pb); |
1cb5f7fd MN |
1480 | |
1481 | /* Write size of mdat tag */ | |
69dde1ad | 1482 | for (i=0, j=0; i<MAX_STREAMS; i++) { |
1cb5f7fd MN |
1483 | if(mov->tracks[i].ents_allocated > 0) { |
1484 | j += mov->tracks[i].mdat_size; | |
1485 | } | |
1486 | } | |
b29af723 MN |
1487 | if (j+8 <= UINT32_MAX) { |
1488 | url_fseek(pb, mov->mdat_pos, SEEK_SET); | |
1489 | put_be32(pb, j+8); | |
1490 | } else { | |
1491 | /* overwrite 'wide' placeholder atom */ | |
1492 | url_fseek(pb, mov->mdat_pos - 8, SEEK_SET); | |
1493 | put_be32(pb, 1); /* special value: real atom size will be 64 bit value after tag field */ | |
1494 | put_tag(pb, "mdat"); | |
1495 | put_be64(pb, j+16); | |
1496 | } | |
69dde1ad | 1497 | url_fseek(pb, moov_pos, SEEK_SET); |
1cb5f7fd | 1498 | |
69dde1ad | 1499 | mov_write_moov_tag(pb, mov, s); |
1cb5f7fd MN |
1500 | |
1501 | for (i=0; i<MAX_STREAMS; i++) { | |
1502 | for (j=0; j<mov->tracks[i].ents_allocated/MOV_INDEX_CLUSTER_SIZE; j++) { | |
1503 | av_free(mov->tracks[i].cluster[j]); | |
1504 | } | |
1505 | av_free(mov->tracks[i].cluster); | |
ec7d0d2e GB |
1506 | if( mov->tracks[i].vosLen ) av_free( mov->tracks[i].vosData ); |
1507 | ||
1cb5f7fd MN |
1508 | mov->tracks[i].cluster = NULL; |
1509 | mov->tracks[i].ents_allocated = mov->tracks[i].entry = 0; | |
1510 | } | |
69dde1ad | 1511 | |
1cb5f7fd MN |
1512 | put_flush_packet(pb); |
1513 | ||
1514 | return res; | |
1515 | } | |
1516 | ||
1517 | static AVOutputFormat mov_oformat = { | |
1518 | "mov", | |
1519 | "mov format", | |
1520 | NULL, | |
1521 | "mov", | |
1522 | sizeof(MOVContext), | |
69dde1ad | 1523 | CODEC_ID_AAC, |
2187d948 | 1524 | CODEC_ID_MPEG4, |
1cb5f7fd MN |
1525 | mov_write_header, |
1526 | mov_write_packet, | |
1527 | mov_write_trailer, | |
c64d476c | 1528 | .flags = AVFMT_GLOBALHEADER, |
1cb5f7fd MN |
1529 | }; |
1530 | ||
1531 | static AVOutputFormat _3gp_oformat = { | |
1532 | "3gp", | |
1533 | "3gp format", | |
1534 | NULL, | |
1535 | "3gp", | |
1536 | sizeof(MOVContext), | |
1537 | CODEC_ID_AMR_NB, | |
1538 | CODEC_ID_H263, | |
1539 | mov_write_header, | |
1540 | mov_write_packet, | |
1541 | mov_write_trailer, | |
c64d476c | 1542 | .flags = AVFMT_GLOBALHEADER, |
1cb5f7fd MN |
1543 | }; |
1544 | ||
1545 | static AVOutputFormat mp4_oformat = { | |
1546 | "mp4", | |
1547 | "mp4 format", | |
4cb3f3b6 DC |
1548 | "application/mp4", |
1549 | "mp4,m4a", | |
1cb5f7fd MN |
1550 | sizeof(MOVContext), |
1551 | CODEC_ID_AAC, | |
1552 | CODEC_ID_MPEG4, | |
1553 | mov_write_header, | |
1554 | mov_write_packet, | |
1555 | mov_write_trailer, | |
c64d476c | 1556 | .flags = AVFMT_GLOBALHEADER, |
1cb5f7fd MN |
1557 | }; |
1558 | ||
8af18154 | 1559 | static AVOutputFormat psp_oformat = { |
1560 | "psp", | |
1561 | "psp mp4 format", | |
1562 | NULL, | |
1563 | "mp4,psp", | |
1564 | sizeof(MOVContext), | |
1565 | CODEC_ID_AAC, | |
1566 | CODEC_ID_MPEG4, | |
1567 | mov_write_header, | |
1568 | mov_write_packet, | |
1569 | mov_write_trailer, | |
ced225a7 | 1570 | // .flags = AVFMT_GLOBALHEADER, |
8af18154 | 1571 | }; |
1572 | ||
8536ab89 | 1573 | static AVOutputFormat _3g2_oformat = { |
1574 | "3g2", | |
1575 | "3gp2 format", | |
1576 | NULL, | |
1577 | "3g2", | |
1578 | sizeof(MOVContext), | |
1579 | CODEC_ID_AMR_NB, | |
1580 | CODEC_ID_H263, | |
1581 | mov_write_header, | |
1582 | mov_write_packet, | |
1583 | mov_write_trailer, | |
c64d476c | 1584 | .flags = AVFMT_GLOBALHEADER, |
8536ab89 | 1585 | }; |
1586 | ||
1cb5f7fd MN |
1587 | int movenc_init(void) |
1588 | { | |
1589 | av_register_output_format(&mov_oformat); | |
1590 | av_register_output_format(&_3gp_oformat); | |
1591 | av_register_output_format(&mp4_oformat); | |
8af18154 | 1592 | av_register_output_format(&psp_oformat); |
8536ab89 | 1593 | av_register_output_format(&_3g2_oformat); |
1cb5f7fd MN |
1594 | return 0; |
1595 | } |