]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
Add the ability to read the media file type from HTTP header for playback
authorGaurav Khurana <gkhurana@godaddy.com>
Tue, 13 Feb 2018 18:55:47 +0000 (10:55 -0800)
committerSean Bright <sean.bright@gmail.com>
Mon, 30 Apr 2018 20:30:44 +0000 (16:30 -0400)
How it works today:
media_cache tries to parse out the extension of the media file to be played
from the URI provided to Asterisk while caching the file.

What's expected:
Better will be to have Asterisk get extension from other ways too. One of the
common ways is to get the type of content from the CONTENT-TYPE header in the
HTTP response for fetching the media file using the URI provided.

Steps to Reproduce:
Provide a URL of the form: http://host/media/1234 to Asterisk for media
playback. It fails to play and logs show the following error line:

[Sep 15 15:48:05] WARNING [29148] [C-00000092] file.c:
File http://host/media/1234 does not exist in any format

Scenario this issue is blocking:
In the case where the media files are stored in some cloud object store,
following can block the media being played via Asterisk:

Cloud storage generally needs authenticated access to the storage. The way
to do that is by using signed URIs. With the signed URIs there's no way to
preserve the name of the file.
In most cases Cloud storage returns a key to access the object and preserving
file name is also not a thing there

ASTERISK-27286

 Reporter: Gaurav Khurana

Change-Id: I1b14692a49b2c1ac67688f58757184122e92ba89

formats/format_pcm.c
formats/format_vox.c
formats/format_wav.c
include/asterisk/file.h
include/asterisk/mod_format.h
main/file.c
main/media_cache.c
res/res_http_media_cache.c

index 4e846d7cfeeb8b380a64cec768dfd31d94c52c2f..674c8b59ad1fb0ac93339b0e6f4efcaf27dd36b4 100644 (file)
@@ -507,6 +507,7 @@ static struct ast_format_def alaw_f = {
 static struct ast_format_def pcm_f = {
        .name = "pcm",
        .exts = "pcm|ulaw|ul|mu|ulw",
+       .mime_types = "audio/basic",
        .write = pcm_write,
        .seek = pcm_seek,
        .trunc = pcm_trunc,
index b63e225be918b65110a72bfd81bfc7acbd93bb80..023c40967802cb61d1c57deb65a47c4e5ef087ef 100644 (file)
@@ -128,6 +128,7 @@ static off_t vox_tell(struct ast_filestream *fs)
 static struct ast_format_def vox_f = {
        .name = "vox",
        .exts = "vox",
+       .mime_types = "audio/x-vox",
        .write = vox_write,
        .seek = vox_seek,
        .trunc = vox_trunc,
index 81a686e361f5d26c953a3fc8be016438adb4d44c..ec7e3d37995cfcd27a201456064d3dfd9bff6793 100644 (file)
@@ -532,6 +532,7 @@ static struct ast_format_def wav16_f = {
 static struct ast_format_def wav_f = {
        .name = "wav",
        .exts = "wav",
+       .mime_types = "audio/wav|audio/x-wav",
        .open = wav_open,
        .rewrite = wav_rewrite,
        .write = wav_write,
index c17cb327b3a6468848f96daa40184a010a2e7983..1c2c7a84a0c2fc4a5759b659b4bcee69aee7d48d 100644 (file)
@@ -427,6 +427,18 @@ char *ast_format_str_reduce(char *fmts);
  */
 struct ast_format *ast_get_format_for_file_ext(const char *file_ext);
 
+/*!
+ * \brief Get a suitable filename extension for the given MIME type
+ *
+ * \param mime_type The MIME type for which to find extensions
+ * \param buffer A pointer to a buffer to receive the extension
+ * \param capacity The size of 'buffer' in bytes
+ *
+ * \retval 1 if an extension was found for the provided MIME type
+ * \retval 0 if the MIME type was not found
+ */
+int ast_get_extension_for_mime_type(const char *mime_type, char *buffer, size_t capacity);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif
index 5f93ea442df8b672835b38cc4ad1c8cca13c4152..6e772f05b296e80e63c8ed4c04edd0fb81041855 100644 (file)
@@ -44,6 +44,7 @@ struct ast_format_def {
        char name[80];          /*!< Name of format */
        char exts[80];          /*!< Extensions (separated by | if more than one)
                                                 * this format can read.  First is assumed for writing (e.g. .mp3) */
+       char mime_types[80]; /*!< MIME Types related to the format (separated by | if more than one)*/
        struct ast_format *format;      /*!< Format of frames it uses/provides (one only) */
        /*!
         * \brief Prepare an input stream for playback.
index 8dded812382054f29344267bf4a2753d5ae79da7..3ee58b1684d66b23cbc3a541ad5eb64f8236e5d0 100644 (file)
@@ -333,20 +333,21 @@ static char *build_filename(const char *filename, const char *ext)
 
 /* compare type against the list 'exts' */
 /* XXX need a better algorithm */
-static int exts_compare(const char *exts, const char *type)
+static int type_in_list(const char *list, const char *type, int (*cmp)(const char *s1, const char *s2))
 {
-       char tmp[256];
-       char *stringp = tmp, *ext;
+       char *stringp = ast_strdupa(list), *item;
 
-       ast_copy_string(tmp, exts, sizeof(tmp));
-       while ((ext = strsep(&stringp, "|"))) {
-               if (!strcmp(ext, type))
+       while ((item = strsep(&stringp, "|"))) {
+               if (!cmp(item, type)) {
                        return 1;
+               }
        }
 
        return 0;
 }
 
+#define exts_compare(list, type) (type_in_list((list), (type), strcmp))
+
 /*!
  * \internal
  * \brief Close the file stream by canceling any pending read / write callbacks
@@ -1926,6 +1927,27 @@ struct ast_format *ast_get_format_for_file_ext(const char *file_ext)
        return NULL;
 }
 
+int ast_get_extension_for_mime_type(const char *mime_type, char *buffer, size_t capacity)
+{
+       struct ast_format_def *f;
+       SCOPED_RDLOCK(lock, &formats.lock);
+
+       ast_assert(buffer && capacity);
+
+       AST_RWLIST_TRAVERSE(&formats, f, list) {
+               if (type_in_list(f->mime_types, mime_type, strcasecmp)) {
+                       size_t item_len = strcspn(f->exts, "|");
+                       size_t bytes_written = snprintf(buffer, capacity, ".%.*s", (int) item_len, f->exts);
+                       if (bytes_written < capacity) {
+                               /* Only return success if we didn't truncate */
+                               return 1;
+                       }
+               }
+       }
+
+       return 0;
+}
+
 static struct ast_cli_entry cli_file[] = {
        AST_CLI_DEFINE(handle_cli_core_show_file_formats, "Displays file formats")
 };
index e93d1a02fbe3e07d16851b7ee236c1151efe2a73..e0a6dbb7489587a78311060db2b66a703931b08a 100644 (file)
@@ -35,6 +35,7 @@
 #include "asterisk/bucket.h"
 #include "asterisk/astdb.h"
 #include "asterisk/cli.h"
+#include "asterisk/file.h"
 #include "asterisk/media_cache.h"
 
 /*! The name of the AstDB family holding items in the cache. */
@@ -123,6 +124,24 @@ static void media_cache_item_del_from_astdb(struct ast_bucket_file *bucket_file)
        ast_free(hash_value);
 }
 
+/*!
+ * \internal
+ * \brief Normalize the value of a Content-Type header
+ *
+ * This will trim off any optional parameters after the type/subtype.
+ */
+static void normalize_content_type_header(char *content_type)
+{
+       char *params = strchr(content_type, ';');
+
+       if (params) {
+               *params-- = 0;
+               while (params > content_type && (*params == ' ' || *params == '\t')) {
+                       *params-- = 0;
+               }
+       }
+}
+
 /*!
  * \internal
  * \brief Update the name of the file backing a \c bucket_file
@@ -142,9 +161,32 @@ static void bucket_file_update_path(struct ast_bucket_file *bucket_file,
        } else if (!strchr(bucket_file->path, '.') && (ext = strrchr(ast_sorcery_object_get_id(bucket_file), '.'))) {
                /* If we don't have a file extension and were provided one in the URI, use it */
                char new_path[PATH_MAX];
+               char found_ext[PATH_MAX];
 
                ast_bucket_file_metadata_set(bucket_file, "ext", ext);
 
+               /* Don't pass '.' while checking for supported extension */
+               if (!ast_get_format_for_file_ext(ext + 1)) {
+                       /* If the file extension passed in the URI isn't supported check for the
+                        * extension based on the MIME type passed in the Content-Type header before
+                        * giving up.
+                        * If a match is found then retrieve the extension from the supported list
+                        * corresponding to the mime-type and use that to rename the file */
+                       struct ast_bucket_metadata *header = ast_bucket_file_metadata_get(bucket_file, "content-type");
+                       if (header) {
+                               char *mime_type = ast_strdup(header->value);
+                               if (mime_type) {
+                                       normalize_content_type_header(mime_type);
+                                       if (!ast_strlen_zero(mime_type)) {
+                                               if (ast_get_extension_for_mime_type(mime_type, found_ext, sizeof(found_ext))) {
+                                                       ext = found_ext;
+                                               }
+                                       }
+                                       ast_free(mime_type);
+                               }
+                       }
+               }
+
                snprintf(new_path, sizeof(new_path), "%s%s", bucket_file->path, ext);
                rename(bucket_file->path, new_path);
                ast_copy_string(bucket_file->path, new_path, sizeof(bucket_file->path));
index eba7ecc618299899360a3a10d9fe004a1c294ace..bca576372c3f6b241f341a387075c0b9183127bd 100644 (file)
@@ -84,6 +84,7 @@ static size_t curl_header_callback(char *buffer, size_t size, size_t nitems, voi
        if (strcasecmp(header, "ETag")
                && strcasecmp(header, "Cache-Control")
                && strcasecmp(header, "Last-Modified")
+               && strcasecmp(header, "Content-Type")
                && strcasecmp(header, "Expires")) {
                return realsize;
        }