changeset 5369:347123a18feb libavformat

Search relative path according to alias record when opening mov reference files. Based on patch by Maksym Veremeyenko, verem at m1stereo dot tv
author bcoudurier
date Sun, 15 Nov 2009 03:26:47 +0000
parents d93af9bfc0b8
children 238bda5af58e
files isom.h mov.c
diffstat 2 files changed, 92 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/isom.h	Fri Nov 13 10:19:42 2009 +0000
+++ b/isom.h	Sun Nov 15 03:26:47 2009 +0000
@@ -56,6 +56,10 @@
 typedef struct {
     uint32_t type;
     char *path;
+    char *dir;
+    char volume[28];
+    char filename[64];
+    int16_t nlvl_to, nlvl_from;
 } MOVDref;
 
 typedef struct {
--- a/mov.c	Fri Nov 13 10:19:42 2009 +0000
+++ b/mov.c	Sun Nov 15 03:26:47 2009 +0000
@@ -274,18 +274,33 @@
         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);
+            get_buffer(pb, dref->volume, 27);
+            dref->volume[volume_len] = 0;
+            av_log(c->fc, AV_LOG_DEBUG, "volume %s, len %d\n", dref->volume, volume_len);
+
+            url_fskip(pb, 12);
 
-            url_fskip(pb, 112);
+            len = get_byte(pb);
+            len = FFMIN(len, 63);
+            get_buffer(pb, dref->filename, 63);
+            dref->filename[len] = 0;
+            av_log(c->fc, AV_LOG_DEBUG, "filename %s, len %d\n", dref->filename, len);
+
+            url_fskip(pb, 16);
+
+            /* read next level up_from_alias/down_to_target */
+            dref->nlvl_from = get_be16(pb);
+            dref->nlvl_to   = get_be16(pb);
+            av_log(c->fc, AV_LOG_DEBUG, "nlvl from %d, nlvl to %d\n",
+                   dref->nlvl_from, dref->nlvl_to);
+
+            url_fskip(pb, 16);
 
             for (type = 0; type != -1 && url_ftell(pb) < next; ) {
                 type = get_be16(pb);
@@ -299,7 +314,7 @@
                     if (!dref->path)
                         return AVERROR(ENOMEM);
                     get_buffer(pb, dref->path, len);
-                    if (len > volume_len && !strncmp(dref->path, volume, volume_len)) {
+                    if (len > volume_len && !strncmp(dref->path, dref->volume, volume_len)) {
                         len -= volume_len;
                         memmove(dref->path, dref->path+volume_len, len);
                         dref->path[len] = 0;
@@ -308,6 +323,17 @@
                         if (dref->path[j] == ':')
                             dref->path[j] = '/';
                     av_log(c->fc, AV_LOG_DEBUG, "path %s\n", dref->path);
+                } else if (type == 0) { // directory name
+                    av_free(dref->dir);
+                    dref->dir = av_malloc(len+1);
+                    if (!dref->dir)
+                        return AVERROR(ENOMEM);
+                    get_buffer(pb, dref->dir, len);
+                    dref->dir[len] = 0;
+                    for (j = 0; j < len; j++)
+                        if (dref->dir[j] == ':')
+                            dref->dir[j] = '/';
+                    av_log(c->fc, AV_LOG_DEBUG, "dir %s\n", dref->dir);
                 } else
                     url_fskip(pb, len);
             }
@@ -1526,6 +1552,52 @@
     }
 }
 
+static int mov_open_dref(ByteIOContext **pb, char *src, MOVDref *ref)
+{
+    /* try absolute path */
+    if (!url_fopen(pb, ref->path, URL_RDONLY))
+        return 0;
+
+    /* try relative path */
+    if (ref->nlvl_to > 0 && ref->nlvl_from > 0) {
+        char filename[1024];
+        char *src_path;
+        int i, l;
+
+        /* find a source dir */
+        src_path = strrchr(src, '/');
+        if (src_path)
+            src_path++;
+        else
+            src_path = src;
+
+        /* find a next level down to target */
+        for (i = 0, l = strlen(ref->path) - 1; l >= 0; l--)
+            if (ref->path[l] == '/') {
+                if (i == ref->nlvl_to - 1)
+                    break;
+                else
+                    i++;
+            }
+
+        /* compose filename if next level down to target was found */
+        if (i == ref->nlvl_to - 1) {
+            memcpy(filename, src, src_path - src);
+            filename[src_path - src] = 0;
+
+            for (i = 1; i < ref->nlvl_from; i++)
+                av_strlcat(filename, "../", 1024);
+
+            av_strlcat(filename, ref->path + l + 1, 1024);
+
+            if (!url_fopen(pb, filename, URL_RDONLY))
+                return 0;
+        }
+    }
+
+    return AVERROR(ENOENT);
+};
+
 static int mov_read_trak(MOVContext *c, ByteIOContext *pb, MOVAtom atom)
 {
     AVStream *st;
@@ -1571,9 +1643,13 @@
     mov_build_index(c, 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(c->fc, AV_LOG_ERROR, "stream %d, error opening file %s: %s\n",
-                   st->index, sc->drefs[sc->dref_id-1].path, strerror(errno));
+        MOVDref *dref = &sc->drefs[sc->dref_id - 1];
+        if (mov_open_dref(&sc->pb, c->fc->filename, dref) < 0)
+            av_log(c->fc, AV_LOG_ERROR,
+                   "stream %d, error opening alias: path='%s', dir='%s', "
+                   "filename='%s', volume='%s', nlvl_from=%d, nlvl_to=%d\n",
+                   st->index, dref->path, dref->dir, dref->filename,
+                   dref->volume, dref->nlvl_from, dref->nlvl_to);
     } else
         sc->pb = c->fc->pb;
 
@@ -2244,8 +2320,10 @@
         MOVStreamContext *sc = st->priv_data;
 
         av_freep(&sc->ctts_data);
-        for (j = 0; j < sc->drefs_count; j++)
+        for (j = 0; j < sc->drefs_count; j++) {
             av_freep(&sc->drefs[j].path);
+            av_freep(&sc->drefs[j].dir);
+        }
         av_freep(&sc->drefs);
         if (sc->pb && sc->pb != s->pb)
             url_fclose(sc->pb);