return update_size(pb, pos);
}
+static int mov_write_dmlp_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track)
+{
+ int64_t pos = avio_tell(pb);
+ int length;
+ avio_wb32(pb, 0);
+ ffio_wfourcc(pb, "dmlp");
+
+ if (track->vos_len < 20) {
+ av_log(s, AV_LOG_ERROR,
+ "Cannot write moov atom before TrueHD packets."
+ " Set the delay_moov flag to fix this.\n");
+ return AVERROR(EINVAL);
+ }
+
+ length = (AV_RB16(track->vos_data) & 0xFFF) * 2;
+ if (length < 20 || length > track->vos_len)
+ return AVERROR_INVALIDDATA;
+
+ // Only TrueHD is supported
+ if (AV_RB32(track->vos_data + 4) != 0xF8726FBA)
+ return AVERROR_INVALIDDATA;
+
+ avio_wb32(pb, AV_RB32(track->vos_data + 8)); /* format_info */
+ avio_wb16(pb, AV_RB16(track->vos_data + 18) << 1); /* peak_data_rate */
+ avio_wb32(pb, 0); /* reserved */
+
+ return update_size(pb, pos);
+}
+
static int mov_write_chan_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track)
{
uint32_t layout_tag, bitmap;
avio_wb16(pb, 0); /* packet size (= 0) */
if (track->par->codec_id == AV_CODEC_ID_OPUS)
avio_wb16(pb, 48000);
+ else if (track->par->codec_id == AV_CODEC_ID_TRUEHD)
+ avio_wb32(pb, track->par->sample_rate);
else
avio_wb16(pb, track->par->sample_rate <= UINT16_MAX ?
track->par->sample_rate : 0);
- avio_wb16(pb, 0); /* Reserved */
+
+ if (track->par->codec_id != AV_CODEC_ID_TRUEHD)
+ avio_wb16(pb, 0); /* Reserved */
}
if (version == 1) { /* SoundDescription V1 extended info */
ret = mov_write_dfla_tag(pb, track);
else if (track->par->codec_id == AV_CODEC_ID_OPUS)
ret = mov_write_dops_tag(s, pb, track);
+ else if (track->par->codec_id == AV_CODEC_ID_TRUEHD)
+ ret = mov_write_dmlp_tag(s, pb, track);
else if (track->vos_len > 0)
ret = mov_write_glbl_tag(pb, track);
static int mov_write_colr_tag(AVIOContext *pb, MOVTrack *track)
{
+ int64_t pos = avio_tell(pb);
+
// Ref (MOV): https://developer.apple.com/library/mac/technotes/tn2162/_index.html#//apple_ref/doc/uid/DTS40013070-CH1-TNTAG9
// Ref (MP4): ISO/IEC 14496-12:2012
/* We should only ever be called by MOV or MP4. */
av_assert0(track->mode == MODE_MOV || track->mode == MODE_MP4);
- avio_wb32(pb, 18 + (track->mode == MODE_MP4));
+ avio_wb32(pb, 0); /* size */
ffio_wfourcc(pb, "colr");
if (track->mode == MODE_MP4)
ffio_wfourcc(pb, "nclx");
if (track->mode == MODE_MP4) {
int full_range = track->par->color_range == AVCOL_RANGE_JPEG;
avio_w8(pb, full_range << 7);
- return 19;
- } else {
- return 18;
}
+
+ return update_size(pb, pos);
}
static void find_compressor(char * compressor_name, int len, MOVTrack *track)
return ret;
mov_write_stts_tag(pb, track);
if ((track->par->codec_type == AVMEDIA_TYPE_VIDEO ||
+ track->par->codec_id == AV_CODEC_ID_TRUEHD ||
+ track->par->codec_id == AV_CODEC_ID_MPEGH_3D_AUDIO ||
track->par->codec_tag == MKTAG('r','t','p',' ')) &&
track->has_keyframes && track->has_keyframes < track->entry)
mov_write_stss_tag(pb, track, MOV_SYNC_SAMPLE);
if (mov->mode & (MODE_MP4|MODE_MOV))
mov_write_track_metadata(pb_buf, st, "name", "title");
- if ((size = avio_close_dyn_buf(pb_buf, &buf)) > 0) {
+ if ((size = avio_get_dyn_buf(pb_buf, &buf)) > 0) {
avio_wb32(pb, size + 8);
ffio_wfourcc(pb, "udta");
avio_write(pb, buf, size);
}
- av_free(buf);
+ ffio_free_dyn_buf(&pb_buf);
return 0;
}
if (s->nb_chapters && !(mov->flags & FF_MOV_FLAG_DISABLE_CHPL))
mov_write_chpl_tag(pb_buf, s);
- if ((size = avio_close_dyn_buf(pb_buf, &buf)) > 0) {
+ if ((size = avio_get_dyn_buf(pb_buf, &buf)) > 0) {
avio_wb32(pb, size + 8);
ffio_wfourcc(pb, "udta");
avio_write(pb, buf, size);
}
- av_free(buf);
+ ffio_free_dyn_buf(&pb_buf);
return 0;
}
{
int64_t pos = avio_tell(pb), offset_pos, end_pos;
int64_t presentation_time, duration, offset;
- int starts_with_SAP, i, entries;
+ unsigned starts_with_SAP;
+ int i, entries;
if (track->entry) {
entries = 1;
return 0;
}
-/* TODO: This needs to be more general */
-static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s)
+static void mov_write_ftyp_tag_internal(AVIOContext *pb, AVFormatContext *s,
+ int has_h264, int has_video, int write_minor)
{
MOVMuxContext *mov = s->priv_data;
- int64_t pos = avio_tell(pb);
- int has_h264 = 0, has_video = 0;
int minor = 0x200;
- int i;
-
- for (i = 0; i < s->nb_streams; i++) {
- AVStream *st = s->streams[i];
- if (is_cover_image(st))
- continue;
- if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
- has_video = 1;
- if (st->codecpar->codec_id == AV_CODEC_ID_H264)
- has_h264 = 1;
- }
-
- avio_wb32(pb, 0); /* size */
- ffio_wfourcc(pb, "ftyp");
if (mov->major_brand && strlen(mov->major_brand) >= 4)
ffio_wfourcc(pb, mov->major_brand);
minor = has_h264 ? 0x20000 : 0x10000;
} else if (mov->mode == MODE_PSP)
ffio_wfourcc(pb, "MSNV");
+ else if (mov->mode == MODE_MP4 && mov->flags & FF_MOV_FLAG_FRAGMENT &&
+ mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS)
+ ffio_wfourcc(pb, "iso6"); // Required when using signed CTS offsets in trun boxes
else if (mov->mode == MODE_MP4 && mov->flags & FF_MOV_FLAG_DEFAULT_BASE_MOOF)
ffio_wfourcc(pb, "iso5"); // Required when using default-base-is-moof
else if (mov->mode == MODE_MP4 && mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS)
else
ffio_wfourcc(pb, "qt ");
- avio_wb32(pb, minor);
+ if (write_minor)
+ avio_wb32(pb, minor);
+}
+
+static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s)
+{
+ MOVMuxContext *mov = s->priv_data;
+ int64_t pos = avio_tell(pb);
+ int has_h264 = 0, has_video = 0;
+ int i;
- if (mov->mode == MODE_MOV)
- ffio_wfourcc(pb, "qt ");
- else if (mov->mode == MODE_ISM) {
- ffio_wfourcc(pb, "piff");
- } else if (!(mov->flags & FF_MOV_FLAG_DEFAULT_BASE_MOOF)) {
- ffio_wfourcc(pb, "isom");
- ffio_wfourcc(pb, "iso2");
- if (has_h264)
- ffio_wfourcc(pb, "avc1");
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st = s->streams[i];
+ if (is_cover_image(st))
+ continue;
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+ has_video = 1;
+ if (st->codecpar->codec_id == AV_CODEC_ID_H264)
+ has_h264 = 1;
}
- // We add tfdt atoms when fragmenting, signal this with the iso6 compatible
- // brand. This is compatible with users that don't understand tfdt.
- if (mov->flags & FF_MOV_FLAG_FRAGMENT && mov->mode != MODE_ISM)
- ffio_wfourcc(pb, "iso6");
+ avio_wb32(pb, 0); /* size */
+ ffio_wfourcc(pb, "ftyp");
- if (mov->mode == MODE_3GP)
- ffio_wfourcc(pb, has_h264 ? "3gp6":"3gp4");
- else if (mov->mode & MODE_3G2)
- ffio_wfourcc(pb, has_h264 ? "3g2b":"3g2a");
- else if (mov->mode == MODE_PSP)
- ffio_wfourcc(pb, "MSNV");
- else if (mov->mode == MODE_MP4)
+ // Write major brand
+ mov_write_ftyp_tag_internal(pb, s, has_h264, has_video, 1);
+ // Write the major brand as the first compatible brand as well
+ mov_write_ftyp_tag_internal(pb, s, has_h264, has_video, 0);
+
+ // Write compatible brands, ensuring that we don't write the major brand as a
+ // compatible brand a second time.
+ if (mov->mode == MODE_ISM) {
+ ffio_wfourcc(pb, "piff");
+ } else if (mov->mode != MODE_MOV) {
+ // We add tfdt atoms when fragmenting, signal this with the iso6 compatible
+ // brand, if not already the major brand. This is compatible with users that
+ // don't understand tfdt.
+ if (mov->mode == MODE_MP4) {
+ if (mov->flags & FF_MOV_FLAG_FRAGMENT && !(mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS))
+ ffio_wfourcc(pb, "iso6");
+ } else {
+ if (mov->flags & FF_MOV_FLAG_FRAGMENT)
+ ffio_wfourcc(pb, "iso6");
+ if (mov->flags & FF_MOV_FLAG_DEFAULT_BASE_MOOF)
+ ffio_wfourcc(pb, "iso5");
+ else if (mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS)
+ ffio_wfourcc(pb, "iso4");
+ }
+ // Brands prior to iso5 can't be signaled when using default-base-is-moof
+ if (!(mov->flags & FF_MOV_FLAG_DEFAULT_BASE_MOOF)) {
+ // write isom for mp4 only if it it's not the major brand already.
+ if (mov->mode != MODE_MP4 || mov->flags & FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS)
+ ffio_wfourcc(pb, "isom");
+ ffio_wfourcc(pb, "iso2");
+ if (has_h264)
+ ffio_wfourcc(pb, "avc1");
+ }
+ }
+
+ if (mov->mode == MODE_MP4)
ffio_wfourcc(pb, "mp41");
if (mov->flags & FF_MOV_FLAG_DASH && mov->flags & FF_MOV_FLAG_GLOBAL_SIDX)
}
}
+static void mov_parse_truehd_frame(AVPacket *pkt, MOVTrack *trk)
+{
+ int length;
+
+ if (pkt->size < 8)
+ return;
+
+ length = (AV_RB16(pkt->data) & 0xFFF) * 2;
+ if (length < 8 || length > pkt->size)
+ return;
+
+ if (AV_RB32(pkt->data + 4) == 0xF8726FBA) {
+ trk->cluster[trk->entry].flags |= MOV_SYNC_SAMPLE;
+ trk->has_keyframes++;
+ }
+
+ return;
+}
+
static int mov_flush_fragment_interleaving(AVFormatContext *s, MOVTrack *track)
{
MOVMuxContext *mov = s->priv_data;
if ((ret = avio_open_dyn_buf(&mov->mdat_buf)) < 0)
return ret;
}
- buf_size = avio_close_dyn_buf(track->mdat_buf, &buf);
- track->mdat_buf = NULL;
+ buf_size = avio_get_dyn_buf(track->mdat_buf, &buf);
offset = avio_tell(mov->mdat_buf);
avio_write(mov->mdat_buf, buf, buf_size);
- av_free(buf);
+ ffio_free_dyn_buf(&track->mdat_buf);
for (i = track->entries_flushed; i < track->entry; i++)
track->cluster[i].pos += offset;
return 0;
}
- buf_size = avio_close_dyn_buf(mov->mdat_buf, &buf);
- mov->mdat_buf = NULL;
+ buf_size = avio_get_dyn_buf(mov->mdat_buf, &buf);
avio_wb32(s->pb, buf_size + 8);
ffio_wfourcc(s->pb, "mdat");
avio_write(s->pb, buf, buf_size);
- av_free(buf);
+ ffio_free_dyn_buf(&mov->mdat_buf);
if (mov->flags & FF_MOV_FLAG_GLOBAL_SIDX)
mov->reserved_header_pos = avio_tell(s->pb);
!TAG_IS_AVCI(trk->tag) &&
(par->codec_id != AV_CODEC_ID_DNXHD)) {
trk->vos_len = par->extradata_size;
- trk->vos_data = av_malloc(trk->vos_len);
+ trk->vos_data = av_malloc(trk->vos_len + AV_INPUT_BUFFER_PADDING_SIZE);
if (!trk->vos_data) {
ret = AVERROR(ENOMEM);
goto err;
}
memcpy(trk->vos_data, par->extradata, trk->vos_len);
+ memset(trk->vos_data + trk->vos_len, 0, AV_INPUT_BUFFER_PADDING_SIZE);
}
if (par->codec_id == AV_CODEC_ID_AAC && pkt->size > 2 &&
}
if ((par->codec_id == AV_CODEC_ID_DNXHD ||
+ par->codec_id == AV_CODEC_ID_TRUEHD ||
par->codec_id == AV_CODEC_ID_AC3) && !trk->vos_len) {
/* copy frame to create needed atoms */
trk->vos_len = size;
- trk->vos_data = av_malloc(size);
+ trk->vos_data = av_malloc(size + AV_INPUT_BUFFER_PADDING_SIZE);
if (!trk->vos_data) {
ret = AVERROR(ENOMEM);
goto err;
}
memcpy(trk->vos_data, pkt->data, size);
+ memset(trk->vos_data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
}
if (trk->entry >= trk->cluster_capacity) {
if (par->codec_id == AV_CODEC_ID_VC1) {
mov_parse_vc1_frame(pkt, trk);
+ } else if (par->codec_id == AV_CODEC_ID_TRUEHD) {
+ mov_parse_truehd_frame(pkt, trk);
} else if (pkt->flags & AV_PKT_FLAG_KEY) {
if (mov->mode == MODE_MOV && par->codec_id == AV_CODEC_ID_MPEG2VIDEO &&
trk->entry > 0) { // force sync sample for the first key frame
cur += strspn(cur, "\n\r");
}
if (have_palette) {
- track->vos_data = av_malloc(16*4);
+ track->vos_data = av_malloc(16*4 + AV_INPUT_BUFFER_PADDING_SIZE);
if (!track->vos_data)
return AVERROR(ENOMEM);
for (i = 0; i < 16; i++) {
AV_WB32(track->vos_data + i * 4, palette[i]);
}
+ memset(track->vos_data + 16*4, 0, AV_INPUT_BUFFER_PADDING_SIZE);
track->vos_len = 16 * 4;
}
st->codecpar->width = width;
track->par = st->codecpar;
track->language = ff_mov_iso639_to_lang(lang?lang->value:"und", mov->mode!=MODE_MOV);
if (track->language < 0)
- track->language = 0;
+ track->language = 32767; // Unspecified Macintosh language code
track->mode = mov->mode;
track->tag = mov_find_codec_tag(s, track);
if (!track->tag) {
}
}
if (track->par->codec_id == AV_CODEC_ID_FLAC ||
+ track->par->codec_id == AV_CODEC_ID_TRUEHD ||
track->par->codec_id == AV_CODEC_ID_OPUS) {
if (track->mode != MODE_MP4) {
av_log(s, AV_LOG_ERROR, "%s only supported in MP4.\n", avcodec_get_name(track->par->codec_id));
mov_create_dvd_sub_decoder_specific_info(track, st);
else if (!TAG_IS_AVCI(track->tag) && st->codecpar->codec_id != AV_CODEC_ID_DNXHD) {
track->vos_len = st->codecpar->extradata_size;
- track->vos_data = av_malloc(track->vos_len);
+ track->vos_data = av_malloc(track->vos_len + AV_INPUT_BUFFER_PADDING_SIZE);
if (!track->vos_data) {
return AVERROR(ENOMEM);
}
memcpy(track->vos_data, st->codecpar->extradata, track->vos_len);
+ memset(track->vos_data + track->vos_len, 0, AV_INPUT_BUFFER_PADDING_SIZE);
}
}
!(mov->flags & FF_MOV_FLAG_DELAY_MOOV)) {
if ((ret = mov_write_moov_tag(pb, mov, s)) < 0)
return ret;
- avio_flush(pb);
mov->moov_written = 1;
if (mov->flags & FF_MOV_FLAG_GLOBAL_SIDX)
mov->reserved_header_pos = avio_tell(pb);
AVCodecParameters *par = track->par;
track->vos_len = par->extradata_size;
- track->vos_data = av_malloc(track->vos_len);
+ track->vos_data = av_malloc(track->vos_len + AV_INPUT_BUFFER_PADDING_SIZE);
if (!track->vos_data)
return AVERROR(ENOMEM);
memcpy(track->vos_data, par->extradata, track->vos_len);
+ memset(track->vos_data + track->vos_len, 0, AV_INPUT_BUFFER_PADDING_SIZE);
}
mov->need_rewrite_extradata = 0;
}
};
const AVCodecTag codec_mp4_tags[] = {
- { AV_CODEC_ID_MPEG4 , MKTAG('m', 'p', '4', 'v') },
- { AV_CODEC_ID_H264 , MKTAG('a', 'v', 'c', '1') },
- { AV_CODEC_ID_H264 , MKTAG('a', 'v', 'c', '3') },
- { AV_CODEC_ID_HEVC , MKTAG('h', 'e', 'v', '1') },
- { AV_CODEC_ID_HEVC , MKTAG('h', 'v', 'c', '1') },
- { AV_CODEC_ID_MPEG2VIDEO , MKTAG('m', 'p', '4', 'v') },
- { AV_CODEC_ID_MPEG1VIDEO , MKTAG('m', 'p', '4', 'v') },
- { AV_CODEC_ID_MJPEG , MKTAG('m', 'p', '4', 'v') },
- { AV_CODEC_ID_PNG , MKTAG('m', 'p', '4', 'v') },
- { AV_CODEC_ID_JPEG2000 , MKTAG('m', 'p', '4', 'v') },
- { AV_CODEC_ID_VC1 , MKTAG('v', 'c', '-', '1') },
- { AV_CODEC_ID_DIRAC , MKTAG('d', 'r', 'a', 'c') },
- { AV_CODEC_ID_TSCC2 , MKTAG('m', 'p', '4', 'v') },
- { AV_CODEC_ID_VP9 , MKTAG('v', 'p', '0', '9') },
- { AV_CODEC_ID_AV1 , MKTAG('a', 'v', '0', '1') },
- { AV_CODEC_ID_AAC , MKTAG('m', 'p', '4', 'a') },
- { AV_CODEC_ID_MP4ALS , MKTAG('m', 'p', '4', 'a') },
- { AV_CODEC_ID_MP3 , MKTAG('m', 'p', '4', 'a') },
- { AV_CODEC_ID_MP2 , MKTAG('m', 'p', '4', 'a') },
- { AV_CODEC_ID_AC3 , MKTAG('a', 'c', '-', '3') },
- { AV_CODEC_ID_EAC3 , MKTAG('e', 'c', '-', '3') },
- { AV_CODEC_ID_DTS , MKTAG('m', 'p', '4', 'a') },
- { AV_CODEC_ID_FLAC , MKTAG('f', 'L', 'a', 'C') },
- { AV_CODEC_ID_OPUS , MKTAG('O', 'p', 'u', 's') },
- { AV_CODEC_ID_VORBIS , MKTAG('m', 'p', '4', 'a') },
- { AV_CODEC_ID_QCELP , MKTAG('m', 'p', '4', 'a') },
- { AV_CODEC_ID_EVRC , MKTAG('m', 'p', '4', 'a') },
- { AV_CODEC_ID_DVD_SUBTITLE, MKTAG('m', 'p', '4', 's') },
- { AV_CODEC_ID_MOV_TEXT , MKTAG('t', 'x', '3', 'g') },
- { AV_CODEC_ID_BIN_DATA , MKTAG('g', 'p', 'm', 'd') },
- { AV_CODEC_ID_NONE , 0 },
+ { AV_CODEC_ID_MPEG4, MKTAG('m', 'p', '4', 'v') },
+ { AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '1') },
+ { AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '3') },
+ { AV_CODEC_ID_HEVC, MKTAG('h', 'e', 'v', '1') },
+ { AV_CODEC_ID_HEVC, MKTAG('h', 'v', 'c', '1') },
+ { AV_CODEC_ID_MPEG2VIDEO, MKTAG('m', 'p', '4', 'v') },
+ { AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', '4', 'v') },
+ { AV_CODEC_ID_MJPEG, MKTAG('m', 'p', '4', 'v') },
+ { AV_CODEC_ID_PNG, MKTAG('m', 'p', '4', 'v') },
+ { AV_CODEC_ID_JPEG2000, MKTAG('m', 'p', '4', 'v') },
+ { AV_CODEC_ID_VC1, MKTAG('v', 'c', '-', '1') },
+ { AV_CODEC_ID_DIRAC, MKTAG('d', 'r', 'a', 'c') },
+ { AV_CODEC_ID_TSCC2, MKTAG('m', 'p', '4', 'v') },
+ { AV_CODEC_ID_VP9, MKTAG('v', 'p', '0', '9') },
+ { AV_CODEC_ID_AV1, MKTAG('a', 'v', '0', '1') },
+ { AV_CODEC_ID_AAC, MKTAG('m', 'p', '4', 'a') },
+ { AV_CODEC_ID_MP4ALS, MKTAG('m', 'p', '4', 'a') },
+ { AV_CODEC_ID_MP3, MKTAG('m', 'p', '4', 'a') },
+ { AV_CODEC_ID_MP2, MKTAG('m', 'p', '4', 'a') },
+ { AV_CODEC_ID_AC3, MKTAG('a', 'c', '-', '3') },
+ { AV_CODEC_ID_EAC3, MKTAG('e', 'c', '-', '3') },
+ { AV_CODEC_ID_DTS, MKTAG('m', 'p', '4', 'a') },
+ { AV_CODEC_ID_TRUEHD, MKTAG('m', 'l', 'p', 'a') },
+ { AV_CODEC_ID_FLAC, MKTAG('f', 'L', 'a', 'C') },
+ { AV_CODEC_ID_OPUS, MKTAG('O', 'p', 'u', 's') },
+ { AV_CODEC_ID_VORBIS, MKTAG('m', 'p', '4', 'a') },
+ { AV_CODEC_ID_QCELP, MKTAG('m', 'p', '4', 'a') },
+ { AV_CODEC_ID_EVRC, MKTAG('m', 'p', '4', 'a') },
+ { AV_CODEC_ID_DVD_SUBTITLE, MKTAG('m', 'p', '4', 's') },
+ { AV_CODEC_ID_MOV_TEXT, MKTAG('t', 'x', '3', 'g') },
+ { AV_CODEC_ID_BIN_DATA, MKTAG('g', 'p', 'm', 'd') },
+ { AV_CODEC_ID_MPEGH_3D_AUDIO, MKTAG('m', 'h', 'm', '1') },
+ { AV_CODEC_ID_NONE, 0 },
};
const AVCodecTag codec_ism_tags[] = {