Mercurial > libavformat.hg
comparison mov.c @ 3169:8ae50c221ae6 libavformat
mp4 fragments support
author | bcoudurier |
---|---|
date | Fri, 21 Mar 2008 13:40:23 +0000 |
parents | 125b08d2ad59 |
children | 3c56f53f437b |
comparison
equal
deleted
inserted
replaced
3168:103f156dd8cc | 3169:8ae50c221ae6 |
---|---|
79 int64_t offset; | 79 int64_t offset; |
80 int64_t size; /* total size (excluding the size and type fields) */ | 80 int64_t size; /* total size (excluding the size and type fields) */ |
81 } MOV_atom_t; | 81 } MOV_atom_t; |
82 | 82 |
83 struct MOVParseTableEntry; | 83 struct MOVParseTableEntry; |
84 | |
85 typedef struct { | |
86 unsigned track_id; | |
87 uint64_t base_data_offset; | |
88 uint64_t moof_offset; | |
89 unsigned stsd_id; | |
90 unsigned duration; | |
91 unsigned size; | |
92 unsigned flags; | |
93 } MOVFragment; | |
94 | |
95 typedef struct { | |
96 unsigned track_id; | |
97 unsigned stsd_id; | |
98 unsigned duration; | |
99 unsigned size; | |
100 unsigned flags; | |
101 } MOVTrackExt; | |
84 | 102 |
85 typedef struct MOVStreamContext { | 103 typedef struct MOVStreamContext { |
86 ByteIOContext *pb; | 104 ByteIOContext *pb; |
87 int ffindex; /* the ffmpeg stream id */ | 105 int ffindex; /* the ffmpeg stream id */ |
88 int next_chunk; | 106 int next_chunk; |
123 int found_mdat; /* we suppose we have enough data to read the file */ | 141 int found_mdat; /* we suppose we have enough data to read the file */ |
124 AVPaletteControl palette_control; | 142 AVPaletteControl palette_control; |
125 DVDemuxContext *dv_demux; | 143 DVDemuxContext *dv_demux; |
126 AVFormatContext *dv_fctx; | 144 AVFormatContext *dv_fctx; |
127 int isom; /* 1 if file is ISO Media (mp4/3gp) */ | 145 int isom; /* 1 if file is ISO Media (mp4/3gp) */ |
146 MOVFragment fragment; ///< current fragment in moof atom | |
147 MOVTrackExt *trex_data; | |
148 unsigned trex_count; | |
128 } MOVContext; | 149 } MOVContext; |
129 | 150 |
130 | 151 |
131 /* XXX: it's the first time I make a recursive parser I think... sorry if it's ugly :P */ | 152 /* XXX: it's the first time I make a recursive parser I think... sorry if it's ugly :P */ |
132 | 153 |
414 /* so we don't parse the whole file if over a network */ | 435 /* so we don't parse the whole file if over a network */ |
415 c->found_moov=1; | 436 c->found_moov=1; |
416 return 0; /* now go for mdat */ | 437 return 0; /* now go for mdat */ |
417 } | 438 } |
418 | 439 |
440 static int mov_read_moof(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) | |
441 { | |
442 c->fragment.moof_offset = url_ftell(pb) - 8; | |
443 dprintf(c->fc, "moof offset %llx\n", c->fragment.moof_offset); | |
444 return mov_read_default(c, pb, atom); | |
445 } | |
419 | 446 |
420 static int mov_read_mdhd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) | 447 static int mov_read_mdhd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) |
421 { | 448 { |
422 AVStream *st = c->fc->streams[c->fc->nb_streams-1]; | 449 AVStream *st = c->fc->streams[c->fc->nb_streams-1]; |
423 MOVStreamContext *sc = st->priv_data; | 450 MOVStreamContext *sc = st->priv_data; |
1357 get_be32(pb); /* track height */ | 1384 get_be32(pb); /* track height */ |
1358 | 1385 |
1359 return 0; | 1386 return 0; |
1360 } | 1387 } |
1361 | 1388 |
1389 static int mov_read_tfhd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) | |
1390 { | |
1391 MOVFragment *frag = &c->fragment; | |
1392 MOVTrackExt *trex = NULL; | |
1393 int flags, track_id, i; | |
1394 | |
1395 get_byte(pb); /* version */ | |
1396 flags = get_be24(pb); | |
1397 | |
1398 track_id = get_be32(pb); | |
1399 if (!track_id || track_id > c->fc->nb_streams) | |
1400 return -1; | |
1401 frag->track_id = track_id; | |
1402 for (i = 0; i < c->trex_count; i++) | |
1403 if (c->trex_data[i].track_id == frag->track_id) { | |
1404 trex = &c->trex_data[i]; | |
1405 break; | |
1406 } | |
1407 if (!trex) { | |
1408 av_log(c->fc, AV_LOG_ERROR, "could not find corresponding trex\n"); | |
1409 return -1; | |
1410 } | |
1411 | |
1412 if (flags & 0x01) frag->base_data_offset = get_be64(pb); | |
1413 else frag->base_data_offset = frag->moof_offset; | |
1414 if (flags & 0x02) frag->stsd_id = get_be32(pb); | |
1415 else frag->stsd_id = trex->stsd_id; | |
1416 | |
1417 frag->duration = flags & 0x08 ? get_be32(pb) : trex->duration; | |
1418 frag->size = flags & 0x10 ? get_be32(pb) : trex->size; | |
1419 frag->flags = flags & 0x20 ? get_be32(pb) : trex->flags; | |
1420 dprintf(c->fc, "frag flags 0x%x\n", frag->flags); | |
1421 return 0; | |
1422 } | |
1423 | |
1424 static int mov_read_trex(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) | |
1425 { | |
1426 MOVTrackExt *trex; | |
1427 | |
1428 if ((uint64_t)c->trex_count+1 >= UINT_MAX / sizeof(*c->trex_data)) | |
1429 return -1; | |
1430 c->trex_data = av_realloc(c->trex_data, (c->trex_count+1)*sizeof(*c->trex_data)); | |
1431 if (!c->trex_data) | |
1432 return AVERROR(ENOMEM); | |
1433 trex = &c->trex_data[c->trex_count++]; | |
1434 get_byte(pb); /* version */ | |
1435 get_be24(pb); /* flags */ | |
1436 trex->track_id = get_be32(pb); | |
1437 trex->stsd_id = get_be32(pb); | |
1438 trex->duration = get_be32(pb); | |
1439 trex->size = get_be32(pb); | |
1440 trex->flags = get_be32(pb); | |
1441 return 0; | |
1442 } | |
1443 | |
1444 static int mov_read_trun(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) | |
1445 { | |
1446 MOVFragment *frag = &c->fragment; | |
1447 AVStream *st = c->fc->streams[frag->track_id-1]; | |
1448 MOVStreamContext *sc = st->priv_data; | |
1449 uint64_t offset; | |
1450 int64_t dts; | |
1451 int data_offset = 0; | |
1452 unsigned entries, first_sample_flags = frag->flags; | |
1453 int flags, distance, i; | |
1454 | |
1455 if (sc->pseudo_stream_id+1 != frag->stsd_id) | |
1456 return 0; | |
1457 if (!st->nb_index_entries) | |
1458 return -1; | |
1459 get_byte(pb); /* version */ | |
1460 flags = get_be24(pb); | |
1461 entries = get_be32(pb); | |
1462 dprintf(c->fc, "flags 0x%x entries %d\n", flags, entries); | |
1463 if (flags & 0x001) data_offset = get_be32(pb); | |
1464 if (flags & 0x004) first_sample_flags = get_be32(pb); | |
1465 if (flags & 0x800) { | |
1466 if ((uint64_t)entries+sc->ctts_count >= UINT_MAX/sizeof(*sc->ctts_data)) | |
1467 return -1; | |
1468 sc->ctts_data = av_realloc(sc->ctts_data, | |
1469 (entries+sc->ctts_count)*sizeof(*sc->ctts_data)); | |
1470 if (!sc->ctts_data) | |
1471 return AVERROR(ENOMEM); | |
1472 } | |
1473 dts = st->duration; | |
1474 offset = frag->base_data_offset + data_offset; | |
1475 distance = 0; | |
1476 dprintf(c->fc, "first sample flags 0x%x\n", first_sample_flags); | |
1477 for (i = 0; i < entries; i++) { | |
1478 unsigned sample_size = frag->size; | |
1479 int sample_flags = i ? frag->flags : first_sample_flags; | |
1480 unsigned sample_duration = frag->duration; | |
1481 int keyframe; | |
1482 | |
1483 if (flags & 0x100) sample_duration = get_be32(pb); | |
1484 if (flags & 0x200) sample_size = get_be32(pb); | |
1485 if (flags & 0x400) sample_flags = get_be32(pb); | |
1486 if (flags & 0x800) { | |
1487 sc->ctts_data[sc->ctts_count].count = 1; | |
1488 sc->ctts_data[sc->ctts_count].duration = get_be32(pb); | |
1489 sc->ctts_count++; | |
1490 } | |
1491 if ((keyframe = st->codec->codec_type == CODEC_TYPE_AUDIO || | |
1492 (flags & 0x004 && !i && !sample_flags) || sample_flags & 0x2000000)) | |
1493 distance = 0; | |
1494 av_add_index_entry(st, offset, dts, sample_size, distance, | |
1495 keyframe ? AVINDEX_KEYFRAME : 0); | |
1496 dprintf(c->fc, "AVIndex stream %d, sample %d, offset %"PRIx64", dts %"PRId64", " | |
1497 "size %d, distance %d, keyframe %d\n", st->index, sc->sample_count+i, | |
1498 offset, dts, sample_size, distance, keyframe); | |
1499 distance++; | |
1500 assert(sample_duration % sc->time_rate == 0); | |
1501 dts += sample_duration / sc->time_rate; | |
1502 offset += sample_size; | |
1503 } | |
1504 frag->moof_offset = offset; | |
1505 sc->sample_count = st->nb_index_entries; | |
1506 st->duration = dts; | |
1507 return 0; | |
1508 } | |
1509 | |
1362 /* this atom should be null (from specs), but some buggy files put the 'moov' atom inside it... */ | 1510 /* this atom should be null (from specs), but some buggy files put the 'moov' atom inside it... */ |
1363 /* like the files created with Adobe Premiere 5.0, for samples see */ | 1511 /* like the files created with Adobe Premiere 5.0, for samples see */ |
1364 /* http://graphics.tudelft.nl/~wouter/publications/soundtests/ */ | 1512 /* http://graphics.tudelft.nl/~wouter/publications/soundtests/ */ |
1365 static int mov_read_wide(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) | 1513 static int mov_read_wide(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) |
1366 { | 1514 { |
1472 { MKTAG( 'j', 'p', '2', 'h' ), mov_read_extradata }, | 1620 { MKTAG( 'j', 'p', '2', 'h' ), mov_read_extradata }, |
1473 { MKTAG( 'm', 'd', 'a', 't' ), mov_read_mdat }, | 1621 { MKTAG( 'm', 'd', 'a', 't' ), mov_read_mdat }, |
1474 { MKTAG( 'm', 'd', 'h', 'd' ), mov_read_mdhd }, | 1622 { MKTAG( 'm', 'd', 'h', 'd' ), mov_read_mdhd }, |
1475 { MKTAG( 'm', 'd', 'i', 'a' ), mov_read_default }, | 1623 { MKTAG( 'm', 'd', 'i', 'a' ), mov_read_default }, |
1476 { MKTAG( 'm', 'i', 'n', 'f' ), mov_read_default }, | 1624 { MKTAG( 'm', 'i', 'n', 'f' ), mov_read_default }, |
1625 { MKTAG( 'm', 'o', 'o', 'f' ), mov_read_moof }, | |
1477 { MKTAG( 'm', 'o', 'o', 'v' ), mov_read_moov }, | 1626 { MKTAG( 'm', 'o', 'o', 'v' ), mov_read_moov }, |
1627 { MKTAG( 'm', 'v', 'e', 'x' ), mov_read_default }, | |
1478 { MKTAG( 'm', 'v', 'h', 'd' ), mov_read_mvhd }, | 1628 { MKTAG( 'm', 'v', 'h', 'd' ), mov_read_mvhd }, |
1479 { MKTAG( 'S', 'M', 'I', ' ' ), mov_read_smi }, /* Sorenson extension ??? */ | 1629 { MKTAG( 'S', 'M', 'I', ' ' ), mov_read_smi }, /* Sorenson extension ??? */ |
1480 { MKTAG( 'a', 'l', 'a', 'c' ), mov_read_extradata }, /* alac specific atom */ | 1630 { MKTAG( 'a', 'l', 'a', 'c' ), mov_read_extradata }, /* alac specific atom */ |
1481 { MKTAG( 'a', 'v', 'c', 'C' ), mov_read_glbl }, | 1631 { MKTAG( 'a', 'v', 'c', 'C' ), mov_read_glbl }, |
1482 { MKTAG( 's', 't', 'b', 'l' ), mov_read_default }, | 1632 { MKTAG( 's', 't', 'b', 'l' ), mov_read_default }, |
1485 { MKTAG( 's', 't', 's', 'd' ), mov_read_stsd }, /* sample description */ | 1635 { MKTAG( 's', 't', 's', 'd' ), mov_read_stsd }, /* sample description */ |
1486 { MKTAG( 's', 't', 's', 's' ), mov_read_stss }, /* sync sample */ | 1636 { MKTAG( 's', 't', 's', 's' ), mov_read_stss }, /* sync sample */ |
1487 { MKTAG( 's', 't', 's', 'z' ), mov_read_stsz }, /* sample size */ | 1637 { MKTAG( 's', 't', 's', 'z' ), mov_read_stsz }, /* sample size */ |
1488 { MKTAG( 's', 't', 't', 's' ), mov_read_stts }, | 1638 { MKTAG( 's', 't', 't', 's' ), mov_read_stts }, |
1489 { MKTAG( 't', 'k', 'h', 'd' ), mov_read_tkhd }, /* track header */ | 1639 { MKTAG( 't', 'k', 'h', 'd' ), mov_read_tkhd }, /* track header */ |
1640 { MKTAG( 't', 'f', 'h', 'd' ), mov_read_tfhd }, /* track fragment header */ | |
1490 { MKTAG( 't', 'r', 'a', 'k' ), mov_read_trak }, | 1641 { MKTAG( 't', 'r', 'a', 'k' ), mov_read_trak }, |
1642 { MKTAG( 't', 'r', 'a', 'f' ), mov_read_default }, | |
1643 { MKTAG( 't', 'r', 'e', 'x' ), mov_read_trex }, | |
1644 { MKTAG( 't', 'r', 'u', 'n' ), mov_read_trun }, | |
1491 { MKTAG( 'u', 'd', 't', 'a' ), mov_read_udta }, | 1645 { MKTAG( 'u', 'd', 't', 'a' ), mov_read_udta }, |
1492 { MKTAG( 'w', 'a', 'v', 'e' ), mov_read_wave }, | 1646 { MKTAG( 'w', 'a', 'v', 'e' ), mov_read_wave }, |
1493 { MKTAG( 'e', 's', 'd', 's' ), mov_read_esds }, | 1647 { MKTAG( 'e', 's', 'd', 's' ), mov_read_esds }, |
1494 { MKTAG( 'w', 'i', 'd', 'e' ), mov_read_wide }, /* place holder */ | 1648 { MKTAG( 'w', 'i', 'd', 'e' ), mov_read_wide }, /* place holder */ |
1495 { MKTAG( 'c', 'm', 'o', 'v' ), mov_read_cmov }, | 1649 { MKTAG( 'c', 'm', 'o', 'v' ), mov_read_cmov }, |
1710 av_freep(&mov->dv_fctx->streams[i]); | 1864 av_freep(&mov->dv_fctx->streams[i]); |
1711 } | 1865 } |
1712 av_freep(&mov->dv_fctx); | 1866 av_freep(&mov->dv_fctx); |
1713 av_freep(&mov->dv_demux); | 1867 av_freep(&mov->dv_demux); |
1714 } | 1868 } |
1869 av_freep(&mov->trex_data); | |
1715 return 0; | 1870 return 0; |
1716 } | 1871 } |
1717 | 1872 |
1718 AVInputFormat mov_demuxer = { | 1873 AVInputFormat mov_demuxer = { |
1719 "mov,mp4,m4a,3gp,3g2,mj2", | 1874 "mov,mp4,m4a,3gp,3g2,mj2", |