@@ -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);
}