Commit e9219d7f authored by François Cartegnie's avatar François Cartegnie 🤞

demux: adaptive: use cookies jar

Requires an ugly unified reference
pass between abstraction layers :/

refs #9632
parent 6dee0982
......@@ -326,6 +326,8 @@ libadaptive_plugin_la_SOURCES = \
demux/adaptive/logic/Representationselectors.cpp \
demux/adaptive/mp4/AtomsReader.cpp \
demux/adaptive/mp4/AtomsReader.hpp \
demux/adaptive/http/AuthStorage.cpp \
demux/adaptive/http/AuthStorage.hpp \
demux/adaptive/http/BytesRange.cpp \
demux/adaptive/http/BytesRange.hpp \
demux/adaptive/http/Chunk.cpp \
......
......@@ -30,6 +30,7 @@
#include "playlist/BaseAdaptationSet.h"
#include "playlist/BaseRepresentation.h"
#include "http/HTTPConnectionManager.h"
#include "http/AuthStorage.hpp"
#include "logic/AlwaysBestAdaptationLogic.h"
#include "logic/RateBasedAdaptationLogic.h"
#include "logic/AlwaysLowestAdaptationLogic.hpp"
......@@ -48,6 +49,7 @@ using namespace adaptive::logic;
using namespace adaptive;
PlaylistManager::PlaylistManager( demux_t *p_demux_,
AuthStorage *auth,
AbstractPlaylist *pl,
AbstractStreamFactory *factory,
AbstractAdaptationLogic::LogicType type ) :
......@@ -59,6 +61,7 @@ PlaylistManager::PlaylistManager( demux_t *p_demux_,
p_demux ( p_demux_ )
{
currentPeriod = playlist->getFirstPeriod();
authStorage = auth;
failedupdates = 0;
b_thread = false;
b_buffering = false;
......@@ -83,6 +86,7 @@ PlaylistManager::~PlaylistManager ()
delete playlist;
delete conManager;
delete logic;
delete authStorage;
vlc_cond_destroy(&waitcond);
vlc_mutex_destroy(&lock);
vlc_mutex_destroy(&demux.lock);
......@@ -150,7 +154,10 @@ bool PlaylistManager::setupPeriod()
bool PlaylistManager::start()
{
if(!conManager && !(conManager = new (std::nothrow) HTTPConnectionManager(VLC_OBJECT(p_demux->s))))
if(!conManager &&
!(conManager =
new (std::nothrow) HTTPConnectionManager(VLC_OBJECT(p_demux->s), authStorage))
)
return false;
if(!setupPeriod())
......
......@@ -37,6 +37,7 @@ namespace adaptive
namespace http
{
class AbstractConnectionManager;
class AuthStorage;
}
using namespace playlist;
......@@ -46,7 +47,9 @@ namespace adaptive
class PlaylistManager
{
public:
PlaylistManager( demux_t *, AbstractPlaylist *,
PlaylistManager( demux_t *,
AuthStorage *,
AbstractPlaylist *,
AbstractStreamFactory *,
AbstractAdaptationLogic::LogicType type );
virtual ~PlaylistManager ();
......@@ -91,6 +94,7 @@ namespace adaptive
virtual AbstractAdaptationLogic *createLogic(AbstractAdaptationLogic::LogicType,
AbstractConnectionManager *);
AuthStorage *authStorage;
AbstractConnectionManager *conManager;
AbstractAdaptationLogic::LogicType logicType;
AbstractAdaptationLogic *logic;
......
......@@ -32,6 +32,7 @@
#include <vlc_plugin.h>
#include <vlc_demux.h>
#include "http/AuthStorage.hpp"
#include "playlist/BasePeriod.h"
#include "xml/DOMParser.h"
......@@ -126,9 +127,9 @@ vlc_module_end ()
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static PlaylistManager * HandleDash(demux_t *, DOMParser &,
static PlaylistManager * HandleDash(demux_t *, AuthStorage *auth, DOMParser &,
const std::string &, AbstractAdaptationLogic::LogicType);
static PlaylistManager * HandleSmooth(demux_t *, DOMParser &,
static PlaylistManager * HandleSmooth(demux_t *, AuthStorage *auth, DOMParser &,
const std::string &, AbstractAdaptationLogic::LogicType);
/*****************************************************************************
......@@ -151,6 +152,7 @@ static int Open(vlc_object_t *p_obj)
}
PlaylistManager *p_manager = NULL;
AuthStorage *authStorage = new AuthStorage();
char *psz_logic = var_InheritString(p_obj, "adaptive-logic");
AbstractAdaptationLogic::LogicType logic = AbstractAdaptationLogic::Default;
......@@ -174,15 +176,16 @@ static int Open(vlc_object_t *p_obj)
if(!dashmime && !smoothmime && HLSManager::isHTTPLiveStreaming(p_demux->s))
{
M3U8Parser parser;
M3U8Parser parser(authStorage);
M3U8 *p_playlist = parser.parse(VLC_OBJECT(p_demux),p_demux->s, playlisturl);
if(!p_playlist)
{
msg_Err( p_demux, "Could not parse playlist" );
delete authStorage;
return VLC_EGENERIC;
}
p_manager = new (std::nothrow) HLSManager(p_demux, p_playlist,
p_manager = new (std::nothrow) HLSManager(p_demux, authStorage, p_playlist,
new (std::nothrow) HLSStreamFactory, logic);
}
else
......@@ -191,11 +194,11 @@ static int Open(vlc_object_t *p_obj)
DOMParser xmlParser; /* Share that xml reader */
if(dashmime)
{
p_manager = HandleDash(p_demux, xmlParser, playlisturl, logic);
p_manager = HandleDash(p_demux, authStorage, xmlParser, playlisturl, logic);
}
else if(smoothmime)
{
p_manager = HandleSmooth(p_demux, xmlParser, playlisturl, logic);
p_manager = HandleSmooth(p_demux, authStorage, xmlParser, playlisturl, logic);
}
else
{
......@@ -211,11 +214,11 @@ static int Open(vlc_object_t *p_obj)
{
if(DASHManager::isDASH(xmlParser.getRootNode()))
{
p_manager = HandleDash(p_demux, xmlParser, playlisturl, logic);
p_manager = HandleDash(p_demux, authStorage, xmlParser, playlisturl, logic);
}
else if(SmoothManager::isSmoothStreaming(xmlParser.getRootNode()))
{
p_manager = HandleSmooth(p_demux, xmlParser, playlisturl, logic);
p_manager = HandleSmooth(p_demux, authStorage, xmlParser, playlisturl, logic);
}
}
vlc_stream_Delete(peekstream);
......@@ -224,7 +227,12 @@ static int Open(vlc_object_t *p_obj)
}
}
if(!p_manager || !p_manager->start())
if(!p_manager)
{
delete authStorage;
return VLC_EGENERIC;
}
else if(!p_manager->start())
{
delete p_manager;
return VLC_EGENERIC;
......@@ -254,7 +262,8 @@ static void Close(vlc_object_t *p_obj)
/*****************************************************************************
*
*****************************************************************************/
static PlaylistManager * HandleDash(demux_t *p_demux, DOMParser &xmlParser,
static PlaylistManager * HandleDash(demux_t *p_demux,
AuthStorage *auth, DOMParser &xmlParser,
const std::string & playlisturl,
AbstractAdaptationLogic::LogicType logic)
{
......@@ -272,12 +281,13 @@ static PlaylistManager * HandleDash(demux_t *p_demux, DOMParser &xmlParser,
return NULL;
}
return new (std::nothrow) DASHManager( p_demux, p_playlist,
return new (std::nothrow) DASHManager( p_demux, auth, p_playlist,
new (std::nothrow) DASHStreamFactory,
logic );
}
static PlaylistManager * HandleSmooth(demux_t *p_demux, DOMParser &xmlParser,
static PlaylistManager * HandleSmooth(demux_t *p_demux,
AuthStorage *auth, DOMParser &xmlParser,
const std::string & playlisturl,
AbstractAdaptationLogic::LogicType logic)
{
......@@ -295,7 +305,7 @@ static PlaylistManager * HandleSmooth(demux_t *p_demux, DOMParser &xmlParser,
return NULL;
}
return new (std::nothrow) SmoothManager( p_demux, p_playlist,
return new (std::nothrow) SmoothManager( p_demux, auth, p_playlist,
new (std::nothrow) SmoothStreamFactory,
logic );
}
/*
* AuthStorage.cpp
*****************************************************************************
* Copyright (C) 2017 - VideoLabs and VideoLAN Authors
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "AuthStorage.hpp"
#include "ConnectionParams.hpp"
using namespace adaptive::http;
AuthStorage::AuthStorage()
{
p_cookies_jar = vlc_http_cookies_new();
}
AuthStorage::~AuthStorage()
{
vlc_http_cookies_destroy( p_cookies_jar );
}
void AuthStorage::addCookie( const std::string &cookie, const ConnectionParams &params )
{
if( !p_cookies_jar )
return;
vlc_http_cookies_store( p_cookies_jar, cookie.c_str(),
params.getHostname().c_str(), params.getPath().c_str() );
}
std::string AuthStorage::getCookie( const ConnectionParams &params, bool secure )
{
if( !p_cookies_jar )
return std::string();
char *psz = vlc_http_cookies_fetch( p_cookies_jar, secure,
params.getHostname().c_str(),
params.getPath().c_str() );
std::string ret;
if( psz )
{
ret = std::string(psz);
free( psz );
}
return ret;
}
/*
* AuthStorage.hpp
*****************************************************************************
* Copyright (C) 2017 - VideoLabs and VideoLAN Authors
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef AUTHSTORAGE_HPP_
#define AUTHSTORAGE_HPP_
#include <vlc_common.h>
#include <vlc_http.h>
#include <string>
namespace adaptive
{
namespace http
{
class ConnectionParams;
class AuthStorage
{
public:
AuthStorage();
~AuthStorage();
void addCookie( const std::string &cookie, const ConnectionParams & );
std::string getCookie( const ConnectionParams &, bool secure );
private:
vlc_http_cookie_jar_t *p_cookies_jar;
};
}
}
#endif
......@@ -23,6 +23,7 @@
#include "HTTPConnection.hpp"
#include "ConnectionParams.hpp"
#include "AuthStorage.hpp"
#include "Sockets.hpp"
#include "../adaptive/tools/Helper.h"
......@@ -59,13 +60,15 @@ size_t AbstractConnection::getContentLength() const
return contentLength;
}
HTTPConnection::HTTPConnection(vlc_object_t *p_object_, Socket *socket_, bool persistent)
HTTPConnection::HTTPConnection(vlc_object_t *p_object_, AuthStorage *auth,
Socket *socket_, bool persistent)
: AbstractConnection( p_object_ )
{
socket = socket_;
psz_useragent = var_InheritString(p_object_, "http-user-agent");
queryOk = false;
retries = 0;
authStorage = auth;
connectionClose = !persistent;
chunked = false;
chunked_eof = false;
......@@ -362,6 +365,10 @@ void HTTPConnection::onHeader(const std::string &key,
{
locationparams = ConnectionParams( value );
}
else if(key == "Set-Cookie" && authStorage)
{
authStorage->addCookie( value, params );
}
}
std::string HTTPConnection::buildRequestHeader(const std::string &path) const
......@@ -377,6 +384,14 @@ std::string HTTPConnection::buildRequestHeader(const std::string &path) const
{
req << "Host: " << params.getHostname() << "\r\n";
}
if(authStorage)
{
std::string cookie = authStorage->getCookie(params,
params.getScheme() == "https" ||
params.getPort() == 443);
if(!cookie.empty())
req << "Cookie: " << cookie << "\r\n";
}
req << "Cache-Control: no-cache" << "\r\n" <<
"User-Agent: " << std::string(psz_useragent) << "\r\n";
req << extraRequestHeaders();
......@@ -499,8 +514,9 @@ void StreamUrlConnection::setUsed( bool b )
reset();
}
ConnectionFactory::ConnectionFactory()
ConnectionFactory::ConnectionFactory( AuthStorage *auth )
{
authStorage = auth;
}
ConnectionFactory::~ConnectionFactory()
......@@ -521,7 +537,7 @@ AbstractConnection * ConnectionFactory::createConnection(vlc_object_t *p_object,
/* disable pipelined tls until we have ticket/resume session support */
HTTPConnection *conn = new (std::nothrow)
HTTPConnection(p_object, socket, sockettype != TLSSocket::TLS);
HTTPConnection(p_object, authStorage, socket, sockettype != TLSSocket::TLS);
if(!conn)
{
delete socket;
......@@ -531,6 +547,12 @@ AbstractConnection * ConnectionFactory::createConnection(vlc_object_t *p_object,
return conn;
}
StreamUrlConnectionFactory::StreamUrlConnectionFactory()
: ConnectionFactory( NULL )
{
}
AbstractConnection * StreamUrlConnectionFactory::createConnection(vlc_object_t *p_object,
const ConnectionParams &)
{
......
......@@ -35,6 +35,7 @@ namespace adaptive
namespace http
{
class Socket;
class AuthStorage;
class AbstractConnection
{
......@@ -63,7 +64,7 @@ namespace adaptive
class HTTPConnection : public AbstractConnection
{
public:
HTTPConnection(vlc_object_t *stream, Socket *, bool = false);
HTTPConnection(vlc_object_t *, AuthStorage *, Socket *, bool = false);
virtual ~HTTPConnection();
virtual bool canReuse (const ConnectionParams &) const;
......@@ -89,6 +90,7 @@ namespace adaptive
std::string readLine();
char * psz_useragent;
AuthStorage *authStorage;
ConnectionParams locationparams;
bool connectionClose;
bool chunked;
......@@ -123,14 +125,17 @@ namespace adaptive
class ConnectionFactory
{
public:
ConnectionFactory();
ConnectionFactory( AuthStorage * );
virtual ~ConnectionFactory();
virtual AbstractConnection * createConnection(vlc_object_t *, const ConnectionParams &);
private:
AuthStorage *authStorage;
};
class StreamUrlConnectionFactory : public ConnectionFactory
{
public:
StreamUrlConnectionFactory();
virtual AbstractConnection * createConnection(vlc_object_t *, const ConnectionParams &);
};
}
......
......@@ -31,6 +31,7 @@
#include "Sockets.hpp"
#include "Downloader.hpp"
#include <vlc_url.h>
#include <vlc_http.h>
using namespace adaptive::http;
......@@ -63,16 +64,21 @@ HTTPConnectionManager::HTTPConnectionManager (vlc_object_t *p_object_, Connec
vlc_mutex_init(&lock);
downloader = new (std::nothrow) Downloader();
downloader->start();
if(!factory_)
{
if(var_InheritBool(p_object, "adaptive-use-access"))
factory = new (std::nothrow) StreamUrlConnectionFactory();
else
factory = new (std::nothrow) ConnectionFactory();
}
factory = factory_;
}
HTTPConnectionManager::HTTPConnectionManager (vlc_object_t *p_object_, AuthStorage *storage)
: AbstractConnectionManager( p_object_ )
{
vlc_mutex_init(&lock);
downloader = new (std::nothrow) Downloader();
downloader->start();
if(var_InheritBool(p_object, "adaptive-use-access"))
factory = new (std::nothrow) StreamUrlConnectionFactory();
else
factory = factory_;
factory = new (std::nothrow) ConnectionFactory( storage );
}
HTTPConnectionManager::~HTTPConnectionManager ()
{
delete downloader;
......
......@@ -28,6 +28,7 @@
#include "../logic/IDownloadRateObserver.h"
#include <vlc_common.h>
#include <vector>
#include <string>
......@@ -38,6 +39,7 @@ namespace adaptive
class ConnectionParams;
class ConnectionFactory;
class AbstractConnection;
class AuthStorage;
class Downloader;
class AbstractChunkSource;
......@@ -64,7 +66,8 @@ namespace adaptive
class HTTPConnectionManager : public AbstractConnectionManager
{
public:
HTTPConnectionManager (vlc_object_t *p_object, ConnectionFactory * = NULL);
HTTPConnectionManager (vlc_object_t *p_object, ConnectionFactory *);
HTTPConnectionManager (vlc_object_t *p_object, AuthStorage *);
virtual ~HTTPConnectionManager ();
virtual void closeAllConnections () /* impl */;
......
......@@ -31,9 +31,9 @@
using namespace adaptive;
using namespace adaptive::http;
block_t * Retrieve::HTTP(vlc_object_t *obj, const std::string &uri)
block_t * Retrieve::HTTP(vlc_object_t *obj, AuthStorage *auth, const std::string &uri)
{
HTTPConnectionManager connManager(obj);
HTTPConnectionManager connManager(obj, auth);
HTTPChunk *datachunk;
try
{
......
......@@ -25,10 +25,15 @@
namespace adaptive
{
namespace http
{
class AuthStorage;
}
class Retrieve
{
public:
static block_t * HTTP(vlc_object_t *, const std::string &uri);
static block_t * HTTP(vlc_object_t *, http::AuthStorage *, const std::string &uri);
};
}
......
......@@ -49,10 +49,12 @@ using namespace dash;
using namespace dash::mpd;
using namespace adaptive::logic;
DASHManager::DASHManager(demux_t *demux_, MPD *mpd,
DASHManager::DASHManager(demux_t *demux_,
AuthStorage *auth,
MPD *mpd,
AbstractStreamFactory *factory,
AbstractAdaptationLogic::LogicType type) :
PlaylistManager(demux_, mpd, factory, type)
PlaylistManager(demux_, auth, mpd, factory, type)
{
}
......@@ -103,7 +105,7 @@ bool DASHManager::updatePlaylist()
url.append("://");
url.append(p_demux->psz_location);
block_t *p_block = Retrieve::HTTP(VLC_OBJECT(p_demux), url);
block_t *p_block = Retrieve::HTTP(VLC_OBJECT(p_demux), authStorage, url);
if(!p_block)
return false;
......
......@@ -44,7 +44,9 @@ namespace dash
class DASHManager : public PlaylistManager
{
public:
DASHManager( demux_t *, mpd::MPD *mpd,
DASHManager( demux_t *,
AuthStorage *,
mpd::MPD *mpd,
AbstractStreamFactory *,
logic::AbstractAdaptationLogic::LogicType type);
virtual ~DASHManager ();
......
......@@ -35,10 +35,12 @@ using namespace adaptive::logic;
using namespace hls;
using namespace hls::playlist;
HLSManager::HLSManager(demux_t *demux_, M3U8 *playlist,
HLSManager::HLSManager(demux_t *demux_,
AuthStorage *auth,
M3U8 *playlist,
AbstractStreamFactory *factory,
AbstractAdaptationLogic::LogicType type) :
PlaylistManager(demux_, playlist, factory, type)
PlaylistManager(demux_, auth, playlist, factory, type)
{
}
......
......@@ -31,7 +31,9 @@ namespace hls
class HLSManager : public PlaylistManager
{
public:
HLSManager( demux_t *, playlist::M3U8 *,
HLSManager( demux_t *,
AuthStorage *,
playlist::M3U8 *,
AbstractStreamFactory *,
logic::AbstractAdaptationLogic::LogicType type );
virtual ~HLSManager();
......
......@@ -33,9 +33,10 @@
using namespace hls::playlist;
M3U8::M3U8 (vlc_object_t *p_object) :
M3U8::M3U8 (vlc_object_t *p_object, AuthStorage *auth_) :
AbstractPlaylist(p_object)
{
auth = auth_;
minUpdatePeriod.Set( 5 * CLOCK_FREQ );
vlc_mutex_init(&keystore_lock);
}
......@@ -54,7 +55,7 @@ std::vector<uint8_t> M3U8::getEncryptionKey(const std::string &uri)
if(it == keystore.end())
{
/* Pretty bad inside the lock */
block_t *p_block = Retrieve::HTTP(p_object, uri);
block_t *p_block = Retrieve::HTTP(p_object, auth, uri);
if(p_block)
{
if(p_block->i_buffer == 16)
......@@ -104,6 +105,11 @@ bool M3U8::isLive() const
return b_live;
}
AuthStorage * M3U8::getAuth()
{
return auth;
}
void M3U8::debug()
{
std::vector<BasePeriod *>::const_iterator i;
......
......@@ -25,6 +25,14 @@
#include <vlc_threads.h>
#include <map>
namespace adaptive
{
namespace http
{
class AuthStorage;
}
}
namespace hls
{
namespace playlist
......@@ -34,14 +42,16 @@ namespace hls
class M3U8 : public AbstractPlaylist
{
public:
M3U8(vlc_object_t *);
M3U8(vlc_object_t *, adaptive::http::AuthStorage * /* ugly data ref, tobefixed */ );
virtual ~M3U8();
std::vector<uint8_t> getEncryptionKey(const std::string &);
virtual bool isLive() const;
virtual void debug();
adaptive::http::AuthStorage * getAuth(); /* ugly data ref, tobefixed */
private:
adaptive::http::AuthStorage *auth; /* ugly data ref, tobefixed */
std::string data;
vlc_mutex_t keystore_lock;
std::map<std::string, std::vector<uint8_t> > keystore;
......
......@@ -45,8 +45,9 @@ using namespace adaptive;
using namespace adaptive::playlist;
using namespace hls::playlist;
M3U8Parser::M3U8Parser()
M3U8Parser::M3U8Parser( AuthStorage *auth_ )
{
auth = auth_;
}
M3U8Parser::~M3U8Parser ()
......@@ -160,7 +161,7 @@ void M3U8Parser::createAndFillRepresentation(vlc_object_t *p_obj, BaseAdaptation
bool M3U8Parser::appendSegmentsFromPlaylistURI(vlc_object_t *p_obj, Representation *rep)
{
block_t *p_block = Retrieve::HTTP(p_obj, rep->getPlaylistUrl().toString());
block_t *p_block = Retrieve::HTTP(p_obj, auth, rep->getPlaylistUrl().toString());
if(p_block)
{
stream_t *substream = vlc_stream_MemoryNew(p_obj, p_block->p_buffer, p_block->i_buffer, true);
......@@ -384,7 +385,7 @@ M3U8 * M3U8Parser::parse(vlc_object_t *p_object, stream_t *p_stream, const std::
}
free(psz_line);
M3U8 *playlist = new (std::nothrow) M3U8(p_object);
M3U8 *playlist = new (std::nothrow) M3U8(p_object, auth);
if(!playlist)
return NULL;
......
......@@ -36,6 +36,11 @@ namespace adaptive
class BasePeriod;
class BaseAdaptationSet;
}
namespace http
{
class AuthStorage;
}
}
namespace hls
......@@ -52,7 +57,7 @@ namespace hls
class M3U8Parser
{
public:
M3U8Parser ();
M3U8Parser (AuthStorage *auth);
virtual ~M3U8Parser ();
M3U8 * parse (vlc_object_t *p_obj, stream_t *p_stream, const std::string &);
......@@ -65,6 +70,7 @@ namespace hls
void parseSegments(vlc_object_t *, Representation *, const std::list<Tag *>&);
void setFormatFromExtension(Representation *rep, const std::string &);
std::list<Tag *> parseEntries(stream_t *);
AuthStorage *auth;
};
}
}
......
......@@ -140,10 +140,13 @@ bool Representation::needsUpdate() const
bool Representation::runLocalUpdates(mtime_t, uint64_t number, bool prune)
{
const time_t now = time(NULL);
const AbstractPlaylist *playlist = getPlaylist();
AbstractPlaylist *playlist = getPlaylist();
if(!b_loaded || (isLive() && nextUpdateTime < now))
{
M3U8Parser parser;
/* ugly hack */