[vlc-devel] upgrade MPEG DASH handling of relative URLs

Robert Forsman bob.forsman at ericsson.com
Tue Feb 14 22:08:46 CET 2012


I started with
commit 1a459c1ccb0d617580ec97bae992f01a799a5e9b
and applied the "download and buffer" patches for DASH

http://www-itec.uni-klu.ac.at/dash/?p=434

although I'm not sure which of the items on
http://www-itec.uni-klu.ac.at/dash/?page_id=6 is the download and
buffer patches, maybe they got retracted.

Anyway, I decided that VLC's DASH plugin was a little too inflexible in
resolving URLs for media files, so I rehacked things a little.  I added
logic to fall back on the URL of the MPD file in case the MPD had no
<BaseURL> tags (which I anticipate will be 99% of MPDs in the future).


This is my first patch for VLC, and my first C++ in years, so you'll
want to give it a shakedown.


diff --git a/modules/stream_filter/dash/Modules.am b/modules/stream_filter/dash/Modules.am
index 749dc52..c17d348 100644
--- a/modules/stream_filter/dash/Modules.am
+++ b/modules/stream_filter/dash/Modules.am
@@ -22,6 +22,7 @@ SOURCES_stream_filter_dash = \
     http/IHTTPConnection.h \
     mpd/AdaptationSet.cpp \
     mpd/AdaptationSet.h \
+    mpd/BaseUrl.cpp \
     mpd/BaseUrl.h \
     mpd/BasicCMManager.cpp \
     mpd/BasicCMManager.h \
diff --git a/modules/stream_filter/dash/exceptions/MalformedURLException.h b/modules/stream_filter/dash/exceptions/MalformedURLException.h
new file mode 100644
index 0000000..41e989f
--- /dev/null
+++ b/modules/stream_filter/dash/exceptions/MalformedURLException.h
@@ -0,0 +1,21 @@
+
+#ifndef MALFORMEDURLEXCEPTION_H_
+#define MALFORMEDURLEXCEPTION_H_
+
+#include <stdexcept>
+
+namespace dash
+{
+    namespace exception
+    {
+        class MalformedURLException : public std::exception
+	    {
+		const char *message;
+            public:
+		MalformedURLException(const char *msg)  { message=msg; }
+		const char * what() const throw() { return message; }
+	    };
+    }
+}
+
+#endif /* MALFORMEDURLEXCEPTION_H_ */
diff --git a/modules/stream_filter/dash/mpd/BaseUrl.cpp b/modules/stream_filter/dash/mpd/BaseUrl.cpp
new file mode 100644
index 0000000..bf07804
--- /dev/null
+++ b/modules/stream_filter/dash/mpd/BaseUrl.cpp
@@ -0,0 +1,53 @@
+#include "../exceptions/MalformedURLException.h"
+#include "BaseUrl.h"
+
+
+std::string  dash::mpd::BaseUrl::maybeRelative(std::string spec) const
+{
+    if (spec[0] == '/') {
+	return protoHostPort()+spec;
+
+    } else if (hasProtocol(spec)) {
+	return spec;
+	
+    } else {
+	int idx = url.rfind('/');
+	if (idx<0)
+	    throw dash::exception::MalformedURLException(url.c_str());
+
+	return url.substr(0, idx+1) + spec;
+    }
+}
+
+/**
+returns the http://host:port without a trailing /
+ */
+std::string dash::mpd::BaseUrl::protoHostPort() const
+{
+    int i1 = url.find("://");
+    if (i1<0)
+	throw dash::exception::MalformedURLException(url.c_str());
+    int i2 = url.find('/', i1+3);
+    if (i2<0)
+	throw dash::exception::MalformedURLException(url.c_str());
+
+    return url.substr(0, i2);
+}
+
+/**
+   if spec matches /^[a-z]+:/ then it has a "protocol" on the front
+ */
+bool dash::mpd::BaseUrl::hasProtocol(const std::string &spec)
+{
+    for (int i=0; i<spec.length(); i++) {
+	char ch = spec[i];
+	// yeah, I'm just faking it.  Maybe read the RFC later.
+	if (ch == ':') {
+	    return i>0;
+	} else if ((ch<'a' || ch > 'z') && (ch < 'A' || ch > 'Z')) {
+	    return false;
+	} 
+    }
+
+    return false;
+}
diff --git a/modules/stream_filter/dash/mpd/BaseUrl.h b/modules/stream_filter/dash/mpd/BaseUrl.h
index f7b5548..da1ef84 100644
--- a/modules/stream_filter/dash/mpd/BaseUrl.h
+++ b/modules/stream_filter/dash/mpd/BaseUrl.h
@@ -38,7 +38,11 @@ namespace dash
                 virtual ~BaseUrl()                  {}
 
                 const std::string& getUrl() const { return this->url; }
+		std::string maybeRelative(std::string spec) const;
 
+		std::string protoHostPort() const;
+
+		static bool hasProtocol(const std::string&);
             private:
                 std::string url;
         };
diff --git a/modules/stream_filter/dash/mpd/BasicCMParser.cpp b/modules/stream_filter/dash/mpd/BasicCMParser.cpp
index fc0b0ea..146eebc 100644
--- a/modules/stream_filter/dash/mpd/BasicCMParser.cpp
+++ b/modules/stream_filter/dash/mpd/BasicCMParser.cpp
@@ -147,10 +147,18 @@ void    BasicCMParser::setMPDBaseUrl        (Node *root)
 {
     std::vector<Node *> baseUrls = DOMHelper::getChildElementByTagName(root, "BaseURL");
 
-    for(size_t i = 0; i < baseUrls.size(); i++)
-    {
-        BaseUrl *url = new BaseUrl(baseUrls.at(i)->getText());
-        this->mpd->addBaseUrl(url);
+    if (baseUrls.size() >0) {
+
+	for(size_t i = 0; i < baseUrls.size(); i++)
+	    {
+		BaseUrl *url = new BaseUrl(baseUrls.at(i)->getText());
+		this->mpd->addBaseUrl(url);
+	    } 
+    } else {
+	std::string url_ = std::string(p_stream->psz_access)+"://"+p_stream->psz_path;
+	//url_ = url_.substr(0, url_.rfind('/')+1);
+	BaseUrl *url = new BaseUrl(url_);
+	this->mpd->addBaseUrl(url);
     }
 }
 
@@ -224,7 +232,15 @@ void BasicCMParser::parseSegmentInfoCommon(Node *node, SegmentInfoCommon *segmen
             segmentInfo->appendBaseURL( (*it)->getText() );
             ++it;
         }
+    } else {
+	std::vector<BaseUrl*> inherited = mpd->getBaseUrls();
+	std::vector<BaseUrl*>::const_iterator it = inherited.begin();
+	std::vector<BaseUrl*>::const_iterator end = inherited.end();
+	while (it != end) {
+	    segmentInfo->appendBaseURL((*it)->getUrl());
+	}
     }
+
     std::map<std::string, std::string>::const_iterator  it = attr.begin();
 
     this->setInitSegment( node, segmentInfo );
diff --git a/modules/stream_filter/dash/mpd/IsoffMainParser.cpp b/modules/stream_filter/dash/mpd/IsoffMainParser.cpp
index 888c347..87e3bcd 100644
--- a/modules/stream_filter/dash/mpd/IsoffMainParser.cpp
+++ b/modules/stream_filter/dash/mpd/IsoffMainParser.cpp
@@ -79,10 +79,18 @@ void    IsoffMainParser::setMPDBaseUrl      ()
 {
     std::vector<Node *> baseUrls = DOMHelper::getChildElementByTagName(this->root, "BaseURL");
 
-    for(size_t i = 0; i < baseUrls.size(); i++)
-    {
-        BaseUrl *url = new BaseUrl(baseUrls.at(i)->getText());
-        this->mpd->addBaseUrl(url);
+    if (baseUrls.size() >0) {
+
+	for(size_t i = 0; i < baseUrls.size(); i++)
+	    {
+		BaseUrl *url = new BaseUrl(baseUrls.at(i)->getText());
+		this->mpd->addBaseUrl(url);
+	    }
+    } else {
+	std::string url_ = std::string(p_stream->psz_access)+"://"+p_stream->psz_path;
+	//url_ = url_.substr(0, url_.rfind('/')+1);
+	BaseUrl *url = new BaseUrl(url_);
+	this->mpd->addBaseUrl(url);
     }
 }
 void    IsoffMainParser::setPeriods         ()
diff --git a/modules/stream_filter/dash/mpd/Segment.cpp b/modules/stream_filter/dash/mpd/Segment.cpp
index aa0f834..0959be4 100644
--- a/modules/stream_filter/dash/mpd/Segment.cpp
+++ b/modules/stream_filter/dash/mpd/Segment.cpp
@@ -97,16 +97,13 @@ dash::http::Chunk*      Segment::toChunk        ()
 
     if(this->baseUrls.size() > 0)
     {
-        std::stringstream ss;
-        ss << this->baseUrls.at(0)->getUrl() << this->sourceUrl;
-        chunk->setUrl(ss.str());
-        ss.clear();
+	std::string resolved = this->baseUrls.at(0) ->maybeRelative(this->sourceUrl) ;
+        chunk->setUrl(resolved);
 
         for(size_t i = 1; i < this->baseUrls.size(); i++)
         {
-            ss << this->baseUrls.at(i)->getUrl() << this->sourceUrl;
-            chunk->addOptionalUrl(ss.str());
-            ss.clear();
+	    resolved = this->baseUrls.at(i) ->maybeRelative(this->sourceUrl);
+            chunk->addOptionalUrl(resolved);
         }
     }
     else



More information about the vlc-devel mailing list