diff mbox

[FFmpeg-devel] Fix for ticket 6658 (Dash demuxer segfault)

Message ID DM5PR22MB068141519F409424E0A8C3C4FE570@DM5PR22MB0681.namprd22.prod.outlook.com
State Superseded
Headers show

Commit Message

Colin NG Nov. 9, 2017, 12:19 a.m. UTC
- Add a function to handle the base URL Processing described in section 5.6.5 of IEC_23009-1.

- Fix for downloading content with byte range selection

Comments

Moritz Barsnick Nov. 12, 2017, 9:31 p.m. UTC | #1
On Thu, Nov 09, 2017 at 00:19:33 +0000, Colin NG wrote:

Before the next attempt, please do have a look at the docs about
ffmpeg's programming style, especially bracket placement.

> +static int isLocal(char *url) {
> +
> +    if (av_strstart(url, "http://", NULL) || av_strstart(url, "https://", NULL))

Apart from the fact that I'm not sure whether such a function doesn't
already exist:

> +    {
> +        return FALSE;
> +    }
> +
> +    return TRUE;
> +}

TRUE/FALSE? Are you sure? Apart from that, a ternary check would be
simpler.

> -    ret = s->io_open(s, pb, url, AVIO_FLAG_READ, &tmp);
> +    {
> +        av_freep(pb);
> +        AVDictionary *opts = NULL;
> +        set_httpheader_options(c, opts);
> +        ret = avio_open2(pb, url, AVIO_FLAG_READ, c->interrupt_callback, &opts);
> +        av_dict_free(&opts);
> +        if (ret < 0)
> +            return ret;
> +    }

Why a separate block?

>      if (ret >= 0) {

And the return above obsoletes this check.

> +    char *path = malloc(MAX_URL_SIZE);

ffmpeg has its own malloc variants, and you MUST check the result.

> -    for (i = 0; i < n_baseurl_nodes; ++i) {
> +    for (i = 0; i < n_baseurl_nodes; i++) {

Why this change? (Yes, the latter is the more correct style, but
doesn't have anything to do with your patch.)

>      }
> +
>      if (rep_bandwidth_val && tmp_str[0] != '\0') {

Why this change?

> -    } else if (!av_strcasecmp(fragmenturl_node->name, (const char *)"SegmentURL")) {
> +    }
> +    else if (!av_strcasecmp(fragmenturl_node->name, (const char *)"SegmentURL")) {

Why this change?

>          }
> +
>          representation_segmenttemplate_node = find_child_node_by_name(representation_node, "SegmentTemplate");

Why this change?

> +            av_log(s, AV_LOG_INFO, "representation_segmentlist_node \n");

And why whitespace before the line break?

>                                                  adaptionset_baseurl_node);
> -            if (ret < 0) {
> +             if (ret < 0) {
>                  return ret;

Why this change?

>          close_in = 1;
> -
>          set_httpheader_options(c, opts);

Why this change?

> +        if ((mpd_baseurl_node = find_child_node_by_name(node, "BaseURL")) == NULL)
> +        {
> +            mpd_baseurl_node = xmlNewNode(node, "BaseURL");
> +         }

Watch your bracket style, and your indentation.


... and probably a lot of other issues which I cannot judge.

Moritz
diff mbox

Patch

diff --git a/libavformat/dashdec.c b/libavformat/dashdec.c
index f63f1ff..19ef6f8 100644
--- a/libavformat/dashdec.c
+++ b/libavformat/dashdec.c
@@ -349,6 +349,16 @@  static void update_options(char **dest, const char *name, void *src)
         av_freep(dest);
 }
 
+static int isLocal(char *url) {
+
+    if (av_strstart(url, "http://", NULL) || av_strstart(url, "https://", NULL))
+    {
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
 static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url,
                     AVDictionary *opts, AVDictionary *opts2, int *is_http)
 {
@@ -392,7 +402,16 @@  static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url,
     else if (strcmp(proto_name, "file") || !strncmp(url, "file,", 5))
         return AVERROR_INVALIDDATA;
 
-    ret = s->io_open(s, pb, url, AVIO_FLAG_READ, &tmp);
+    {
+        av_freep(pb);
+        AVDictionary *opts = NULL;
+        set_httpheader_options(c, opts);
+        ret = avio_open2(pb, url, AVIO_FLAG_READ, c->interrupt_callback, &opts);
+        av_dict_free(&opts);
+        if (ret < 0)
+            return ret;
+    }
+
     if (ret >= 0) {
         // update cookies on http response with setcookies.
         char *new_cookies = NULL;
@@ -416,11 +435,156 @@  static int open_url(AVFormatContext *s, AVIOContext **pb, const char *url,
     return ret;
 }
 
-static char *get_content_url(xmlNodePtr *baseurl_nodes,
-                             int n_baseurl_nodes,
-                             char *rep_id_val,
-                             char *rep_bandwidth_val,
-                             char *val)
+
+static int resolve_content_path(AVFormatContext *s, const char *url,  xmlNodePtr *baseurl_nodes,  int n_baseurl_nodes) {
+
+    int i;
+    char *text;
+    char tmp_str[MAX_URL_SIZE];
+    char tmp_str_2[MAX_URL_SIZE];
+
+    char *path = malloc(MAX_URL_SIZE);
+    int nameSize = 0;
+    int updated = 0;
+
+    av_strlcpy(tmp_str, url, strlen(url)+1);
+    char *mpdName = strtok (tmp_str," /");
+
+    while ((mpdName =strtok (NULL, "/")) != NULL)
+    {
+        nameSize = strlen(mpdName);
+    }
+
+    memset(path, 0, MAX_URL_SIZE* sizeof(char));
+    av_strlcpy (path, url, strlen(url)-nameSize+1 );
+
+    int rootId = 0;
+    xmlNodePtr  *node = NULL;
+    for (rootId = n_baseurl_nodes-1; rootId >0; rootId--)
+    {
+        if ((node = baseurl_nodes[rootId])== NULL) continue;
+
+        if (isLocal(xmlNodeGetContent(node)) == FALSE)  {
+            break;
+        }
+    }
+
+    node = baseurl_nodes[rootId];
+    char *baseurl = xmlNodeGetContent(node);
+    char *root_url = (!av_strcasecmp(baseurl, ""))? path: baseurl;
+
+    if (node)  {
+        xmlNodeSetContent(node, root_url);
+    }
+
+    int size = strlen(root_url);
+    int isRootLocal = isLocal(root_url);
+
+    char token ='/';
+    if (strncmp(&root_url[size-1],&token, 1) != 0)
+    {
+        strcat(root_url, "/");
+        size++;
+    }
+
+    for (i = 0; i < n_baseurl_nodes; ++i)
+    {
+        if (i==rootId) continue;
+        text = xmlNodeGetContent(baseurl_nodes[i]);
+        if (text)
+        {
+            memset(tmp_str, 0, sizeof(tmp_str));
+
+            if (isLocal(text) == TRUE && isRootLocal == FALSE)
+            {
+                av_strlcpy(tmp_str, root_url, size+1);
+            }
+            if (text)
+            {
+                int start = (strncmp(text, &token, 1) == 0) ? 1: 0;
+                memset(tmp_str_2, 0, sizeof(tmp_str_2));
+                av_strlcat(tmp_str, text+start, MAX_URL_SIZE);
+                xmlFree(text);
+            }
+            xmlNodeSetContent(baseurl_nodes[i], tmp_str);
+            updated = 1;
+        }
+    }
+
+    free(path);
+    return updated;
+
+}
+
+static int av_strchr(const char *str, char tok) {
+
+    char * pch;
+    int *pos;
+    int ctr = 0;
+    pch=strchr(str, tok);
+    pos = av_mallocz( strlen(str)*sizeof(int));
+    while (pch!=NULL)  {
+        pos[ctr] = pch-str+1;
+        pch=strchr(pch+1, tok);
+        ctr++;
+    }
+
+    return pos[ctr-2];
+}
+static void updatePath(AVFormatContext *s, char *baseURL, char *locale) {
+
+    int len = strlen(baseURL);
+    int upDir=0;
+
+    if (!locale  || isLocal(locale) == FALSE)
+        return;
+
+    char curDirChar[] = "./";
+    char parentDirChar[] = "../";
+
+    while (av_strstart(locale, curDirChar, NULL) == TRUE) {
+        locale = av_strireplace(locale, curDirChar, "");
+    }
+
+    while (av_strstart(locale, parentDirChar, NULL) == TRUE) {
+        locale = av_strireplace(locale, parentDirChar, "");
+        upDir++;
+    }
+
+    char *pch = strchr (baseURL, '/');
+
+    if (upDir == 0 || pch == NULL)
+        goto finish;
+
+    int depth = 0;
+    int *marker = (int *)av_mallocz(strlen(baseURL)*sizeof(int));
+
+    memset(marker, 0, strlen(baseURL)*sizeof(int));
+
+    do {
+        marker[depth] = pch-baseURL+1;
+        pch = strchr (pch+1, '/');
+        depth++;
+    } while (pch != NULL);
+
+    if (pch-baseURL+1 < strlen(baseURL))
+        marker[depth] = pch-baseURL+1;
+    else
+        depth--;
+
+    len = marker[depth-upDir];
+    free(marker);
+
+finish:
+    strcpy(baseURL+len, locale);
+
+}
+
+static char *get_content_url(AVFormatContext *s, xmlNodePtr *baseurl_nodes,
+                                            int n_baseurl_nodes,
+                                            char *rep_id_val,
+                                            char *rep_bandwidth_val,
+                                            char *val)
 {
     int i;
     char *text;
@@ -430,7 +594,7 @@  static char *get_content_url(xmlNodePtr *baseurl_nodes,
 
     memset(tmp_str, 0, sizeof(tmp_str));
 
-    for (i = 0; i < n_baseurl_nodes; ++i) {
+    for (i = 0; i < n_baseurl_nodes; i++) {
         if (baseurl_nodes[i] &&
             baseurl_nodes[i]->children &&
             baseurl_nodes[i]->children->type == XML_TEXT_NODE) {
@@ -445,23 +609,25 @@  static char *get_content_url(xmlNodePtr *baseurl_nodes,
         }
     }
 
-    if (val)
-        av_strlcat(tmp_str, (const char*)val, sizeof(tmp_str));
+   updatePath(s, tmp_str,  val);
 
     if (rep_id_val) {
         url = av_strireplace(tmp_str, "$RepresentationID$", (const char*)rep_id_val);
         if (!url) {
             return NULL;
         }
-        av_strlcpy(tmp_str, url, sizeof(tmp_str));
-        av_free(url);
+        av_strlcpy(tmp_str, url, strlen(url)+1);
     }
+
     if (rep_bandwidth_val && tmp_str[0] != '\0') {
         url = av_strireplace(tmp_str, "$Bandwidth$", (const char*)rep_bandwidth_val);
         if (!url) {
             return NULL;
         }
     }
+
+    av_strlcpy(url, tmp_str, strlen(tmp_str)+1);
+
     return url;
 }
 
@@ -522,55 +688,82 @@  static enum AVMediaType get_content_type(xmlNodePtr node)
     return type;
 }
 
+static struct fragment * getFragment(char *range)
+{
+    struct fragment * seg =  av_mallocz(sizeof(struct fragment));
+
+    memset(seg, 0, sizeof(struct fragment));
+    seg->size = -1;
+    if (range)  {
+        char *str_end_offset;
+        char *str_offset = av_strtok(range, "-", &str_end_offset);
+        seg->url_offset = strtoll(str_offset, NULL, 10);
+        seg->size = strtoll(str_end_offset, NULL, 10) -seg->url_offset;
+    }
+
+    return seg;
+}
+
 static int parse_manifest_segmenturlnode(AVFormatContext *s, struct representation *rep,
                                          xmlNodePtr fragmenturl_node,
                                          xmlNodePtr *baseurl_nodes,
                                          char *rep_id_val,
                                          char *rep_bandwidth_val)
 {
-    char *initialization_val = NULL;
+    char *source_val = NULL;
     char *media_val = NULL;
+    char *range_val = NULL;
 
     if (!av_strcasecmp(fragmenturl_node->name, (const char *)"Initialization")) {
-        initialization_val = xmlGetProp(fragmenturl_node, "sourceURL");
-        if (initialization_val) {
-            rep->init_section = av_mallocz(sizeof(struct fragment));
+
+        source_val = xmlGetProp(fragmenturl_node, "sourceURL");
+        range_val = xmlGetProp(fragmenturl_node, "range"); // byte range on
+        if (source_val || range_val) {
+            rep->init_section = getFragment(range_val);
+
             if (!rep->init_section) {
-                xmlFree(initialization_val);
+                xmlFree(source_val);
+                xmlFree(range_val);
                 return AVERROR(ENOMEM);
             }
-            rep->init_section->url = get_content_url(baseurl_nodes, 4,
-                                                     rep_id_val,
-                                                     rep_bandwidth_val,
-                                                     initialization_val);
+            rep->init_section->url = get_content_url(s, baseurl_nodes, 4,
+                                                                            rep_id_val,
+                                                                            rep_bandwidth_val,
+                                                                            source_val);
             if (!rep->init_section->url) {
                 av_free(rep->init_section);
-                xmlFree(initialization_val);
+                xmlFree(source_val);
+                xmlFree(range_val);
                 return AVERROR(ENOMEM);
-            }
-            rep->init_section->size = -1;
-            xmlFree(initialization_val);
+             }
+            xmlFree(source_val);
+            xmlFree(range_val);
         }
-    } else if (!av_strcasecmp(fragmenturl_node->name, (const char *)"SegmentURL")) {
+    }
+    else if (!av_strcasecmp(fragmenturl_node->name, (const char *)"SegmentURL")) {
         media_val = xmlGetProp(fragmenturl_node, "media");
-        if (media_val) {
-            struct fragment *seg = av_mallocz(sizeof(struct fragment));
+        range_val = xmlGetProp(fragmenturl_node, "mediaRange");
+
+        if (media_val  || range_val) {
+            struct fragment *seg = getFragment(range_val);
             if (!seg) {
                 xmlFree(media_val);
+                xmlFree(range_val);
                 return AVERROR(ENOMEM);
             }
-            seg->url = get_content_url(baseurl_nodes, 4,
-                                       rep_id_val,
-                                       rep_bandwidth_val,
-                                       media_val);
+            seg->url = get_content_url(s, baseurl_nodes, 4,
+                                                        rep_id_val,
+                                                        rep_bandwidth_val,
+                                                        media_val);
             if (!seg->url) {
                 av_free(seg);
                 xmlFree(media_val);
+                xmlFree(range_val);
                 return AVERROR(ENOMEM);
             }
-            seg->size = -1;
             dynarray_add(&rep->fragments, &rep->n_fragments, seg);
             xmlFree(media_val);
+            xmlFree(range_val);
         }
     }
 
@@ -663,6 +856,7 @@  static int parse_manifest_representation(AVFormatContext *s, const char *url,
             ret = AVERROR(ENOMEM);
             goto end;
         }
+
         representation_segmenttemplate_node = find_child_node_by_name(representation_node, "SegmentTemplate");
         representation_baseurl_node = find_child_node_by_name(representation_node, "BaseURL");
         representation_segmentlist_node = find_child_node_by_name(representation_node, "SegmentList");
@@ -672,6 +866,8 @@  static int parse_manifest_representation(AVFormatContext *s, const char *url,
         baseurl_nodes[2] = adaptionset_baseurl_node;
         baseurl_nodes[3] = representation_baseurl_node;
 
+        resolve_content_path(s, url, baseurl_nodes, 4);
+
         if (representation_segmenttemplate_node || fragment_template_node) {
             fragment_timeline_node = NULL;
             fragment_templates_tab[0] = representation_segmenttemplate_node;
@@ -691,7 +887,7 @@  static int parse_manifest_representation(AVFormatContext *s, const char *url,
                     ret = AVERROR(ENOMEM);
                     goto end;
                 }
-                rep->init_section->url = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, initialization_val);
+                rep->init_section->url = get_content_url(s, baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, initialization_val);
                 if (!rep->init_section->url) {
                     av_free(rep->init_section);
                     av_free(rep);
@@ -703,7 +899,8 @@  static int parse_manifest_representation(AVFormatContext *s, const char *url,
             }
 
             if (media_val) {
-                rep->url_template = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, media_val);
+                rep->url_template = get_content_url(s, baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, media_val);
+               av_log(s, AV_LOG_DEBUG, "rep->url_template  = %s\n", rep->url_template);
                 xmlFree(media_val);
             }
 
@@ -744,7 +941,9 @@  static int parse_manifest_representation(AVFormatContext *s, const char *url,
                 ret = AVERROR(ENOMEM);
                 goto end;
             }
-            seg->url = get_content_url(baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, NULL);
+            seg->url = get_content_url(s, baseurl_nodes, 4, rep_id_val, rep_bandwidth_val, NULL);
+            av_log(s, AV_LOG_DEBUG, "seg->url = %s\n", seg->url);
+
             if (!seg->url) {
                 av_free(seg);
                 ret = AVERROR(ENOMEM);
@@ -755,6 +954,8 @@  static int parse_manifest_representation(AVFormatContext *s, const char *url,
         } else if (representation_segmentlist_node) {
             // TODO: https://www.brendanlong.com/the-structure-of-an-mpeg-dash-mpd.html
             // http://www-itec.uni-klu.ac.at/dash/ddash/mpdGenerator.php?fragmentlength=15&type=full
+
+            av_log(s, AV_LOG_INFO, "representation_segmentlist_node \n");
             xmlNodePtr fragmenturl_node = NULL;
             duration_val = xmlGetProp(representation_segmentlist_node, "duration");
             timescale_val = xmlGetProp(representation_segmentlist_node, "timescale");
@@ -836,6 +1037,7 @@  static int parse_manifest_adaptationset(AVFormatContext *s, const char *url,
 
     node = xmlFirstElementChild(adaptionset_node);
     while (node) {
+       av_log(s, AV_LOG_INFO, "node name[%s] type[%d]\n",  node->name, (int)node->type);
         if (!av_strcasecmp(node->name, (const char *)"SegmentTemplate")) {
             fragment_template_node = node;
         } else if (!av_strcasecmp(node->name, (const char *)"ContentComponent")) {
@@ -850,7 +1052,7 @@  static int parse_manifest_adaptationset(AVFormatContext *s, const char *url,
                                                 fragment_template_node,
                                                 content_component_node,
                                                 adaptionset_baseurl_node);
-            if (ret < 0) {
+             if (ret < 0) {
                 return ret;
             }
         }
@@ -884,7 +1086,6 @@  static int parse_manifest(AVFormatContext *s, const char *url, AVIOContext *in)
 
     if (!in) {
         close_in = 1;
-
         set_httpheader_options(c, opts);
         ret = avio_open2(&in, url, AVIO_FLAG_READ, c->interrupt_callback, &opts);
         av_dict_free(&opts);
@@ -966,7 +1167,10 @@  static int parse_manifest(AVFormatContext *s, const char *url, AVIOContext *in)
             xmlFree(val);
         }
 
-        mpd_baseurl_node = find_child_node_by_name(node, "BaseURL");
+        if ((mpd_baseurl_node = find_child_node_by_name(node, "BaseURL")) == NULL)
+        {
+            mpd_baseurl_node = xmlNewNode(node, "BaseURL");
+         }
 
         // at now we can handle only one period, with the longest duration
         node = xmlFirstElementChild(node);
@@ -1285,7 +1489,7 @@  static int read_from_url(struct representation *pls, struct fragment *seg,
     if (mode == READ_COMPLETE) {
         ret = avio_read(pls->input, buf, buf_size);
         if (ret < buf_size) {
-            av_log(pls->parent, AV_LOG_WARNING, "Could not read complete fragment.\n");
+            av_log(pls->parent, AV_LOG_WARNING, "Could not read complete fragment. Data Request = %d Data read = %d \n", buf_size, ret);
         }
     } else {
         ret = avio_read(pls->input, buf, buf_size);
@@ -1306,6 +1510,7 @@  static int open_input(DASHContext *c, struct representation *pls, struct fragmen
     if (seg->size >= 0) {
         /* try to restrict the HTTP request to the part we want
          * (if this is in fact a HTTP request) */
+        av_log(pls->parent, AV_LOG_DEBUG, "open_input offset=%d end_offset=%d \n", seg->url_offset, seg->url_offset + seg->size);
         av_dict_set_int(&opts, "offset", seg->url_offset, 0);
         av_dict_set_int(&opts, "end_offset", seg->url_offset + seg->size, 0);
     }