Commit 9565350a authored by François Cartegnie's avatar François Cartegnie 🤞

demux: hls: add adaptative based hls demuxer

parent 621b86f9
......@@ -57,6 +57,7 @@ Demuxers:
* Support for Creative ADPCM/alaw/ulaw/S16L in VOC files
* Support for Creative ADPCM in AVI
* Directory Demux can now sort items, ignore extensions and hidden files
* Replaced httplive stream filter with new hls demuxer
Stream filter:
* Added ARIB STD-B25 TS streams decoder
......
......@@ -175,6 +175,7 @@ $Id$
* hevc: HEVC demuxer
* hotkeys: hotkeys control module
* hqdn3d: High Quality denoising filter
* hls: HTTP Live Streaming demuxer
* http: HTTP Network access module
* httplive: HTTP Live streaming for playback
* i420_rgb: planar YUV to packed RGB conversion functions
......
......@@ -244,45 +244,7 @@ if HAVE_DVBPSI
demux_LTLIBRARIES += libts_plugin.la
endif
libdash_plugin_la_SOURCES = \
demux/dash/mpd/AdaptationSet.cpp \
demux/dash/mpd/AdaptationSet.h \
demux/dash/mpd/DASHCommonAttributesElements.cpp \
demux/dash/mpd/DASHCommonAttributesElements.h \
demux/dash/mpd/DASHSegment.cpp \
demux/dash/mpd/DASHSegment.h \
demux/dash/mpd/ContentDescription.cpp \
demux/dash/mpd/ContentDescription.h \
demux/dash/mpd/IsoffMainParser.cpp \
demux/dash/mpd/IsoffMainParser.h \
demux/dash/mpd/MPD.cpp \
demux/dash/mpd/MPD.h \
demux/dash/mpd/MPDFactory.cpp \
demux/dash/mpd/MPDFactory.h \
demux/dash/mpd/Period.cpp \
demux/dash/mpd/Period.h \
demux/dash/mpd/Profile.cpp \
demux/dash/mpd/Profile.hpp \
demux/dash/mpd/ProgramInformation.cpp \
demux/dash/mpd/ProgramInformation.h \
demux/dash/mpd/Representation.cpp \
demux/dash/mpd/Representation.h \
demux/dash/mpd/TrickModeType.cpp \
demux/dash/mpd/TrickModeType.h \
demux/dash/mp4/AtomsReader.cpp \
demux/dash/mp4/AtomsReader.hpp \
demux/dash/xml/DOMHelper.cpp \
demux/dash/xml/DOMHelper.h \
demux/dash/xml/DOMParser.cpp \
demux/dash/xml/DOMParser.h \
demux/dash/xml/Node.cpp \
demux/dash/xml/Node.h \
demux/dash/dash.cpp \
demux/dash/dash.hpp \
demux/dash/DASHManager.cpp \
demux/dash/DASHManager.h
libdash_plugin_la_SOURCES += \
adaptative_SOURCES = \
demux/adaptative/playlist/AbstractPlaylist.cpp \
demux/adaptative/playlist/AbstractPlaylist.hpp \
demux/adaptative/playlist/BaseAdaptationSet.cpp \
......@@ -345,8 +307,46 @@ libdash_plugin_la_SOURCES += \
demux/adaptative/tools/Retrieve.cpp \
demux/adaptative/tools/Retrieve.hpp
libdash_plugin_la_SOURCES += demux/mp4/libmp4.c demux/mp4/libmp4.h
libdash_plugin_la_SOURCES = \
demux/dash/mpd/AdaptationSet.cpp \
demux/dash/mpd/AdaptationSet.h \
demux/dash/mpd/DASHCommonAttributesElements.cpp \
demux/dash/mpd/DASHCommonAttributesElements.h \
demux/dash/mpd/DASHSegment.cpp \
demux/dash/mpd/DASHSegment.h \
demux/dash/mpd/ContentDescription.cpp \
demux/dash/mpd/ContentDescription.h \
demux/dash/mpd/IsoffMainParser.cpp \
demux/dash/mpd/IsoffMainParser.h \
demux/dash/mpd/MPD.cpp \
demux/dash/mpd/MPD.h \
demux/dash/mpd/MPDFactory.cpp \
demux/dash/mpd/MPDFactory.h \
demux/dash/mpd/Period.cpp \
demux/dash/mpd/Period.h \
demux/dash/mpd/Profile.cpp \
demux/dash/mpd/Profile.hpp \
demux/dash/mpd/ProgramInformation.cpp \
demux/dash/mpd/ProgramInformation.h \
demux/dash/mpd/Representation.cpp \
demux/dash/mpd/Representation.h \
demux/dash/mpd/TrickModeType.cpp \
demux/dash/mpd/TrickModeType.h \
demux/dash/mp4/AtomsReader.cpp \
demux/dash/mp4/AtomsReader.hpp \
demux/dash/xml/DOMHelper.cpp \
demux/dash/xml/DOMHelper.h \
demux/dash/xml/DOMParser.cpp \
demux/dash/xml/DOMParser.h \
demux/dash/xml/Node.cpp \
demux/dash/xml/Node.h \
demux/dash/dash.cpp \
demux/dash/dash.hpp \
demux/dash/DASHManager.cpp \
demux/dash/DASHManager.h
libdash_plugin_la_SOURCES += $(adaptative_SOURCES)
libdash_plugin_la_SOURCES += demux/mp4/libmp4.c demux/mp4/libmp4.h
libdash_plugin_la_CXXFLAGS = $(AM_CFLAGS) -I$(srcdir)/demux/dash
libdash_plugin_la_LIBADD = $(SOCKET_LIBS) $(LIBM)
if HAVE_ZLIB
......@@ -354,6 +354,36 @@ libdash_plugin_la_LIBADD += -lz
endif
demux_LTLIBRARIES += libdash_plugin.la
libhls_plugin_la_SOURCES = \
demux/hls/playlist/M3U8.hpp \
demux/hls/playlist/M3U8.cpp \
demux/hls/playlist/Parser.hpp \
demux/hls/playlist/Parser.cpp \
demux/hls/playlist/Representation.hpp \
demux/hls/playlist/Representation.cpp \
demux/hls/playlist/HLSSegment.hpp \
demux/hls/playlist/HLSSegment.cpp \
demux/hls/playlist/Tags.hpp \
demux/hls/playlist/Tags.cpp \
demux/hls/HLSManager.hpp \
demux/hls/HLSManager.cpp \
demux/hls/hls.cpp \
demux/hls/hls.hpp
libhls_plugin_la_SOURCES += $(adaptative_SOURCES)
libhls_plugin_la_CXXFLAGS = $(AM_CFLAGS) -I$(srcdir)/demux/hls
libhls_plugin_la_LIBADD = $(SOCKET_LIBS) $(LIBM)
if HAVE_ZLIB
libhls_plugin_la_LIBADD += -lz
endif
if HAVE_GCRYPT
libhls_plugin_la_CXXFLAGS += $(GCRYPT_CFLAGS)
libhls_plugin_la_LIBADD += $(GCRYPT_LIBS)
endif
demux_LTLIBRARIES += libhls_plugin.la
libttml_plugin_la_SOURCES = demux/ttml.c
demux_LTLIBRARIES += libttml_plugin.la
/*****************************************************************************
* HLSManager.cpp
*****************************************************************************
* Copyright © 2015 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.
*****************************************************************************/
#include "HLSManager.hpp"
#include "../adaptative/logic/RateBasedAdaptationLogic.h"
#include "../adaptative/tools/Retrieve.hpp"
#include "playlist/Parser.hpp"
#include <vlc_stream.h>
using namespace adaptative;
using namespace adaptative::logic;
using namespace hls;
using namespace hls::playlist;
HLSManager::HLSManager(M3U8 *playlist,
AbstractAdaptationLogic::LogicType type, stream_t *stream) :
PlaylistManager(playlist, type, stream)
{
}
HLSManager::~HLSManager()
{
}
AbstractAdaptationLogic *HLSManager::createLogic(AbstractAdaptationLogic::LogicType type)
{
size_t bps = var_InheritInteger(stream, "hls-prefbw") * 8192;
switch(type)
{
case AbstractAdaptationLogic::FixedRate:
{
size_t bps = var_InheritInteger(stream, "hls-prefbw") * 8192;
return new (std::nothrow) FixedRateAdaptationLogic(bps);
}
case AbstractAdaptationLogic::Default:
case AbstractAdaptationLogic::RateBased:
{
int width = var_InheritInteger(stream, "hls-prefwidth");
int height = var_InheritInteger(stream, "hls-prefheight");
return new (std::nothrow) RateBasedAdaptationLogic(width, height);
}
default:
return PlaylistManager::createLogic(type);
}
}
bool HLSManager::updatePlaylist()
{
if(!playlist->isLive() || !playlist->minUpdatePeriod.Get())
return true;
mtime_t now = time(NULL);
if(nextPlaylistupdate && now < nextPlaylistupdate)
return true;
M3U8 *updatedplaylist = NULL;
/* do update */
if(nextPlaylistupdate)
{
std::string url(stream->psz_access);
url.append("://");
url.append(stream->psz_path);
uint8_t *p_data = NULL;
size_t i_data = Retrieve::HTTP(VLC_OBJECT(stream), url, (void**) &p_data);
if(!p_data)
return false;
stream_t *updatestream = stream_MemoryNew(stream, p_data, i_data, false);
if(!updatestream)
{
free(p_data);
nextPlaylistupdate = now + playlist->minUpdatePeriod.Get();
return false;
}
Parser parser(updatestream);
updatedplaylist = parser.parse(url);
if(!updatedplaylist)
{
stream_Delete(updatestream);
nextPlaylistupdate = now + playlist->minUpdatePeriod.Get();
return false;
}
stream_Delete(updatestream);
}
/* Compute new MPD update time */
mtime_t mininterval = 0;
mtime_t maxinterval = 0;
if(updatedplaylist)
{
updatedplaylist->getPlaylistDurationsRange(&mininterval, &maxinterval);
playlist->mergeWith(updatedplaylist);
playlist->debug();
delete updatedplaylist;
/* pruning */
for(int type=0; type<StreamTypeCount; type++)
{
if(!streams[type])
continue;
streams[type]->prune();
}
}
else
{
playlist->getPlaylistDurationsRange(&mininterval, &maxinterval);
}
if(playlist->minUpdatePeriod.Get() * CLOCK_FREQ > mininterval)
mininterval = playlist->minUpdatePeriod.Get() * CLOCK_FREQ;
if(maxinterval < mininterval)
maxinterval = mininterval;
nextPlaylistupdate = now + (mininterval + maxinterval) / (2 * CLOCK_FREQ);
msg_Dbg(stream, "Updated playlist, next update in %" PRId64 "s %ld %ld", nextPlaylistupdate - now, mininterval, maxinterval );
return true;
}
/*
* HLSManager.hpp
*****************************************************************************
* Copyright © 2015 - 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 HLSMANAGER_HPP
#define HLSMANAGER_HPP
#include "../adaptative/PlaylistManager.h"
#include "../adaptative/logic/AbstractAdaptationLogic.h"
#include "playlist/M3U8.hpp"
namespace hls
{
using namespace adaptative;
class HLSManager : public PlaylistManager
{
public:
HLSManager( playlist::M3U8 *,
logic::AbstractAdaptationLogic::LogicType type,
stream_t *stream );
virtual ~HLSManager();
virtual AbstractAdaptationLogic *createLogic(AbstractAdaptationLogic::LogicType);
virtual bool updatePlaylist();
};
}
#endif // HLSMANAGER_HPP
/*****************************************************************************
* hls.cpp: HTTP Live Streaming module
*****************************************************************************
* Copyright © 2015 - 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.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#define __STDC_CONSTANT_MACROS 1
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdint.h>
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_demux.h>
#include <vlc_meta.h>
#include <errno.h>
#include "../adaptative/logic/AbstractAdaptationLogic.h"
#include "HLSManager.hpp"
#include "playlist/Parser.hpp"
#include "playlist/M3U8.hpp"
#include "hls.hpp"
using namespace adaptative;
using namespace adaptative::logic;
using namespace adaptative::playlist;
using namespace hls;
using namespace hls::playlist;
/*****************************************************************************
* Module descriptor
*****************************************************************************/
static int Open (vlc_object_t *);
static void Close (vlc_object_t *);
#define HLS_WIDTH_TEXT N_("Preferred Width")
#define HLS_WIDTH_LONGTEXT N_("Preferred Width")
#define HLS_HEIGHT_TEXT N_("Preferred Height")
#define HLS_HEIGHT_LONGTEXT N_("Preferred Height")
#define HLS_BW_TEXT N_("Fixed Bandwidth in KiB/s")
#define HLS_BW_LONGTEXT N_("Preferred bandwidth for non adaptative streams")
#define HLS_LOGIC_TEXT N_("Adaptation Logic")
static const int pi_logics[] = {AbstractAdaptationLogic::RateBased,
AbstractAdaptationLogic::FixedRate,
AbstractAdaptationLogic::AlwaysLowest,
AbstractAdaptationLogic::AlwaysBest};
static const char *const ppsz_logics[] = { N_("Bandwidth Adaptive"),
N_("Fixed Bandwidth"),
N_("Lowest Bandwidth/Quality"),
N_("Highest Bandwith/Quality")};
vlc_module_begin ()
set_shortname( N_("hls"))
set_description( N_("HTTP Live Streaming") )
set_capability( "demux", 12 )
set_category( CAT_INPUT )
set_subcategory( SUBCAT_INPUT_DEMUX )
add_integer( "hls-logic", AbstractAdaptationLogic::Default,
HLS_LOGIC_TEXT, NULL, false )
change_integer_list( pi_logics, ppsz_logics )
add_integer( "hls-prefwidth", 480, HLS_WIDTH_TEXT, HLS_WIDTH_LONGTEXT, true )
add_integer( "hls-prefheight", 360, HLS_HEIGHT_TEXT, HLS_HEIGHT_LONGTEXT, true )
add_integer( "hls-prefbw", 250, HLS_BW_TEXT, HLS_BW_LONGTEXT, false )
set_callbacks( Open, Close )
vlc_module_end ()
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int Demux( demux_t * );
static int Control (demux_t *p_demux, int i_query, va_list args);
/*****************************************************************************
* Open:
*****************************************************************************/
static bool isHTTPLiveStreaming(stream_t *s)
{
const uint8_t *peek;
int size = stream_Peek(s, &peek, 46);
if (size < 7)
return false;
if (memcmp(peek, "#EXTM3U", 7) != 0)
return false;
peek += 7;
size -= 7;
/* Parse stream and search for
* EXT-X-TARGETDURATION or EXT-X-STREAM-INF tag, see
* http://tools.ietf.org/html/draft-pantos-http-live-streaming-04#page-8 */
while (size--)
{
static const char *const ext[] = {
"TARGETDURATION",
"MEDIA-SEQUENCE",
"KEY",
"ALLOW-CACHE",
"ENDLIST",
"STREAM-INF",
"DISCONTINUITY",
"VERSION"
};
if (*peek++ != '#')
continue;
if (size < 6)
continue;
if (memcmp(peek, "EXT-X-", 6))
continue;
peek += 6;
size -= 6;
for (size_t i = 0; i < ARRAY_SIZE(ext); i++)
{
size_t len = strlen(ext[i]);
if (size < 0 || (size_t)size < len)
continue;
if (!memcmp(peek, ext[i], len))
return true;
}
}
return false;
}
static int Open(vlc_object_t *p_obj)
{
demux_t *p_demux = (demux_t*) p_obj;
if(!isHTTPLiveStreaming(p_demux->s))
return VLC_EGENERIC;
demux_sys_t *p_sys = (demux_sys_t *) malloc(sizeof(demux_sys_t));
if (unlikely(p_sys == NULL))
return VLC_ENOMEM;
Parser parser(p_demux->s);
p_sys->p_playlist = parser.parse(std::string());
if(!p_sys->p_playlist)
{
free(p_sys);
return VLC_EGENERIC;
}
int logic = var_InheritInteger(p_obj, "hls-logic");
HLSManager *p_manager =
new (std::nothrow) HLSManager(p_sys->p_playlist,
static_cast<AbstractAdaptationLogic::LogicType>(logic),
p_demux->s);
BasePeriod *period = p_sys->p_playlist->getFirstPeriod();
if(period && !p_manager->start(p_demux))
{
delete p_manager;
free( p_sys );
return VLC_EGENERIC;
}
p_sys->p_manager = p_manager;
p_demux->p_sys = p_sys;
p_demux->pf_demux = Demux;
p_demux->pf_control = Control;
p_sys->i_nzpcr = VLC_TS_INVALID;
msg_Dbg(p_obj,"opening mpd file (%s)", p_demux->s->psz_path);
return VLC_SUCCESS;
}
/*****************************************************************************
* Close:
*****************************************************************************/
static void Close(vlc_object_t *p_obj)
{
demux_t *p_demux = (demux_t*) p_obj;
demux_sys_t *p_sys = (demux_sys_t *) p_demux->p_sys;
delete p_sys->p_manager;
free(p_sys);
}
/*****************************************************************************
* Callbacks:
*****************************************************************************/
#define DEMUX_INCREMENT (CLOCK_FREQ / 20)
static int Demux(demux_t *p_demux)
{
demux_sys_t *p_sys = p_demux->p_sys;
Stream::status status =
p_sys->p_manager->demux(p_sys->i_nzpcr + DEMUX_INCREMENT);
switch(status)
{
case Stream::status_eof:
return VLC_DEMUXER_EOF;
case Stream::status_buffering:
break;
case Stream::status_demuxed:
if(p_sys->i_nzpcr == VLC_TS_INVALID)
p_sys->i_nzpcr = p_sys->p_manager->getPCR();
else
p_sys->i_nzpcr += DEMUX_INCREMENT;
int group = p_sys->p_manager->getGroup();
es_out_Control(p_demux->out, ES_OUT_SET_GROUP_PCR, group, VLC_TS_0 + p_sys->i_nzpcr);
break;
}
if( !p_sys->p_manager->updatePlaylist() )
msg_Warn(p_demux, "Can't update playlist");
return VLC_DEMUXER_SUCCESS;
}
static int Control (demux_t *p_demux, int i_query, va_list args)
{
demux_sys_t *p_sys = p_demux->p_sys;
switch (i_query)
{
case DEMUX_CAN_SEEK:
*(va_arg (args, bool *)) = p_sys->p_manager->seekAble();
break;
case DEMUX_CAN_CONTROL_PACE:
*(va_arg (args, bool *)) = true;
break;
case DEMUX_CAN_PAUSE:
*(va_arg (args, bool *)) = p_sys->p_playlist->isLive();
break;
case DEMUX_GET_TIME:
*(va_arg (args, int64_t *)) = p_sys->i_nzpcr;
break;
case DEMUX_GET_LENGTH:
*(va_arg (args, int64_t *)) = p_sys->p_manager->getDuration();
break;
case DEMUX_GET_POSITION:
if(!p_sys->p_manager->getDuration())
return VLC_EGENERIC;
*(va_arg (args, double *)) = (double) p_sys->i_nzpcr
/ p_sys->p_manager->getDuration();
break;
case DEMUX_SET_POSITION:
{
int64_t time = p_sys->p_manager->getDuration() * va_arg(args, double);
if(p_sys->p_playlist->isLive() ||
!p_sys->p_manager->getDuration() ||
!p_sys->p_manager->setPosition(time))
return VLC_EGENERIC;
p_sys->i_nzpcr = VLC_TS_INVALID;
break;
}
case DEMUX_SET_TIME:
{
int64_t time = va_arg(args, int64_t);
if(p_sys->p_playlist->isLive() ||
!p_sys->p_manager->setPosition(time))
return VLC_EGENERIC;
p_sys->i_nzpcr = VLC_TS_INVALID;
break;
}
case DEMUX_GET_PTS_DELAY:
*va_arg (args, int64_t *) = INT64_C(1000) *
var_InheritInteger(p_demux, "network-caching");
break;
default:
return VLC_EGENERIC;
}
return VLC_SUCCESS;
}
/*****************************************************************************
* hls.hpp: HTTP Live Streaming module
*****************************************************************************
* Copyright © 2015 - 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 HLS_HPP
#define HLS_HPP
struct demux_sys_t
{
adaptative::PlaylistManager *p_manager;
hls::playlist::M3U8 *p_playlist;
mtime_t i_nzpcr;
};
#endif // HLS_HPP
/*
* HLSSegment.cpp
*****************************************************************************
* Copyright (C) 2015 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 "HLSSegment.hpp"
#include <vlc_common.h>
#include <vlc_block.h>
#ifdef HAVE_GCRYPT
#include <vlc_gcrypt.h>
#endif
using namespace hls::playlist;
SegmentEncryption::SegmentEncryption()
{
method = SegmentEncryption::NONE;
}
HLSSegment::HLSSegment( ICanonicalUrl *parent, uint64_t seq ) :
Segment( parent )
{
sequence = seq;
#ifdef HAVE_GCRYPT
ctx = NULL;
#endif