[FFmpeg-devel] [PATCH v2 05/18] avformat/movenc: support for multiple and client-provided track references

Erkki Seppälä erkki.seppala.ext at nokia.com
Wed Aug 31 14:35:48 EEST 2016


Instead of one track reference, allow many, and instead of
of track reference type (ie. 'cdsc'), allow many.

In addition this patch allows client to explicitly add track references
with side packet AV_PKT_DATA_TRACK_REFERENCES containing
AVTrackReferences (which of there can many many).

Internally MOVTrack's track references can be manipulated with helper
functions ff_mov_tref* (and is used by a later patch for reading
MOVTRefs).

Multiple track references can be useful in particular with timed meta
data tracks, indicating the track is related to multiple other tracks.

This information ends up in ISO media file box 'tref' as specified by
ISO/IEC 14496-12.

Signed-off-by: Erkki Seppälä <erkki.seppala.ext at nokia.com>
Signed-off-by: OZOPlayer <OZOPL at nokia.com>
---
 libavcodec/avcodec.h  |  19 +++++++-
 libavformat/Makefile  |   4 +-
 libavformat/movenc.c  | 127 +++++++++++++++++++++++++++++++++++++++++---------
 libavformat/movenc.h  |   5 +-
 libavformat/movtref.c | 115 +++++++++++++++++++++++++++++++++++++++++++++
 libavformat/movtref.h |  75 +++++++++++++++++++++++++++++
 6 files changed, 318 insertions(+), 27 deletions(-)
 create mode 100644 libavformat/movtref.c
 create mode 100644 libavformat/movtref.h

diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 0d0447e6..f45cdc2 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -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
diff --git a/libavformat/Makefile b/libavformat/Makefile
index fda1e17..6a393ed 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -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
diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index f02458b..6cc315a 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -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);
diff --git a/libavformat/movenc.h b/libavformat/movenc.h
index ea76e39..700d8d5 100644
--- a/libavformat/movenc.h
+++ b/libavformat/movenc.h
@@ -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;
diff --git a/libavformat/movtref.c b/libavformat/movtref.c
new file mode 100644
index 0000000..dd6059c
--- /dev/null
+++ b/libavformat/movtref.c
@@ -0,0 +1,115 @@
+/*
+ * ISO base media file format track references
+ * Copyright (c) 2016 Erkki Seppälä <erkki.seppala.ext at 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);
+}
diff --git a/libavformat/movtref.h b/libavformat/movtref.h
new file mode 100644
index 0000000..091f467
--- /dev/null
+++ b/libavformat/movtref.h
@@ -0,0 +1,75 @@
+/*
+ * ISO base media file format track references
+ * Copyright (c) 2016 Erkki Seppälä <erkki.seppala.ext at 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 */
-- 
2.7.4



More information about the ffmpeg-devel mailing list