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