gmediaserver-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gmediaserver-devel] [Patch] Add support for MPG files; speed up initial


From: Jan Ceuleers
Subject: [gmediaserver-devel] [Patch] Add support for MPG files; speed up initial start-up
Date: Sun, 04 Nov 2007 14:03:19 +0100
User-agent: Thunderbird 2.0.0.6 (Windows/20070728)

Back in the 0.11.0 days I contributed some patches to Oskar aimed at adding support for media types other than audio to gmediaserver.

Oskar decided to deal with the functionality that I proposed in the following ways:

- Support for still images: Taken. This is now present in 0.12.0.

- Support for video: Not taken. This is absent from 0.12.0.

- Method of determining the media type contained in a particular file: not taken. I had proposed a data-driven content type checking mechanism. Oskar however decided to rely on libmagic for this purpose.

I have continued to maintain and run my own version of gmediaserver, for the following reasons:

- The built-in content type scanning mechanism is several orders of magnitude faster than the libmagic method. As my media collection is quite large, and I routinely have gmediaserver re-scan the content directory following the addition of new files, this makes for less of an interruption to the enjoyment of my library.

- I have videos, not only audio files and photos.

For those of you who are interested in these features, please find attached a patch against 0.12.0 that provides them. The only video format currently supported is MPG, but this can trivially be extended to other types as well.

Cheers, Jan

diff -u -r -X exclude gmediaserver-0.12.0/src/metadata.c 
gmediaserver-0.12.0jc/src/metadata.c
--- gmediaserver-0.12.0/src/metadata.c  2006-08-31 21:48:01.000000000 +0200
+++ gmediaserver-0.12.0jc/src/metadata.c        2007-11-04 12:26:02.000000000 
+0100
@@ -78,10 +78,143 @@
     FILE_M3U,
     FILE_EXTM3U,
     FILE_JPG,
+    FILE_MPG,
     FILE_UNKNOWN,
     FILE_TYPES_COUNT,
 } FileType;
 
+typedef struct {
+    enum {
+        TEST_END, TEST_IGNORE, TEST_STRING,
+        TEST_BITS, TEST_CRorLF, TEST_FEXT
+    } TestType;
+    enum {
+        TESTL_BOF, TESTL_REL, TESTL_FIND
+    } TestLocation;
+    uint8_t *TestString;
+    int TestCount;
+} Pattern;
+
+Pattern pattern_MP3[] =
+{
+  {  TEST_STRING,       TESTL_BOF,      "\xff",                 1  },
+  {  TEST_BITS,         TESTL_REL,      "\xfe\xfa",             1  },
+  {  TEST_END                                                      }
+};
+
+Pattern pattern_MP3_ID3[] =
+{
+  {  TEST_STRING,       TESTL_BOF,      "ID3",                  3  },
+  {  TEST_END                                                      }
+};
+
+Pattern pattern_WMA[] =
+{
+  {  TEST_STRING,       TESTL_BOF,      "\x30\x26\x26\x75",     4  },
+  {  TEST_END                                                      }
+};
+
+Pattern pattern_JPG_JFIF[] =
+{
+  {  TEST_STRING,       TESTL_BOF,      "\xff\xd8",             2  },
+  {  TEST_IGNORE,       TESTL_REL,      NULL,                   4  },
+  {  TEST_STRING,       TESTL_REL,      "JFIF",                 5  },
+  {  TEST_END                                                      }
+};
+
+Pattern pattern_JPG_Exif[] =
+{
+  {  TEST_STRING,       TESTL_BOF,      "\xff\xd8",             2  },
+  {  TEST_IGNORE,       TESTL_REL,      NULL,                   4  },
+  {  TEST_STRING,       TESTL_REL,      "Exif",                 5  },
+  {  TEST_END                                                      }
+};
+
+Pattern pattern_RIFF_WAVE[] =
+{
+  {  TEST_STRING,       TESTL_BOF,      "RIFF",                 4  },
+  {  TEST_IGNORE,       TESTL_REL,      NULL,                   4  },
+  {  TEST_STRING,       TESTL_REL,      "WAVE",                 4  },
+  {  TEST_END                                                      }
+};
+
+Pattern pattern_OGG[] =
+{
+  {  TEST_STRING,       TESTL_BOF,      "OggS",                 4  },
+  {  TEST_IGNORE,       TESTL_REL,      NULL,                   24 },
+  {  TEST_STRING,       TESTL_REL,      "\1vorbis",             7  },
+  {  TEST_END                                                      }
+};
+
+Pattern pattern_M4A[] =
+{
+  {  TEST_IGNORE,       TESTL_BOF,      NULL,                   4  },
+  {  TEST_STRING,       TESTL_REL,      "ftypM4A",              7  },
+  {  TEST_END                                                      }
+};
+
+Pattern pattern_EXTM3U[] =
+{
+  {  TEST_STRING,       TESTL_BOF,      "#EXTM3U",              7  },
+  {  TEST_CRorLF,       TESTL_REL,      NULL,                   0  },
+  {  TEST_END                                                      }
+};
+
+Pattern pattern_PLS[] =
+{
+  {  TEST_STRING,       TESTL_BOF,      "[playlist]",           10 },
+  {  TEST_CRorLF,       TESTL_REL,      NULL,                   0  },
+  {  TEST_END                                                      }
+};
+
+Pattern pattern_M3U[] =
+{
+  {  TEST_FEXT,         TESTL_BOF,      ".m3u",                 0  },
+  {  TEST_END                                                      }
+};
+
+Pattern pattern_MPG[] =
+{
+  {  TEST_STRING,       TESTL_BOF,      "\x00\x00\x01\xba",     4  },
+  {  TEST_END                                                      }
+};
+
+Pattern pattern_MP3_liberal[] =
+{
+  {  TEST_FEXT,         TESTL_BOF,      ".mp3",                 0  },
+  {  TEST_BITS,         TESTL_FIND,     "\xff\xff\xfe\xfa",     2  },
+  {  TEST_END                                                      }
+};
+
+typedef struct {
+        FileType FT;
+        Pattern *pP;
+} PatternTableEntry;
+
+PatternTableEntry all_patterns[] =
+{
+  {     FILE_MP3,               &pattern_MP3[0]         },
+  {     FILE_MP3_ID3,           &pattern_MP3_ID3[0]     },
+  {     FILE_WMA,               &pattern_WMA[0]         },
+  {     FILE_JPG,               &pattern_JPG_JFIF[0]    },
+  {     FILE_JPG,               &pattern_JPG_Exif[0]    },
+  {     FILE_RIFF_WAVE,         &pattern_RIFF_WAVE[0]   },
+  {     FILE_OGG,               &pattern_OGG[0]         },
+  {     FILE_M4A,               &pattern_M4A[0]         },
+  {     FILE_EXTM3U,            &pattern_EXTM3U[0]      },
+  {     FILE_PLS,               &pattern_PLS[0]         },
+  {     FILE_M3U,               &pattern_M3U[0]         },
+  {     FILE_MPG,               &pattern_MPG[0]         },
+    /* Include the potentially expensive tests below, not above */
+  {     FILE_MP3,               &pattern_MP3_liberal[0] },
+  {
+    /* This marks the end of the list. Insert new items above, not below */
+    FILE_UNKNOWN,       NULL
+  }
+};
+
+uint8_t buf[512];
+
 typedef struct _InodeList InodeList;
 
 struct _InodeList {
@@ -100,6 +233,7 @@
     [FILE_M3U]                 = "audio/m3u",
     [FILE_EXTM3U]      = "audio/m3u",
     [FILE_JPG]         = "image/jpeg",
+    [FILE_MPG]         = "video/mpeg",
     [FILE_UNKNOWN]     = "application/octet-stream",
 };
 
@@ -114,6 +248,7 @@
     [FILE_M3U]                 = "m3u",
     [FILE_EXTM3U]      = "m3u", /* possibly extm3u in the future */
     [FILE_JPG]         = "jpg",
+    [FILE_MPG]         = "mpg",
     [FILE_UNKNOWN]     = "unknown",
 };
 
@@ -128,7 +263,8 @@
     [FILE_PLS]         = "PLS playlist",
     [FILE_M3U]         = "Simple M3U playlist",
     [FILE_EXTM3U]      = "Extended M3U playlist",
-    [FILE_JPG]         = "JPEG image"
+    [FILE_JPG]         = "JPEG image",
+    [FILE_MPG]         = "MPEG-2 video"
 };
 
 static ItemClass file_type_item_classes[] = {
@@ -142,6 +278,7 @@
     [FILE_M3U]         = ITEM_PLAYLIST,
     [FILE_EXTM3U]      = ITEM_PLAYLIST,
     [FILE_JPG]         = ITEM_IMAGE,
+    [FILE_MPG]         = ITEM_VIDEO,
 };
 
 static Entry *scan_entry(const char *fullpath, const char *name, int32_t 
parent, int indent_size, InodeList *inl);
@@ -254,75 +391,181 @@
 static FileType
 check_file_content_type(const char *fullpath)
 {
-    const char *magic;
     int fd;
-    uint8_t buf[11];
-    int c;
+    PatternTableEntry *curPTE;
 
-    magic = magic_file(magic_cookie, fullpath);
-    if (magic == NULL) {
-       warn(_("%s: cannot identify file type: %s\n"), 
quotearg(conv_filename(fullpath)), magic_error(magic_cookie));
-       return FILE_UNKNOWN;
-    }
-
-    if (strcmp(magic, "application/octet-stream") != 0
-           && strncmp(magic, "text/plain", 10) != 0) {
-       struct {
-           FileType id;
-           char *mime;
-       } mime_map[] = {
-           { FILE_MP3,         "audio/mpeg" }, /* XXX: FILE_MP3_ID3? */
-           { FILE_WMA,         "audio/x-ms-wma" },
-           { FILE_RIFF_WAVE,   "audio/x-wav" },
-           { FILE_M4A,         "video/mp4" },
-           { FILE_M4A,         "audio/mp4" },
-           { FILE_OGG,         "application/ogg" },
-           { FILE_JPG,         "image/jpeg" },
-            { 0, },
-        };
-        for (c = 0; mime_map[c].mime != NULL; c++) {
-            if (strcmp(magic, mime_map[c].mime) == 0)
-                return mime_map[c].id;
-        }
-    }
+    memset(buf, 0, sizeof(buf)); /* Needed to avoid false matches in case file 
shorter than buffer */
 
+    /* XXX: Use stdio instead of fd syscalls */
     fd = open(fullpath, O_RDONLY);
     if (fd < 0) {
         warn(_("%s: cannot open for reading: %s\n"), 
quotearg(conv_filename(fullpath)), errstr);
         return FILE_UNKNOWN;
     }
-    if (!attempt_read_at(fd, 11, 0, buf, fullpath)) {
+
+    if (!attempt_read_at(fd, sizeof(buf), 0, buf, fullpath))
+    {
         close(fd);
         return FILE_UNKNOWN;
     }
 
-    /* Microsoft ASF */
-    if (buf[0] == 0x30 && buf[1] == 0x26 && buf[2] == 0xb2 && buf[3] == 0x75) {
-        close(fd); /* Ignore errors since we opened for reading */
-        return FILE_WMA;
-    }
+    close(fd); /* No further need for it */
+
+    for (curPTE = all_patterns; curPTE->FT != FILE_UNKNOWN; curPTE++)
+    {
+        Pattern *curP;
+        uint8_t *currentPos=buf;
+        enum { Inconclusive, PosMatch, NegMatch } testResult = Inconclusive;
+
+        for (curP = curPTE->pP;
+             (curP->TestType != TEST_END)
+             && (testResult == Inconclusive)
+             && (currentPos < buf+sizeof(buf)-curP->TestCount);
+             curP++)
+        {
+            switch (curP->TestLocation)
+            {
+                case TESTL_BOF:
+                    currentPos = buf;
+                    break;
 
+                case TESTL_REL:
+                    break;
 
-    /* Extended M3U */
-    if (buf[0]=='#' && buf[1]=='E' && buf[2]=='X' && buf[3]=='T' && 
buf[4]=='M' && buf[5]=='3' && buf[6]=='U' && (buf[7]=='\r' || buf[7]=='\n')) {
-        close(fd); /* Ignore errors since we opened for reading */
-        return FILE_EXTM3U;
-    }
-    /* Playlist (PLS) */
-    if (memcmp(buf, "[playlist]", 10) == 0 && (buf[10] == '\r' || buf[10] == 
'\n')) {
-        close(fd); /* Ignore errors since we opened for reading */
-        return FILE_PLS;
-    }
-    /* Simple M3U */
-    if (ends_with_nocase(fullpath, ".m3u")) {
-        close(fd); /* Ignore errors since we opened for reading */
-        return FILE_M3U;
+                case TESTL_FIND:
+                    /* This is currently only handled by the for loop within
+                     * the TEST_BITS test below */
+                    /* FIXME */
+                    break;
+
+            //    default:
+                    /* FIXME */
+            }
+
+            switch (curP->TestType)
+            {
+                case TEST_END:
+                    testResult = PosMatch;
+                    break;
+
+                case TEST_IGNORE:
+                    currentPos += curP->TestCount;
+                    break;
+
+                case TEST_STRING:
+                    if (0 != memcmp(currentPos, curP->TestString, 
curP->TestCount))
+                        testResult = NegMatch;
+
+                    currentPos += curP->TestCount;
+                    break;
+
+                case TEST_BITS:
+                    if (curP->TestLocation == TESTL_FIND)
+                    {
+                        /* Check whether the bit pattern described by the
+                         * TestString occurs anywhere between currentPos and
+                         * the end of the buffer.
+                         * TestString alternates between masks and test
+                         * values: the test is as follows for each (mask,value)
+                         * pair in TestString:
+                         * ((*currentPos & *TestString) == *(TestString+1)) */
+                        bool matchFound = false;
+
+                        for (;
+                             currentPos < buf+sizeof(buf)-curP->TestCount;
+                             currentPos++)
+                        {
+                            uint8_t *b = curP->TestString;
+                            uint8_t *cp = currentPos;
+                            int count = curP->TestCount;
+
+                            for (; count > 0; count--, b+=2)
+                            {
+                                if ((*cp++ & *b) != *(b+1))
+                                {
+                                    /* The pattern does not occur here, but
+                                     * that does not mean that this is a
+                                     * negative match. It's only when we
+                                     * get to the end of the buffer that we
+                                     * can conclude that the pattern does not
+                                     * occur */
+                                    break;
+                                }
+                            }
+
+                            if (0 == count)
+                            {
+                                /* We got to the end of the TestString. This
+                                 * means that we found the pattern at the
+                                 * current position. We can stop this test
+                                 * now and move onto the next-one */
+                                currentPos += curP->TestCount;
+                                matchFound = true;
+                                break;
+                            }
+                        }
+
+                        if ((!matchFound) && (currentPos >= 
buf+sizeof(buf)-curP->TestCount))
+                        {
+                            /* We got to the end of the buffer. This means
+                             * that the pattern did not occur anywhere within
+                             * it. */
+                            testResult = NegMatch;
+                        }
+                    }
+                    else
+                    {
+                        /* Carry out the BITS test only once: at currentPos */
+                        uint8_t *b;
+                        int count;
+
+                        for (b=curP->TestString, count=curP->TestCount;
+                             count > 0;
+                             count--, b+=2)
+                        {
+                            if ((*currentPos++ & *b) != *(b+1))
+                            {
+                                testResult = NegMatch;
+                                break;
+                            }
+                        }
+                    }
+                    break;
+
+                case TEST_CRorLF:
+                    if ((*currentPos != '\n') && (*currentPos != '\r'))
+                    {
+                        testResult = NegMatch;
+                    }
+                    currentPos++;
+                    break;
+
+                case TEST_FEXT:
+                    if (!ends_with_nocase(fullpath, curP->TestString))
+                    {
+                        testResult = NegMatch;
+                    }
+                    break;
+
+            //    default:
+                    /* FIXME */
+            }
+        }
+
+        if ((testResult == Inconclusive) && (curP->TestType == TEST_END))
+            testResult = PosMatch;
+
+        if (testResult == PosMatch)
+        {
+            return curPTE->FT;
+        }
     }
 
-    close(fd); /* Ignore errors since we opened for reading */
+    /* All tests performed, no match found */
     return FILE_UNKNOWN;
 }
 
+
 #ifdef HAVE_ID3LIB
 /* XXX: move this function to a more appropriate place, perhaps lib/iconvme? */
 static char *

reply via email to

[Prev in Thread] Current Thread [Next in Thread]