You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

376 lines
14 KiB

/*****************************************************************************
* adaptive.cpp: Adaptive streaming module
*****************************************************************************
* Copyright © 2015 - VideoLAN and VLC 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.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdint.h>
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_demux.h>
#include "SharedResources.hpp"
#include "logic/BufferingLogic.hpp"
#include "xml/DOMParser.h"
#include "../dash/DASHManager.h"
#include "../dash/DASHStream.hpp"
#include "../dash/mpd/IsoffMainParser.h"
#include "../hls/HLSManager.hpp"
#include "../hls/HLSStreams.hpp"
#include "../hls/playlist/Parser.hpp"
#include "../hls/playlist/M3U8.hpp"
#include "../smooth/SmoothManager.hpp"
#include "../smooth/SmoothStream.hpp"
#include "../smooth/playlist/SmoothParser.hpp"
using namespace adaptive::http;
using namespace adaptive::logic;
using namespace adaptive::playlist;
using namespace adaptive::xml;
using namespace dash::mpd;
using namespace dash;
using namespace hls;
using namespace hls::playlist;
using namespace smooth;
using namespace smooth::playlist;
/*****************************************************************************
* Module descriptor
*****************************************************************************/
static int Open (vlc_object_t *);
static void Close (vlc_object_t *);
#define ADAPT_WIDTH_TEXT N_("Maximum device width")
#define ADAPT_HEIGHT_TEXT N_("Maximum device height")
#define ADAPT_BW_TEXT N_("Fixed Bandwidth in KiB/s")
#define ADAPT_BW_LONGTEXT N_("Preferred bandwidth for non adaptive streams")
#define ADAPT_BUFFER_TEXT N_("Live Playback delay (ms)")
#define ADAPT_BUFFER_LONGTEXT N_("Tradeoff between stability and real time")
#define ADAPT_MAXBUFFER_TEXT N_("Max buffering (ms)")
#define ADAPT_LOGIC_TEXT N_("Adaptive Logic")
#define ADAPT_ACCESS_TEXT N_("Use regular HTTP modules")
#define ADAPT_ACCESS_LONGTEXT N_("Connect using HTTP access instead of custom HTTP code")
#define ADAPT_LOWLATENCY_TEXT N_("Low latency")
#define ADAPT_LOWLATENCY_LONGTEXT N_("Overrides low latency parameters")
static const AbstractAdaptationLogic::LogicType pi_logics[] = {
AbstractAdaptationLogic::LogicType::Default,
AbstractAdaptationLogic::LogicType::Predictive,
AbstractAdaptationLogic::LogicType::NearOptimal,
AbstractAdaptationLogic::LogicType::RateBased,
AbstractAdaptationLogic::LogicType::FixedRate,
AbstractAdaptationLogic::LogicType::AlwaysLowest,
AbstractAdaptationLogic::LogicType::AlwaysBest};
static const char *const ppsz_logics_values[] = {
"",
"predictive",
"nearoptimal",
"rate",
"fixedrate",
"lowest",
"highest"};
static const char *const ppsz_logics[] = { N_("Default"),
N_("Predictive"),
N_("Near Optimal"),
N_("Bandwidth Adaptive"),
N_("Fixed Bandwidth"),
N_("Lowest Bandwidth/Quality"),
N_("Highest Bandwidth/Quality")};
static_assert( ARRAY_SIZE( pi_logics ) == ARRAY_SIZE( ppsz_logics ),
"pi_logics and ppsz_logics shall have the same number of elements" );
static_assert( ARRAY_SIZE( pi_logics ) == ARRAY_SIZE( ppsz_logics_values ),
"pi_logics and ppsz_logics_values shall have the same number of elements" );
static const int rgi_latency[] = { -1, 1, 0 };
static const char *const ppsz_latency[] = { N_("Auto"),
N_("Force"),
N_("Disable") };
vlc_module_begin ()
set_shortname( N_("Adaptive"))
set_description( N_("Unified adaptive streaming for DASH/HLS") )
set_capability( "demux", 12 )
set_subcategory( SUBCAT_INPUT_DEMUX )
add_string( "adaptive-logic", "", ADAPT_LOGIC_TEXT, nullptr )
change_string_list( ppsz_logics_values, ppsz_logics )
add_integer( "adaptive-maxwidth", 0,
ADAPT_WIDTH_TEXT, nullptr )
add_integer( "adaptive-maxheight", 0,
ADAPT_HEIGHT_TEXT, nullptr )
add_integer( "adaptive-bw", 250, ADAPT_BW_TEXT, ADAPT_BW_LONGTEXT )
add_bool ( "adaptive-use-access", false, ADAPT_ACCESS_TEXT, ADAPT_ACCESS_LONGTEXT )
add_integer( "adaptive-livedelay",
MS_FROM_VLC_TICK(AbstractBufferingLogic::DEFAULT_LIVE_BUFFERING),
ADAPT_BUFFER_TEXT, ADAPT_BUFFER_LONGTEXT )
add_integer( "adaptive-maxbuffer",
MS_FROM_VLC_TICK(AbstractBufferingLogic::DEFAULT_MAX_BUFFERING),
ADAPT_MAXBUFFER_TEXT, nullptr )
add_integer( "adaptive-lowlatency", -1, ADAPT_LOWLATENCY_TEXT, ADAPT_LOWLATENCY_LONGTEXT )
change_integer_list(rgi_latency, ppsz_latency)
set_callbacks( Open, Close )
vlc_module_end ()
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static PlaylistManager * HandleDash(demux_t *, DOMParser &,
const std::string &, AbstractAdaptationLogic::LogicType);
static PlaylistManager * HandleSmooth(demux_t *, DOMParser &,
const std::string &, AbstractAdaptationLogic::LogicType);
static PlaylistManager * HandleHLS(demux_t *,
const std::string &, AbstractAdaptationLogic::LogicType);
/*****************************************************************************
* Open:
*****************************************************************************/
static int Open(vlc_object_t *p_obj)
{
demux_t *p_demux = (demux_t*) p_obj;
if(!p_demux->s->psz_url)
return VLC_EGENERIC;
std::string mimeType;
char *psz_mime = stream_ContentType(p_demux->s);
if(psz_mime)
{
mimeType = std::string(psz_mime);
free(psz_mime);
}
PlaylistManager *p_manager = nullptr;
char *psz_logic = var_InheritString(p_obj, "adaptive-logic");
AbstractAdaptationLogic::LogicType logic = AbstractAdaptationLogic::LogicType::Default;
if( psz_logic )
{
bool b_found = false;
for(size_t i=0;i<ARRAY_SIZE(pi_logics); i++)
{
if(!strcmp(psz_logic, ppsz_logics_values[i]))
{
logic = pi_logics[i];
b_found = true;
break;
}
}
if(!b_found)
msg_Err(p_demux, "Unknown adaptive-logic value '%s'", psz_logic);
free( psz_logic );
}
std::string playlisturl(p_demux->s->psz_url);
bool dashmime = DASHManager::mimeMatched(mimeType);
bool smoothmime = SmoothManager::mimeMatched(mimeType);
if(!dashmime && !smoothmime && HLSManager::isHTTPLiveStreaming(p_demux->s))
{
p_manager = HandleHLS(p_demux, playlisturl, logic);
}
else
{
/* Handle XML Based ones */
DOMParser xmlParser; /* Share that xml reader */
if(dashmime)
{
p_manager = HandleDash(p_demux, xmlParser, playlisturl, logic);
}
else if(smoothmime)
{
p_manager = HandleSmooth(p_demux, xmlParser, playlisturl, logic);
}
else
{
/* We need to probe content */
const uint8_t *p_peek;
const ssize_t i_peek = vlc_stream_Peek(p_demux->s, &p_peek, 2048);
if(i_peek > 0)
{
stream_t *peekstream = vlc_stream_MemoryNew(p_demux, const_cast<uint8_t *>(p_peek), (size_t)i_peek, true);
if(peekstream)
{
if(xmlParser.reset(peekstream) && xmlParser.parse(false))
{
if(DASHManager::isDASH(xmlParser.getRootNode()))
{
p_manager = HandleDash(p_demux, xmlParser, playlisturl, logic);
}
else if(SmoothManager::isSmoothStreaming(xmlParser.getRootNode()))
{
p_manager = HandleSmooth(p_demux, xmlParser, playlisturl, logic);
}
}
vlc_stream_Delete(peekstream);
}
}
}
}
if(!p_manager || !p_manager->init(p_demux->b_preparsing))
{
delete p_manager;
return VLC_EGENERIC;
}
/* disable annoying stuff */
if(VLC_SUCCESS == var_Create( p_demux, "lua", VLC_VAR_BOOL))
var_SetBool(p_demux, "lua", false);
p_demux->p_sys = p_manager;
p_demux->pf_demux = p_manager->demux_callback;
p_demux->pf_control = p_manager->control_callback;
msg_Dbg(p_obj,"opening playlist file (%s)", p_demux->psz_location);
return VLC_SUCCESS;
}
/*****************************************************************************
* Close:
*****************************************************************************/
static void Close(vlc_object_t *p_obj)
{
demux_t *p_demux = (demux_t*) p_obj;
PlaylistManager *p_manager = reinterpret_cast<PlaylistManager *>(p_demux->p_sys);
p_manager->stop();
delete p_manager;
}
/*****************************************************************************
*
*****************************************************************************/
static PlaylistManager * HandleDash(demux_t *p_demux, DOMParser &xmlParser,
const std::string & playlisturl,
AbstractAdaptationLogic::LogicType logic)
{
if(!xmlParser.reset(p_demux->s) || !xmlParser.parse(true))
{
msg_Err(p_demux, "Cannot parse MPD");
return nullptr;
}
IsoffMainParser mpdparser(xmlParser.getRootNode(), VLC_OBJECT(p_demux),
p_demux->s, playlisturl);
MPD *p_playlist = mpdparser.parse();
if(p_playlist == nullptr)
{
msg_Err( p_demux, "Cannot create/unknown MPD for profile");
return nullptr;
}
SharedResources *resources =
SharedResources::createDefault(VLC_OBJECT(p_demux), playlisturl);
DASHStreamFactory *factory = new (std::nothrow) DASHStreamFactory;
DASHManager *manager = nullptr;
if(!resources || !factory ||
!(manager = new (std::nothrow) DASHManager(p_demux, resources,
p_playlist, factory, logic)))
{
delete resources;
delete factory;
delete p_playlist;
}
return manager;
}
static PlaylistManager * HandleSmooth(demux_t *p_demux, DOMParser &xmlParser,
const std::string & playlisturl,
AbstractAdaptationLogic::LogicType logic)
{
if(!xmlParser.reset(p_demux->s) || !xmlParser.parse(true))
{
msg_Err(p_demux, "Cannot parse Manifest");
return nullptr;
}
ManifestParser mparser(xmlParser.getRootNode(), VLC_OBJECT(p_demux),
p_demux->s, playlisturl);
Manifest *p_playlist = mparser.parse();
if(p_playlist == nullptr)
{
msg_Err( p_demux, "Cannot create Manifest");
return nullptr;
}
SharedResources *resources =
SharedResources::createDefault(VLC_OBJECT(p_demux), playlisturl);
SmoothStreamFactory *factory = new (std::nothrow) SmoothStreamFactory;
SmoothManager *manager = nullptr;
if(!resources || !factory ||
!(manager = new (std::nothrow) SmoothManager(p_demux, resources,
p_playlist, factory, logic)))
{
delete resources;
delete factory;
delete p_playlist;
}
return manager;
}
static PlaylistManager * HandleHLS(demux_t *p_demux,
const std::string & playlisturl,
AbstractAdaptationLogic::LogicType logic)
{
SharedResources *resources =
SharedResources::createDefault(VLC_OBJECT(p_demux), playlisturl);
if(!resources)
return nullptr;
M3U8Parser parser(resources);
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 resources;
return nullptr;
}
HLSStreamFactory *factory = new (std::nothrow) HLSStreamFactory;
HLSManager *manager = nullptr;
if(!factory ||
!(manager = new (std::nothrow) HLSManager(p_demux, resources,
p_playlist, factory, logic)))
{
delete p_playlist;
delete factory;
delete resources;
}
return manager;
}