support mov reference files, ref.mov/ref.m2v/ref.wav and detect BrianCox.mov
authorBaptiste Coudurier <baptiste.coudurier@gmail.com>
Tue, 26 Feb 2008 18:21:58 +0000 (18:21 +0000)
committerBaptiste Coudurier <baptiste.coudurier@gmail.com>
Tue, 26 Feb 2008 18:21:58 +0000 (18:21 +0000)
Originally committed as revision 12241 to svn://svn.ffmpeg.org/ffmpeg/trunk

libavformat/mov.c

index e0bb8d2..2e57915 100644 (file)
@@ -69,6 +69,11 @@ typedef struct {
     int id;
 } MOV_stsc_t;
 
     int id;
 } MOV_stsc_t;
 
+typedef struct {
+    uint32_t type;
+    char *path;
+} MOV_dref_t;
+
 typedef struct {
     uint32_t type;
     int64_t offset;
 typedef struct {
     uint32_t type;
     int64_t offset;
@@ -78,6 +83,7 @@ typedef struct {
 struct MOVParseTableEntry;
 
 typedef struct MOVStreamContext {
 struct MOVParseTableEntry;
 
 typedef struct MOVStreamContext {
+    ByteIOContext *pb;
     int ffindex; /* the ffmpeg stream id */
     int next_chunk;
     unsigned int chunk_count;
     int ffindex; /* the ffmpeg stream id */
     int next_chunk;
     unsigned int chunk_count;
@@ -104,6 +110,9 @@ typedef struct MOVStreamContext {
     int dv_audio_container;
     int pseudo_stream_id;
     int16_t audio_cid; ///< stsd audio compression id
     int dv_audio_container;
     int pseudo_stream_id;
     int16_t audio_cid; ///< stsd audio compression id
+    unsigned drefs_count;
+    MOV_dref_t *drefs;
+    int dref_id;
 } MOVStreamContext;
 
 typedef struct MOVContext {
 } MOVStreamContext;
 
 typedef struct MOVContext {
@@ -200,6 +209,71 @@ static int mov_read_default(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom)
     return err;
 }
 
     return err;
 }
 
+static int mov_read_dref(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom)
+{
+    AVStream *st = c->fc->streams[c->fc->nb_streams-1];
+    MOVStreamContext *sc = st->priv_data;
+    int entries, i, j;
+
+    get_be32(pb); // version + flags
+    entries = get_be32(pb);
+    if (entries >= UINT_MAX / sizeof(*sc->drefs))
+        return -1;
+    sc->drefs_count = entries;
+    sc->drefs = av_mallocz(entries * sizeof(*sc->drefs));
+
+    for (i = 0; i < sc->drefs_count; i++) {
+        MOV_dref_t *dref = &sc->drefs[i];
+        uint32_t size = get_be32(pb);
+        offset_t next = url_ftell(pb) + size - 4;
+
+        dref->type = get_le32(pb);
+        get_be32(pb); // version + flags
+        dprintf(c->fc, "type %.4s size %d\n", (char*)&dref->type, size);
+
+        if (dref->type == MKTAG('a','l','i','s') && size > 150) {
+            /* macintosh alias record */
+            uint16_t volume_len, len;
+            char volume[28];
+            int16_t type;
+
+            url_fskip(pb, 10);
+
+            volume_len = get_byte(pb);
+            volume_len = FFMIN(volume_len, 27);
+            get_buffer(pb, volume, 27);
+            volume[volume_len] = 0;
+            av_log(c->fc, AV_LOG_DEBUG, "volume %s, len %d\n", volume, volume_len);
+
+            url_fskip(pb, 112);
+
+            for (type = 0; type != -1 && url_ftell(pb) < next; ) {
+                type = get_be16(pb);
+                len = get_be16(pb);
+                av_log(c->fc, AV_LOG_DEBUG, "type %d, len %d\n", type, len);
+                if (len&1)
+                    len += 1;
+                if (type == 2) { // absolute path
+                    dref->path = av_mallocz(len+1);
+                    get_buffer(pb, dref->path, len);
+                    if (!strncmp(dref->path, volume, volume_len)) {
+                        len -= volume_len;
+                        memmove(dref->path, dref->path+volume_len, len);
+                        dref->path[len] = 0;
+                    }
+                    for (j = 0; j < len; j++)
+                        if (dref->path[j] == ':')
+                            dref->path[j] = '/';
+                    av_log(c->fc, AV_LOG_DEBUG, "path %s\n", dref->path);
+                } else
+                    url_fskip(pb, len);
+            }
+        }
+        url_fseek(pb, next, SEEK_SET);
+    }
+    return 0;
+}
+
 static int mov_read_hdlr(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom)
 {
     AVStream *st = c->fc->streams[c->fc->nb_streams-1];
 static int mov_read_hdlr(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom)
 {
     AVStream *st = c->fc->streams[c->fc->nb_streams-1];
@@ -574,6 +648,7 @@ static int mov_read_stsd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom)
 
     for(pseudo_stream_id=0; pseudo_stream_id<entries; pseudo_stream_id++) { //Parsing Sample description table
         enum CodecID id;
 
     for(pseudo_stream_id=0; pseudo_stream_id<entries; pseudo_stream_id++) { //Parsing Sample description table
         enum CodecID id;
+        int dref_id;
         MOV_atom_t a = { 0, 0, 0 };
         offset_t start_pos = url_ftell(pb);
         int size = get_be32(pb); /* size */
         MOV_atom_t a = { 0, 0, 0 };
         offset_t start_pos = url_ftell(pb);
         int size = get_be32(pb); /* size */
@@ -581,7 +656,7 @@ static int mov_read_stsd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom)
 
         get_be32(pb); /* reserved */
         get_be16(pb); /* reserved */
 
         get_be32(pb); /* reserved */
         get_be16(pb); /* reserved */
-        get_be16(pb); /* index */
+        dref_id = get_be16(pb);
 
         if (st->codec->codec_tag &&
             (c->fc->video_codec_id ? codec_get_id(codec_movvideo_tags, format) != c->fc->video_codec_id
 
         if (st->codec->codec_tag &&
             (c->fc->video_codec_id ? codec_get_id(codec_movvideo_tags, format) != c->fc->video_codec_id
@@ -594,6 +669,7 @@ static int mov_read_stsd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom)
             continue;
         }
         sc->pseudo_stream_id= pseudo_stream_id;
             continue;
         }
         sc->pseudo_stream_id= pseudo_stream_id;
+        sc->dref_id= dref_id;
 
         st->codec->codec_tag = format;
         id = codec_get_id(codec_movaudio_tags, format);
 
         st->codec->codec_tag = format;
         id = codec_get_id(codec_movaudio_tags, format);
@@ -1217,6 +1293,8 @@ static const MOVParseTableEntry mov_default_parse_table[] = {
 /* mp4 atoms */
 { MKTAG( 'c', 'o', '6', '4' ), mov_read_stco },
 { MKTAG( 'c', 't', 't', 's' ), mov_read_ctts }, /* composition time to sample */
 /* mp4 atoms */
 { MKTAG( 'c', 'o', '6', '4' ), mov_read_stco },
 { MKTAG( 'c', 't', 't', 's' ), mov_read_ctts }, /* composition time to sample */
+{ MKTAG( 'd', 'i', 'n', 'f' ), mov_read_default },
+{ MKTAG( 'd', 'r', 'e', 'f' ), mov_read_dref },
 { MKTAG( 'e', 'd', 't', 's' ), mov_read_default },
 { MKTAG( 'e', 'l', 's', 't' ), mov_read_elst },
 { MKTAG( 'e', 'n', 'd', 'a' ), mov_read_enda },
 { MKTAG( 'e', 'd', 't', 's' ), mov_read_default },
 { MKTAG( 'e', 'l', 's', 't' ), mov_read_elst },
 { MKTAG( 'e', 'n', 'd', 'a' ), mov_read_enda },
@@ -1451,6 +1529,13 @@ static int mov_read_header(AVFormatContext *s, AVFormatParameters *ap)
         sc->ffindex = i;
         mov_build_index(mov, st);
 
         sc->ffindex = i;
         mov_build_index(mov, st);
 
+        if (sc->dref_id-1 < sc->drefs_count && sc->drefs[sc->dref_id-1].path) {
+            if (url_fopen(&sc->pb, sc->drefs[sc->dref_id-1].path, URL_RDONLY) < 0)
+                av_log(s, AV_LOG_ERROR, "stream %d, error opening external essence: %s\n",
+                       st->index, strerror(errno));
+        } else
+            sc->pb = s->pb;
+
         switch (st->codec->codec_id) {
 #ifdef CONFIG_H261_DECODER
         case CODEC_ID_H261:
         switch (st->codec->codec_id) {
 #ifdef CONFIG_H261_DECODER
         case CODEC_ID_H261:
@@ -1499,15 +1584,16 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
     for (i = 0; i < s->nb_streams; i++) {
         AVStream *st = s->streams[i];
         MOVStreamContext *msc = st->priv_data;
     for (i = 0; i < s->nb_streams; i++) {
         AVStream *st = s->streams[i];
         MOVStreamContext *msc = st->priv_data;
-        if (st->discard != AVDISCARD_ALL && msc->current_sample < msc->sample_count) {
+        if (st->discard != AVDISCARD_ALL && msc->pb && msc->current_sample < msc->sample_count) {
             AVIndexEntry *current_sample = &st->index_entries[msc->current_sample];
             int64_t dts = av_rescale(current_sample->timestamp * (int64_t)msc->time_rate,
                                      AV_TIME_BASE, msc->time_scale);
             dprintf(s, "stream %d, sample %d, dts %"PRId64"\n", i, msc->current_sample, dts);
             if (!sample || (url_is_streamed(s->pb) && current_sample->pos < sample->pos) ||
                 (!url_is_streamed(s->pb) &&
             AVIndexEntry *current_sample = &st->index_entries[msc->current_sample];
             int64_t dts = av_rescale(current_sample->timestamp * (int64_t)msc->time_rate,
                                      AV_TIME_BASE, msc->time_scale);
             dprintf(s, "stream %d, sample %d, dts %"PRId64"\n", i, msc->current_sample, dts);
             if (!sample || (url_is_streamed(s->pb) && current_sample->pos < sample->pos) ||
                 (!url_is_streamed(s->pb) &&
+                 ((msc->pb != s->pb && dts < best_dts) || (msc->pb == s->pb &&
                  ((FFABS(best_dts - dts) <= AV_TIME_BASE && current_sample->pos < sample->pos) ||
                  ((FFABS(best_dts - dts) <= AV_TIME_BASE && current_sample->pos < sample->pos) ||
-                  (FFABS(best_dts - dts) > AV_TIME_BASE && dts < best_dts)))) {
+                  (FFABS(best_dts - dts) > AV_TIME_BASE && dts < best_dts)))))) {
                 sample = current_sample;
                 best_dts = dts;
                 sc = msc;
                 sample = current_sample;
                 best_dts = dts;
                 sc = msc;
@@ -1518,12 +1604,12 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
         return -1;
     /* must be done just before reading, to avoid infinite loop on sample */
     sc->current_sample++;
         return -1;
     /* must be done just before reading, to avoid infinite loop on sample */
     sc->current_sample++;
-    if (url_fseek(s->pb, sample->pos, SEEK_SET) != sample->pos) {
+    if (url_fseek(sc->pb, sample->pos, SEEK_SET) != sample->pos) {
         av_log(mov->fc, AV_LOG_ERROR, "stream %d, offset 0x%"PRIx64": partial file\n",
                sc->ffindex, sample->pos);
         return -1;
     }
         av_log(mov->fc, AV_LOG_ERROR, "stream %d, offset 0x%"PRIx64": partial file\n",
                sc->ffindex, sample->pos);
         return -1;
     }
-    av_get_packet(s->pb, pkt, sample->size);
+    av_get_packet(sc->pb, pkt, sample->size);
 #ifdef CONFIG_DV_DEMUXER
     if (mov->dv_demux && sc->dv_audio_container) {
         dv_produce_packet(mov->dv_demux, pkt, pkt->data, pkt->size);
 #ifdef CONFIG_DV_DEMUXER
     if (mov->dv_demux && sc->dv_audio_container) {
         dv_produce_packet(mov->dv_demux, pkt, pkt->data, pkt->size);
@@ -1614,11 +1700,16 @@ static int mov_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti
 
 static int mov_read_close(AVFormatContext *s)
 {
 
 static int mov_read_close(AVFormatContext *s)
 {
-    int i;
+    int i, j;
     MOVContext *mov = s->priv_data;
     for(i=0; i<s->nb_streams; i++) {
         MOVStreamContext *sc = s->streams[i]->priv_data;
         av_freep(&sc->ctts_data);
     MOVContext *mov = s->priv_data;
     for(i=0; i<s->nb_streams; i++) {
         MOVStreamContext *sc = s->streams[i]->priv_data;
         av_freep(&sc->ctts_data);
+        for (j=0; j<sc->drefs_count; j++)
+            av_freep(&sc->drefs[j].path);
+        av_freep(&sc->drefs);
+        if (sc->pb && sc->pb != s->pb)
+            url_fclose(sc->pb);
     }
     if(mov->dv_demux){
         for(i=0; i<mov->dv_fctx->nb_streams; i++){
     }
     if(mov->dv_demux){
         for(i=0; i<mov->dv_fctx->nb_streams; i++){