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@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@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 */
