@@ -1348,6 +1348,15 @@ typedef struct AVCPBProperties {
* Types and functions for working with AVPacket.
* @{
*/
+
+typedef struct AVTrackReferences {
+ int next_tref_ofs; /** offset in bytes to the next AVTrackReferences or 0 if this is the last one*/
+ char tag[4]; /** 4cc used for describing this */
+ int nb_tracks; /** number of tracks */
+ int tracks[1]; /** tracks this track refers to (contains nb_tracks entries) */
+ /** followed by an optional gap for alignment purposes and another AVTrackReferences is applicaple */
+} AVTrackReferences;
+
enum AVPacketSideDataType {
AV_PKT_DATA_PALETTE,
@@ -1525,7 +1534,15 @@ enum AVPacketSideDataType {
* should be associated with a video stream and containts data in the form
* of the AVMasteringDisplayMetadata struct.
*/
- AV_PKT_DATA_MASTERING_DISPLAY_METADATA
+ AV_PKT_DATA_MASTERING_DISPLAY_METADATA,
+
+ /**
+ * Define track references (in particular applicaple for ISO MP4
+ * files). The data is a sequence of type AVTrackReferences
+ * (including the track list that follows it), for as long as
+ * indicated by the key's length.
+ */
+ AV_PKT_DATA_TRACK_REFERENCES,
};
#define AV_PKT_DATA_QUALITY_FACTOR AV_PKT_DATA_QUALITY_STATS //DEPRECATED
@@ -280,10 +280,10 @@ OBJS-$(CONFIG_MLV_DEMUXER) += mlvdec.o riffdec.o
OBJS-$(CONFIG_MM_DEMUXER) += mm.o
OBJS-$(CONFIG_MMF_DEMUXER) += mmf.o
OBJS-$(CONFIG_MMF_MUXER) += mmf.o rawenc.o
-OBJS-$(CONFIG_MOV_DEMUXER) += mov.o mov_chan.o replaygain.o
+OBJS-$(CONFIG_MOV_DEMUXER) += mov.o mov_chan.o replaygain.o movtref.o
OBJS-$(CONFIG_MOV_MUXER) += movenc.o avc.o hevc.o vpcc.o \
movenchint.o mov_chan.o rtp.o \
- movenccenc.o rawutils.o
+ movenccenc.o rawutils.o movtref.o
OBJS-$(CONFIG_MP2_MUXER) += mp3enc.o rawenc.o id3v2enc.o
OBJS-$(CONFIG_MP3_DEMUXER) += mp3dec.o replaygain.o
OBJS-$(CONFIG_MP3_MUXER) += mp3enc.o rawenc.o id3v2enc.o
@@ -2631,14 +2631,22 @@ static int mov_write_edts_tag(AVIOContext *pb, MOVMuxContext *mov,
return size;
}
-static int mov_write_tref_tag(AVIOContext *pb, MOVTrack *track)
+static int mov_write_tref_tag(AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track)
{
- avio_wb32(pb, 20); // size
+ int64_t pos = avio_tell(pb);
+ int i, j;
+ avio_wb32(pb, 0); // size
ffio_wfourcc(pb, "tref");
- avio_wb32(pb, 12); // size (subatom)
- avio_wl32(pb, track->tref_tag);
- avio_wb32(pb, track->tref_id);
- return 20;
+ for (j = 0; j < track->trefs.nb_trefs; ++j) {
+ int64_t pos_sub = avio_tell(pb);
+ MOVTRef* tref = &track->trefs.trefs[j];
+ avio_wb32(pb, 0); // size (subatom)
+ avio_wl32(pb, tref->tag);
+ for (i = 0; i < tref->nb_track_ids; i++)
+ avio_wb32(pb, tref->track_ids[i]);
+ update_size(pb, pos_sub);
+ }
+ return update_size(pb, pos);
}
// goes at the end of each track! ... Critical for PSP playback ("Incompatible data" without it)
@@ -2749,8 +2757,8 @@ static int mov_write_trak_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContext
"Not writing any edit list even though one would have been required\n");
}
- if (track->tref_tag)
- mov_write_tref_tag(pb, track);
+ if (track->trefs.nb_trefs)
+ mov_write_tref_tag(pb, mov, track);
if ((ret = mov_write_mdia_tag(s, pb, mov, track)) < 0)
return ret;
@@ -3487,16 +3495,82 @@ static int mov_setup_track_ids(MOVMuxContext *mov, AVFormatContext *s)
return 0;
}
+static int mov_tref_copy_from_side_data(MOVMuxContext *mov, MOVTrack *track, AVFormatContext *s)
+{
+ int size;
+ int ret;
+ int i;
+ MOVTRef *tref;
+ int *ref_tracks = NULL;
+
+ char *ptr = (void*) av_stream_get_side_data(track->st,
+ AV_PKT_DATA_TRACK_REFERENCES,
+ &size);
+
+ if (!ptr)
+ return 0;
+
+ ret = 0;
+ while (ret == 0 && size >= sizeof(AVTrackReferences)) {
+ AVTrackReferences refs;
+ int *track_ids;
+
+ // avoid any potential alignment issues by copying the struct before accessing it
+ // to a well-aligned address
+ memcpy(&refs, ptr, sizeof(refs));
+ // as well as the ids
+ ref_tracks = av_malloc_array(refs.nb_tracks, sizeof(refs.tracks[0]));
+ memcpy(ref_tracks, &((AVTrackReferences*) ptr)->tracks, sizeof(refs.tracks[0]) * refs.nb_tracks);
+
+ ret = ff_mov_tref_find_or_add(&track->trefs,
+ MKTAG(refs.tag[0], refs.tag[1], refs.tag[2], refs.tag[3]),
+ &tref);
+ if (ret < 0)
+ goto error;
+
+ ret = ff_mov_tref_alloc(tref, refs.nb_tracks, &track_ids);
+ if (ret < 0)
+ goto error;
+
+ for (i = 0; i < refs.nb_tracks; i++) {
+ int tref_stream_id = ref_tracks[i];
+ int stream_idx;
+ for (stream_idx = 0; stream_idx < mov->nb_streams; ++stream_idx)
+ if (mov->tracks[stream_idx].st &&
+ mov->tracks[stream_idx].st->id == tref_stream_id) {
+ track_ids[i] = mov->tracks[stream_idx].track_id;
+ break;
+ }
+ }
+
+ size -= refs.next_tref_ofs;
+ ptr += refs.next_tref_ofs;
+
+ av_free(ref_tracks);
+ if (refs.next_tref_ofs == 0)
+ break;
+ }
+ return 0;
+error:
+ av_free(ref_tracks);
+ return ret;
+}
+
static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov,
AVFormatContext *s)
{
int i;
int64_t pos = avio_tell(pb);
+ int ret;
avio_wb32(pb, 0); /* size placeholder*/
ffio_wfourcc(pb, "moov");
mov_setup_track_ids(mov, s);
+ for (i = 0; i < mov->nb_streams; i++)
+ if (mov->tracks[i].st)
+ mov_tref_copy_from_side_data(mov, &mov->tracks[i], s);
+
for (i = 0; i < mov->nb_streams; i++) {
if (mov->tracks[i].entry <= 0 && !(mov->flags & FF_MOV_FLAG_FRAGMENT))
continue;
@@ -3508,34 +3582,42 @@ static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov,
}
if (mov->chapter_track)
- for (i = 0; i < s->nb_streams; i++) {
- mov->tracks[i].tref_tag = MKTAG('c','h','a','p');
- mov->tracks[i].tref_id = mov->tracks[mov->chapter_track].track_id;
- }
+ for (i = 0; i < s->nb_streams; i++)
+ if (!ff_codec_get_id(ff_codec_metadata_tags, mov->tracks[i].tag)) {
+ ret = ff_mov_tref_add_one_track(&mov->tracks[i].trefs, MKTAG('c','h','a','p'), mov->chapter_track);
+ if (ret < 0)
+ return ret;
+ }
for (i = 0; i < mov->nb_streams; i++) {
MOVTrack *track = &mov->tracks[i];
if (track->tag == MKTAG('r','t','p',' ')) {
- track->tref_tag = MKTAG('h','i','n','t');
- track->tref_id = mov->tracks[track->src_track].track_id;
+ ret = ff_mov_tref_add_one_track(&mov->tracks[i].trefs, MKTAG('h','i','n','t'), mov->tracks[track->src_track].track_id);
+ if (ret < 0)
+ return ret;
} else if (track->par->codec_type == AVMEDIA_TYPE_AUDIO) {
int * fallback, size;
fallback = (int*)av_stream_get_side_data(track->st,
AV_PKT_DATA_FALLBACK_TRACK,
&size);
- if (fallback != NULL && size == sizeof(int)) {
- if (*fallback >= 0 && *fallback < mov->nb_streams) {
- track->tref_tag = MKTAG('f','a','l','l');
- track->tref_id = mov->tracks[*fallback].track_id;
- }
+ if (fallback != NULL && size == sizeof(int) &&
+ *fallback >= 0 && *fallback < mov->nb_streams) {
+ ret = ff_mov_tref_add_one_track(&track->trefs, MKTAG('f','a','l','l'), *fallback);
+ if (ret < 0)
+ return ret;
}
}
}
for (i = 0; i < mov->nb_streams; i++) {
if (mov->tracks[i].tag == MKTAG('t','m','c','d')) {
int src_trk = mov->tracks[i].src_track;
- mov->tracks[src_trk].tref_tag = mov->tracks[i].tag;
- mov->tracks[src_trk].tref_id = mov->tracks[i].track_id;
- //src_trk may have a different timescale than the tmcd track
+
+ ret = ff_mov_tref_add_one_track(&mov->tracks[src_trk].trefs,
+ mov->tracks[i].tag,
+ mov->tracks[i].track_id);
+ if (ret < 0)
+ return ret;
+
+ //src_trk may have a different timescale than the tmcd track
mov->tracks[i].track_duration = av_rescale(mov->tracks[src_trk].track_duration,
mov->tracks[i].timescale,
mov->tracks[src_trk].timescale);
@@ -5346,6 +5428,7 @@ static void mov_free(AVFormatContext *s)
av_freep(&mov->tracks[i].vos_data);
ff_mov_cenc_free(&mov->tracks[i].cenc);
+ ff_mov_tref_free(&mov->tracks[i].trefs);
}
av_freep(&mov->tracks);
@@ -26,6 +26,7 @@
#include "avformat.h"
#include "movenccenc.h"
+#include "movtref.h"
#define MOV_FRAG_INFO_ALLOC_INCREMENT 64
#define MOV_INDEX_CLUSTER_SIZE 1024
@@ -110,8 +111,8 @@ typedef struct MOVTrack {
unsigned cluster_capacity;
int audio_vbr;
int height; ///< active picture (w/o VBI) height for D-10/IMX
- uint32_t tref_tag;
- int tref_id; ///< trackID of the referenced track
+
+ MOVTRefs trefs;
int64_t start_dts;
int64_t start_cts;
int64_t end_pts;
new file mode 100644
@@ -0,0 +1,115 @@
+/*
+ * ISO base media file format track references
+ * Copyright (c) 2016 Erkki Seppälä <erkki.seppala.ext@nokia.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include "movtref.h"
+#include "libavutil/mem.h"
+
+MOVTRef *ff_mov_tref_find(MOVTRefs *trefs, uint32_t tag)
+{
+ int i;
+ MOVTRef *tref = NULL;
+
+ for (i = 0; i < trefs->nb_trefs && !tref; ++i) {
+ if (trefs->trefs[i].tag == tag) {
+ tref = trefs->trefs + i;
+ }
+ }
+
+ return tref;
+}
+
+int ff_mov_tref_alloc(MOVTRef *tref, int n, int **track_ids_ret)
+{
+ int ret = av_reallocp_array(&tref->track_ids, tref->nb_track_ids + n, sizeof(tref->track_ids));
+ *track_ids_ret = NULL;
+ if (ret >= 0) {
+ *track_ids_ret = tref->track_ids + tref->nb_track_ids;
+ tref->nb_track_ids += n;
+ }
+ return ret;
+}
+
+int ff_mov_tref_find_or_add(MOVTRefs *trefs, uint32_t tag, MOVTRef **tref_ret)
+{
+ int ret;
+ int i;
+ MOVTRef *tref = ff_mov_tref_find(trefs, tag);
+ *tref_ret = NULL;
+
+ for (i = 0; i < trefs->nb_trefs && !tref; ++i) {
+ if (trefs->trefs[i].tag == tag) {
+ tref = trefs->trefs + i;
+ }
+ }
+
+ if (!tref) {
+ ret = av_reallocp_array(&trefs->trefs, trefs->nb_trefs + 1, sizeof(*trefs->trefs));
+ if (ret < 0)
+ return ret;
+ tref = trefs->trefs + trefs->nb_trefs;
+ trefs->nb_trefs++;
+ tref->tag = tag;
+ tref->track_ids = NULL;
+ tref->nb_track_ids = 0;
+ }
+
+ *tref_ret = tref;
+ return 0;
+}
+
+int ff_mov_tref_add_one_track(MOVTRefs *trefs, unsigned tag, int track_id)
+{
+ int ret;
+ MOVTRef *tref;
+ int *ids;
+ int track_id_exists = 0;
+ int i;
+
+ ret = ff_mov_tref_find_or_add(trefs, tag, &tref);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < tref->nb_track_ids && !track_id_exists; i++) {
+ if (tref->track_ids[i] == track_id) {
+ track_id_exists = 1;
+ }
+ }
+
+ if (!track_id_exists) {
+ ret = ff_mov_tref_alloc(tref, 1, &ids);
+ if (ret >= 0) {
+ *ids = track_id;
+ }
+ }
+ return ret;
+}
+
+void ff_mov_tref_free(MOVTRefs *trefs)
+{
+ int i;
+ for (i = 0; i < trefs->nb_trefs; ++i)
+ av_freep(&trefs->trefs[i].track_ids);
+ av_freep(&trefs->trefs);
+}
new file mode 100644
@@ -0,0 +1,75 @@
+/*
+ * ISO base media file format track references
+ * Copyright (c) 2016 Erkki Seppälä <erkki.seppala.ext@nokia.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef AVFORMAT_MOVTREF_H
+#define AVFORMAT_MOVTREF_H
+
+typedef struct MOVTRef {
+ uint32_t tag;
+ int nb_track_ids;
+ int* track_ids;
+} MOVTRef;
+
+typedef struct MOVTRefs {
+ int nb_trefs;
+ MOVTRef *trefs;
+} MOVTRefs;
+
+/**
+ * @brief Finds a track reference of certain tag from a track; if not
+ * found, return NULL
+ *
+ * @param track The track to search from
+ * @param tag The tag (4cc) to search for
+ * @return a MOVTRef describing the track reference or NULL if not found.
+ */
+MOVTRef *ff_mov_tref_find(MOVTRefs *trefs, uint32_t tag);
+
+/**
+ * @brief Allocate space for n more tracks (and increase nb_ids accordingly) returning
+ * the pointer in *track_ids
+ *
+ * @param tref The track reference to allocate tracks to
+ * @param n Number of tracks to add
+ * @param track_ids_ret A non-null pointer where the pointer to the first free track
+ * id is returned to
+ * @return Returns a value <0 on error, otherwise 0.
+ */
+int ff_mov_tref_alloc(MOVTRef *tref, int n, int **track_ids_ret);
+
+/**
+ * @brief Finds an existing MOVTRef for a track for the given tag, or
+ * creates a new one if it is missing. Returns the tref in tref_ret.
+ */
+
+int ff_mov_tref_find_or_add(MOVTRefs *trefs, uint32_t tag, MOVTRef **tref_ret);
+
+/**
+ * Adds one tref track reference with given track id if it doesn't
+ * already exist
+ */
+int ff_mov_tref_add_one_track(MOVTRefs *trefs, unsigned tag, int track_id);
+
+/**
+ * Release MOVTRefs
+ */
+void ff_mov_tref_free(MOVTRefs *trefs);
+
+#endif /* AVFORMAT_MOVTREF_H */