|
|
|
@@ -47,7 +47,6 @@ typedef struct MOVIentry { |
|
|
|
typedef struct MOVIndex { |
|
|
|
int mode; |
|
|
|
int entry; |
|
|
|
uint64_t mdat_size; |
|
|
|
int ents_allocated; |
|
|
|
long timescale; |
|
|
|
long time; |
|
|
|
@@ -58,6 +57,7 @@ typedef struct MOVIndex { |
|
|
|
int hasBframes; |
|
|
|
int language; |
|
|
|
int trackID; |
|
|
|
int tag; |
|
|
|
AVCodecContext *enc; |
|
|
|
|
|
|
|
int vosLen; |
|
|
|
@@ -70,12 +70,11 @@ typedef struct MOVContext { |
|
|
|
int64_t time; |
|
|
|
int nb_streams; |
|
|
|
offset_t mdat_pos; |
|
|
|
uint64_t mdat_size; |
|
|
|
long timescale; |
|
|
|
MOVTrack tracks[MAX_STREAMS]; |
|
|
|
} MOVContext; |
|
|
|
|
|
|
|
static int mov_write_esds_tag(ByteIOContext *pb, MOVTrack* track); |
|
|
|
|
|
|
|
//FIXME supprt 64bit varaint with wide placeholders |
|
|
|
static offset_t updateSize (ByteIOContext *pb, offset_t pos) |
|
|
|
{ |
|
|
|
@@ -225,6 +224,111 @@ static int mov_write_damr_tag(ByteIOContext *pb) |
|
|
|
return 0x11; |
|
|
|
} |
|
|
|
|
|
|
|
static unsigned int descrLength(unsigned int len) |
|
|
|
{ |
|
|
|
if (len < 0x00000080) |
|
|
|
return 2 + len; |
|
|
|
else if (len < 0x00004000) |
|
|
|
return 3 + len; |
|
|
|
else if(len < 0x00200000) |
|
|
|
return 4 + len; |
|
|
|
else |
|
|
|
return 5 + len; |
|
|
|
} |
|
|
|
|
|
|
|
static void putDescr(ByteIOContext *pb, int tag, int size) |
|
|
|
{ |
|
|
|
uint32_t len; |
|
|
|
uint8_t vals[4]; |
|
|
|
|
|
|
|
len = size; |
|
|
|
vals[3] = (uint8_t)(len & 0x7f); |
|
|
|
len >>= 7; |
|
|
|
vals[2] = (uint8_t)((len & 0x7f) | 0x80); |
|
|
|
len >>= 7; |
|
|
|
vals[1] = (uint8_t)((len & 0x7f) | 0x80); |
|
|
|
len >>= 7; |
|
|
|
vals[0] = (uint8_t)((len & 0x7f) | 0x80); |
|
|
|
|
|
|
|
put_byte(pb, tag); // DescriptorTag |
|
|
|
|
|
|
|
if (size < 0x00000080) |
|
|
|
{ |
|
|
|
put_byte(pb, vals[3]); |
|
|
|
} |
|
|
|
else if (size < 0x00004000) |
|
|
|
{ |
|
|
|
put_byte(pb, vals[2]); |
|
|
|
put_byte(pb, vals[3]); |
|
|
|
} |
|
|
|
else if (size < 0x00200000) |
|
|
|
{ |
|
|
|
put_byte(pb, vals[1]); |
|
|
|
put_byte(pb, vals[2]); |
|
|
|
put_byte(pb, vals[3]); |
|
|
|
} |
|
|
|
else if (size < 0x10000000) |
|
|
|
{ |
|
|
|
put_byte(pb, vals[0]); |
|
|
|
put_byte(pb, vals[1]); |
|
|
|
put_byte(pb, vals[2]); |
|
|
|
put_byte(pb, vals[3]); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static int mov_write_esds_tag(ByteIOContext *pb, MOVTrack* track) // Basic |
|
|
|
{ |
|
|
|
int decoderSpecificInfoLen; |
|
|
|
offset_t pos = url_ftell(pb); |
|
|
|
|
|
|
|
decoderSpecificInfoLen = track->vosLen ? descrLength(track->vosLen):0; |
|
|
|
|
|
|
|
put_be32(pb, 0); // size |
|
|
|
put_tag(pb, "esds"); |
|
|
|
put_be32(pb, 0); // Version |
|
|
|
|
|
|
|
// ES descriptor |
|
|
|
putDescr(pb, 0x03, 3 + descrLength(13 + decoderSpecificInfoLen) + |
|
|
|
descrLength(1)); |
|
|
|
put_be16(pb, track->trackID); |
|
|
|
put_byte(pb, 0x00); // flags (= no flags) |
|
|
|
|
|
|
|
// DecoderConfig descriptor |
|
|
|
putDescr(pb, 0x04, 13 + decoderSpecificInfoLen); |
|
|
|
|
|
|
|
// Object type indication |
|
|
|
put_byte(pb, codec_get_tag(ff_mov_obj_type, track->enc->codec_id)); |
|
|
|
|
|
|
|
// the following fields is made of 6 bits to identify the streamtype (4 for video, 5 for audio) |
|
|
|
// plus 1 bit to indicate upstream and 1 bit set to 1 (reserved) |
|
|
|
if(track->enc->codec_type == CODEC_TYPE_AUDIO) |
|
|
|
put_byte(pb, 0x15); // flags (= Audiostream) |
|
|
|
else |
|
|
|
put_byte(pb, 0x11); // flags (= Visualstream) |
|
|
|
|
|
|
|
put_byte(pb, track->enc->rc_buffer_size>>(3+16)); // Buffersize DB (24 bits) |
|
|
|
put_be16(pb, (track->enc->rc_buffer_size>>3)&0xFFFF); // Buffersize DB |
|
|
|
|
|
|
|
put_be32(pb, FFMAX(track->enc->bit_rate, track->enc->rc_max_rate)); // maxbitrate (FIXME should be max rate in any 1 sec window) |
|
|
|
if(track->enc->rc_max_rate != track->enc->rc_min_rate || track->enc->rc_min_rate==0) |
|
|
|
put_be32(pb, 0); // vbr |
|
|
|
else |
|
|
|
put_be32(pb, track->enc->rc_max_rate); // avg bitrate |
|
|
|
|
|
|
|
if (track->vosLen) |
|
|
|
{ |
|
|
|
// DecoderSpecific info descriptor |
|
|
|
putDescr(pb, 0x05, track->vosLen); |
|
|
|
put_buffer(pb, track->vosData, track->vosLen); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// SL descriptor |
|
|
|
putDescr(pb, 0x06, 1); |
|
|
|
put_byte(pb, 0x02); |
|
|
|
return updateSize (pb, pos); |
|
|
|
} |
|
|
|
|
|
|
|
static int mov_write_wave_tag(ByteIOContext *pb, MOVTrack* track) |
|
|
|
{ |
|
|
|
offset_t pos = url_ftell(pb); |
|
|
|
@@ -270,21 +374,9 @@ static const CodecTag codec_movaudio_tags[] = { |
|
|
|
static int mov_write_audio_tag(ByteIOContext *pb, MOVTrack* track) |
|
|
|
{ |
|
|
|
offset_t pos = url_ftell(pb); |
|
|
|
int tag; |
|
|
|
|
|
|
|
put_be32(pb, 0); /* size */ |
|
|
|
|
|
|
|
tag = track->enc->codec_tag; |
|
|
|
if (!tag) |
|
|
|
tag = codec_get_tag(codec_movaudio_tags, track->enc->codec_id); |
|
|
|
// if no mac fcc found, try with Microsoft tags |
|
|
|
if (!tag) |
|
|
|
{ |
|
|
|
int tmp = codec_get_tag(codec_wav_tags, track->enc->codec_id); |
|
|
|
tag = MKTAG('m', 's', ((tmp >> 8) & 0xff), (tmp & 0xff)); |
|
|
|
} |
|
|
|
put_le32(pb, tag); // store it byteswapped |
|
|
|
|
|
|
|
put_le32(pb, track->tag); // store it byteswapped |
|
|
|
put_be32(pb, 0); /* Reserved */ |
|
|
|
put_be16(pb, 0); /* Reserved */ |
|
|
|
put_be16(pb, 1); /* Data-reference index, XXX == 1 */ |
|
|
|
@@ -303,20 +395,17 @@ static int mov_write_audio_tag(ByteIOContext *pb, MOVTrack* track) |
|
|
|
put_be16(pb, 0x10); /* Reserved */ |
|
|
|
|
|
|
|
if(track->enc->codec_id == CODEC_ID_AAC || |
|
|
|
track->enc->codec_id == CODEC_ID_MP3) |
|
|
|
{ |
|
|
|
track->enc->codec_id == CODEC_ID_MP3) { |
|
|
|
put_be16(pb, 0xfffe); /* compression ID (vbr)*/ |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
else { |
|
|
|
put_be16(pb, 0); /* compression ID (= 0) */ |
|
|
|
} |
|
|
|
put_be16(pb, 0); /* packet size (= 0) */ |
|
|
|
put_be16(pb, track->timescale); /* Time scale */ |
|
|
|
put_be16(pb, 0); /* Reserved */ |
|
|
|
|
|
|
|
if(track->mode == MODE_MOV && track->enc->codec_id == CODEC_ID_AAC) |
|
|
|
{ |
|
|
|
if(track->mode == MODE_MOV && track->enc->codec_id == CODEC_ID_AAC) { |
|
|
|
/* SoundDescription V1 extended info */ |
|
|
|
put_be32(pb, track->enc->frame_size); /* Samples per packet */ |
|
|
|
put_be32(pb, 1536); /* Bytes per packet */ |
|
|
|
@@ -467,111 +556,6 @@ static int mov_write_avcc_tag(ByteIOContext *pb, MOVTrack *track) |
|
|
|
return updateSize(pb, pos); |
|
|
|
} |
|
|
|
|
|
|
|
static unsigned int descrLength(unsigned int len) |
|
|
|
{ |
|
|
|
if (len < 0x00000080) |
|
|
|
return 2 + len; |
|
|
|
else if (len < 0x00004000) |
|
|
|
return 3 + len; |
|
|
|
else if(len < 0x00200000) |
|
|
|
return 4 + len; |
|
|
|
else |
|
|
|
return 5 + len; |
|
|
|
} |
|
|
|
|
|
|
|
static void putDescr(ByteIOContext *pb, int tag, int size) |
|
|
|
{ |
|
|
|
uint32_t len; |
|
|
|
uint8_t vals[4]; |
|
|
|
|
|
|
|
len = size; |
|
|
|
vals[3] = (uint8_t)(len & 0x7f); |
|
|
|
len >>= 7; |
|
|
|
vals[2] = (uint8_t)((len & 0x7f) | 0x80); |
|
|
|
len >>= 7; |
|
|
|
vals[1] = (uint8_t)((len & 0x7f) | 0x80); |
|
|
|
len >>= 7; |
|
|
|
vals[0] = (uint8_t)((len & 0x7f) | 0x80); |
|
|
|
|
|
|
|
put_byte(pb, tag); // DescriptorTag |
|
|
|
|
|
|
|
if (size < 0x00000080) |
|
|
|
{ |
|
|
|
put_byte(pb, vals[3]); |
|
|
|
} |
|
|
|
else if (size < 0x00004000) |
|
|
|
{ |
|
|
|
put_byte(pb, vals[2]); |
|
|
|
put_byte(pb, vals[3]); |
|
|
|
} |
|
|
|
else if (size < 0x00200000) |
|
|
|
{ |
|
|
|
put_byte(pb, vals[1]); |
|
|
|
put_byte(pb, vals[2]); |
|
|
|
put_byte(pb, vals[3]); |
|
|
|
} |
|
|
|
else if (size < 0x10000000) |
|
|
|
{ |
|
|
|
put_byte(pb, vals[0]); |
|
|
|
put_byte(pb, vals[1]); |
|
|
|
put_byte(pb, vals[2]); |
|
|
|
put_byte(pb, vals[3]); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static int mov_write_esds_tag(ByteIOContext *pb, MOVTrack* track) // Basic |
|
|
|
{ |
|
|
|
int decoderSpecificInfoLen; |
|
|
|
offset_t pos = url_ftell(pb); |
|
|
|
|
|
|
|
decoderSpecificInfoLen = track->vosLen ? descrLength(track->vosLen):0; |
|
|
|
|
|
|
|
put_be32(pb, 0); // size |
|
|
|
put_tag(pb, "esds"); |
|
|
|
put_be32(pb, 0); // Version |
|
|
|
|
|
|
|
// ES descriptor |
|
|
|
putDescr(pb, 0x03, 3 + descrLength(13 + decoderSpecificInfoLen) + |
|
|
|
descrLength(1)); |
|
|
|
put_be16(pb, track->trackID); |
|
|
|
put_byte(pb, 0x00); // flags (= no flags) |
|
|
|
|
|
|
|
// DecoderConfig descriptor |
|
|
|
putDescr(pb, 0x04, 13 + decoderSpecificInfoLen); |
|
|
|
|
|
|
|
// Object type indication |
|
|
|
put_byte(pb, codec_get_tag(ff_mov_obj_type, track->enc->codec_id)); |
|
|
|
|
|
|
|
// the following fields is made of 6 bits to identify the streamtype (4 for video, 5 for audio) |
|
|
|
// plus 1 bit to indicate upstream and 1 bit set to 1 (reserved) |
|
|
|
if(track->enc->codec_type == CODEC_TYPE_AUDIO) |
|
|
|
put_byte(pb, 0x15); // flags (= Audiostream) |
|
|
|
else |
|
|
|
put_byte(pb, 0x11); // flags (= Visualstream) |
|
|
|
|
|
|
|
put_byte(pb, track->enc->rc_buffer_size>>(3+16)); // Buffersize DB (24 bits) |
|
|
|
put_be16(pb, (track->enc->rc_buffer_size>>3)&0xFFFF); // Buffersize DB |
|
|
|
|
|
|
|
put_be32(pb, FFMAX(track->enc->bit_rate, track->enc->rc_max_rate)); // maxbitrate (FIXME should be max rate in any 1 sec window) |
|
|
|
if(track->enc->rc_max_rate != track->enc->rc_min_rate || track->enc->rc_min_rate==0) |
|
|
|
put_be32(pb, 0); // vbr |
|
|
|
else |
|
|
|
put_be32(pb, track->enc->rc_max_rate); // avg bitrate |
|
|
|
|
|
|
|
if (track->vosLen) |
|
|
|
{ |
|
|
|
// DecoderSpecific info descriptor |
|
|
|
putDescr(pb, 0x05, track->vosLen); |
|
|
|
put_buffer(pb, track->vosData, track->vosLen); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// SL descriptor |
|
|
|
putDescr(pb, 0x06, 1); |
|
|
|
put_byte(pb, 0x02); |
|
|
|
return updateSize (pb, pos); |
|
|
|
} |
|
|
|
|
|
|
|
static const CodecTag codec_movvideo_tags[] = { |
|
|
|
{ CODEC_ID_SVQ1, MKTAG('S', 'V', 'Q', '1') }, |
|
|
|
{ CODEC_ID_SVQ3, MKTAG('S', 'V', 'Q', '3') }, |
|
|
|
@@ -587,11 +571,9 @@ static const CodecTag codec_movvideo_tags[] = { |
|
|
|
{ CODEC_ID_NONE, 0 }, |
|
|
|
}; |
|
|
|
|
|
|
|
static int mov_find_video_codec_tag(MOVTrack* track) |
|
|
|
static int mov_find_video_codec_tag(AVFormatContext *s, MOVTrack *track) |
|
|
|
{ |
|
|
|
int tag; |
|
|
|
|
|
|
|
tag = track->enc->codec_tag; |
|
|
|
int tag = track->enc->codec_tag; |
|
|
|
if (!tag) { |
|
|
|
if (track->enc->codec_id == CODEC_ID_DVVIDEO) { |
|
|
|
if (track->enc->height == 480) { /* NTSC */ |
|
|
|
@@ -612,8 +594,30 @@ static int mov_find_video_codec_tag(MOVTrack* track) |
|
|
|
} |
|
|
|
} |
|
|
|
// if no mac fcc found, try with Microsoft tags |
|
|
|
if (!tag) |
|
|
|
if (!tag) { |
|
|
|
tag = codec_get_tag(codec_bmp_tags, track->enc->codec_id); |
|
|
|
if (tag) { |
|
|
|
av_log(s, AV_LOG_INFO, "Warning, using MS style video codec tag, the file may be unplayable!\n"); |
|
|
|
} |
|
|
|
} |
|
|
|
assert(tag); |
|
|
|
return tag; |
|
|
|
} |
|
|
|
|
|
|
|
static int mov_find_audio_codec_tag(AVFormatContext *s, MOVTrack *track) |
|
|
|
{ |
|
|
|
int tag = track->enc->codec_tag; |
|
|
|
if (!tag) { |
|
|
|
tag = codec_get_tag(codec_movaudio_tags, track->enc->codec_id); |
|
|
|
} |
|
|
|
// if no mac fcc found, try with Microsoft tags |
|
|
|
if (!tag) { |
|
|
|
int ms_tag = codec_get_tag(codec_wav_tags, track->enc->codec_id); |
|
|
|
if (ms_tag) { |
|
|
|
tag = MKTAG('m', 's', ((ms_tag >> 8) & 0xff), (ms_tag & 0xff)); |
|
|
|
av_log(s, AV_LOG_INFO, "Warning, using MS style audio codec tag, the file may be unplayable!\n"); |
|
|
|
} |
|
|
|
} |
|
|
|
assert(tag); |
|
|
|
return tag; |
|
|
|
} |
|
|
|
@@ -622,13 +626,9 @@ static int mov_write_video_tag(ByteIOContext *pb, MOVTrack* track) |
|
|
|
{ |
|
|
|
offset_t pos = url_ftell(pb); |
|
|
|
char compressor_name[32]; |
|
|
|
int tag; |
|
|
|
|
|
|
|
put_be32(pb, 0); /* size */ |
|
|
|
|
|
|
|
tag = mov_find_video_codec_tag(track); |
|
|
|
put_le32(pb, tag); // store it byteswapped |
|
|
|
|
|
|
|
put_le32(pb, track->tag); // store it byteswapped |
|
|
|
put_be32(pb, 0); /* Reserved */ |
|
|
|
put_be16(pb, 0); /* Reserved */ |
|
|
|
put_be16(pb, 1); /* Data-reference index */ |
|
|
|
@@ -1583,30 +1583,19 @@ static int mov_write_header(AVFormatContext *s) |
|
|
|
} |
|
|
|
|
|
|
|
for(i=0; i<s->nb_streams; i++){ |
|
|
|
AVCodecContext *c= s->streams[i]->codec; |
|
|
|
|
|
|
|
if(c->codec_type == CODEC_TYPE_VIDEO){ |
|
|
|
av_set_pts_info(s->streams[i], 64, 1, c->time_base.den); |
|
|
|
if (!codec_get_tag(codec_movvideo_tags, c->codec_id)){ |
|
|
|
if(!codec_get_tag(codec_bmp_tags, c->codec_id)) |
|
|
|
return -1; |
|
|
|
else |
|
|
|
av_log(s, AV_LOG_INFO, "Warning, using MS style video codec tag, the file may be unplayable!\n"); |
|
|
|
} |
|
|
|
}else if(c->codec_type == CODEC_TYPE_AUDIO){ |
|
|
|
av_set_pts_info(s->streams[i], 64, 1, c->sample_rate); |
|
|
|
if (!codec_get_tag(codec_movaudio_tags, c->codec_id)){ |
|
|
|
if(!codec_get_tag(codec_wav_tags, c->codec_id)) |
|
|
|
return -1; |
|
|
|
else |
|
|
|
av_log(s, AV_LOG_INFO, "Warning, using MS style audio codec tag, the file may be unplayable!\n"); |
|
|
|
} |
|
|
|
AVStream *st= s->streams[i]; |
|
|
|
MOVTrack *track= &mov->tracks[i]; |
|
|
|
|
|
|
|
track->enc = st->codec; |
|
|
|
if(st->codec->codec_type == CODEC_TYPE_VIDEO){ |
|
|
|
track->tag = mov_find_video_codec_tag(s, track); |
|
|
|
av_set_pts_info(st, 64, 1, st->codec->time_base.den); |
|
|
|
}else if(st->codec->codec_type == CODEC_TYPE_AUDIO){ |
|
|
|
track->tag = mov_find_audio_codec_tag(s, track); |
|
|
|
av_set_pts_info(st, 64, 1, st->codec->sample_rate); |
|
|
|
} |
|
|
|
mov->tracks[i].language = ff_mov_iso639_to_lang(s->streams[i]->language, mov->mode != MODE_MOV); |
|
|
|
} |
|
|
|
|
|
|
|
for (i=0; i<MAX_STREAMS; i++) { |
|
|
|
mov->tracks[i].mode = mov->mode; |
|
|
|
track->language = ff_mov_iso639_to_lang(st->language, mov->mode != MODE_MOV); |
|
|
|
track->mode = mov->mode; |
|
|
|
} |
|
|
|
|
|
|
|
mov_write_mdat_tag(pb, mov); |
|
|
|
@@ -1621,8 +1610,8 @@ static int mov_write_packet(AVFormatContext *s, AVPacket *pkt) |
|
|
|
{ |
|
|
|
MOVContext *mov = s->priv_data; |
|
|
|
ByteIOContext *pb = &s->pb; |
|
|
|
AVCodecContext *enc = s->streams[pkt->stream_index]->codec; |
|
|
|
MOVTrack* trk = &mov->tracks[pkt->stream_index]; |
|
|
|
MOVTrack *trk = &mov->tracks[pkt->stream_index]; |
|
|
|
AVCodecContext *enc = trk->enc; |
|
|
|
int cl, id; |
|
|
|
unsigned int samplesInChunk = 0; |
|
|
|
int size= pkt->size; |
|
|
|
@@ -1698,10 +1687,9 @@ static int mov_write_packet(AVFormatContext *s, AVPacket *pkt) |
|
|
|
if(trk->cluster[cl][id].key_frame) |
|
|
|
trk->hasKeyframes++; |
|
|
|
} |
|
|
|
trk->enc = enc; |
|
|
|
trk->entry++; |
|
|
|
trk->sampleCount += samplesInChunk; |
|
|
|
trk->mdat_size += size; |
|
|
|
mov->mdat_size += size; |
|
|
|
|
|
|
|
put_buffer(pb, pkt->data, size); |
|
|
|
|
|
|
|
@@ -1714,26 +1702,20 @@ static int mov_write_trailer(AVFormatContext *s) |
|
|
|
MOVContext *mov = s->priv_data; |
|
|
|
ByteIOContext *pb = &s->pb; |
|
|
|
int res = 0; |
|
|
|
int i; |
|
|
|
uint64_t j; |
|
|
|
int i, j; |
|
|
|
|
|
|
|
offset_t moov_pos = url_ftell(pb); |
|
|
|
|
|
|
|
/* Write size of mdat tag */ |
|
|
|
for (i=0, j=0; i<MAX_STREAMS; i++) { |
|
|
|
if(mov->tracks[i].ents_allocated > 0) { |
|
|
|
j += mov->tracks[i].mdat_size; |
|
|
|
} |
|
|
|
} |
|
|
|
if (j+8 <= UINT32_MAX) { |
|
|
|
if (mov->mdat_size+8 <= UINT32_MAX) { |
|
|
|
url_fseek(pb, mov->mdat_pos, SEEK_SET); |
|
|
|
put_be32(pb, j+8); |
|
|
|
put_be32(pb, mov->mdat_size+8); |
|
|
|
} else { |
|
|
|
/* overwrite 'wide' placeholder atom */ |
|
|
|
url_fseek(pb, mov->mdat_pos - 8, SEEK_SET); |
|
|
|
put_be32(pb, 1); /* special value: real atom size will be 64 bit value after tag field */ |
|
|
|
put_tag(pb, "mdat"); |
|
|
|
put_be64(pb, j+16); |
|
|
|
put_be64(pb, mov->mdat_size+16); |
|
|
|
} |
|
|
|
url_fseek(pb, moov_pos, SEEK_SET); |
|
|
|
|
|
|
|
|