Commit 9565350a authored by Francois Cartegnie's avatar Francois Cartegnie

demux: hls: add adaptative based hls demuxer

parent 621b86f9
...@@ -57,6 +57,7 @@ Demuxers: ...@@ -57,6 +57,7 @@ Demuxers:
* Support for Creative ADPCM/alaw/ulaw/S16L in VOC files * Support for Creative ADPCM/alaw/ulaw/S16L in VOC files
* Support for Creative ADPCM in AVI * Support for Creative ADPCM in AVI
* Directory Demux can now sort items, ignore extensions and hidden files * Directory Demux can now sort items, ignore extensions and hidden files
* Replaced httplive stream filter with new hls demuxer
Stream filter: Stream filter:
* Added ARIB STD-B25 TS streams decoder * Added ARIB STD-B25 TS streams decoder
......
...@@ -175,6 +175,7 @@ $Id$ ...@@ -175,6 +175,7 @@ $Id$
* hevc: HEVC demuxer * hevc: HEVC demuxer
* hotkeys: hotkeys control module * hotkeys: hotkeys control module
* hqdn3d: High Quality denoising filter * hqdn3d: High Quality denoising filter
* hls: HTTP Live Streaming demuxer
* http: HTTP Network access module * http: HTTP Network access module
* httplive: HTTP Live streaming for playback * httplive: HTTP Live streaming for playback
* i420_rgb: planar YUV to packed RGB conversion functions * i420_rgb: planar YUV to packed RGB conversion functions
......
...@@ -244,45 +244,7 @@ if HAVE_DVBPSI ...@@ -244,45 +244,7 @@ if HAVE_DVBPSI
demux_LTLIBRARIES += libts_plugin.la demux_LTLIBRARIES += libts_plugin.la
endif endif
libdash_plugin_la_SOURCES = \ adaptative_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 += \
demux/adaptative/playlist/AbstractPlaylist.cpp \ demux/adaptative/playlist/AbstractPlaylist.cpp \
demux/adaptative/playlist/AbstractPlaylist.hpp \ demux/adaptative/playlist/AbstractPlaylist.hpp \
demux/adaptative/playlist/BaseAdaptationSet.cpp \ demux/adaptative/playlist/BaseAdaptationSet.cpp \
...@@ -345,8 +307,46 @@ libdash_plugin_la_SOURCES += \ ...@@ -345,8 +307,46 @@ libdash_plugin_la_SOURCES += \
demux/adaptative/tools/Retrieve.cpp \ demux/adaptative/tools/Retrieve.cpp \
demux/adaptative/tools/Retrieve.hpp 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_CXXFLAGS = $(AM_CFLAGS) -I$(srcdir)/demux/dash
libdash_plugin_la_LIBADD = $(SOCKET_LIBS) $(LIBM) libdash_plugin_la_LIBADD = $(SOCKET_LIBS) $(LIBM)
if HAVE_ZLIB if HAVE_ZLIB
...@@ -354,6 +354,36 @@ libdash_plugin_la_LIBADD += -lz ...@@ -354,6 +354,36 @@ libdash_plugin_la_LIBADD += -lz
endif endif
demux_LTLIBRARIES += libdash_plugin.la 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 libttml_plugin_la_SOURCES = demux/ttml.c
demux_LTLIBRARIES += libttml_plugin.la 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
}
HLSSegment::~HLSSegment()
{
#ifdef HAVE_GCRYPT
if(ctx)
gcry_cipher_close(ctx);
#endif
}
void HLSSegment::onChunkDownload(block_t **pp_block, Chunk *chunk, BaseRepresentation *)
{
block_t *p_block = *pp_block;
#ifdef HAVE_GCRYPT
if(encryption.method == SegmentEncryption::AES_128)
{
block_t *p_block = *pp_block;
/* first bytes */
if(!ctx && chunk->getBytesRead() == p_block->i_buffer)
{
vlc_gcrypt_init();
if (encryption.iv.size() != 16)
{
encryption.iv.clear();
encryption.iv.resize(16);
encryption.iv[15] = sequence & 0xff;
encryption.iv[14] = (sequence >> 8)& 0xff;
encryption.iv[13] = (sequence >> 16)& 0xff;
encryption.iv[12] = (sequence >> 24)& 0xff;
}
if( gcry_cipher_open(&ctx, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 0) ||
gcry_cipher_setkey(ctx, &encryption.key[0], 16) ||
gcry_cipher_setiv(ctx, &encryption.iv[0], 16) )
{
gcry_cipher_close(ctx);
ctx = NULL;
}
}
if(ctx)
{
if ((p_block->i_buffer % 16) != 0 || p_block->i_buffer < 16 ||
gcry_cipher_decrypt(ctx, p_block->p_buffer, p_block->i_buffer, NULL, 0))
{
p_block->i_buffer = 0;
gcry_cipher_close(ctx);
ctx = NULL;
}
else
{
/* last bytes */
if(chunk->getBytesToRead() == 0)
{
/* remove the PKCS#7 padding from the buffer */
const uint8_t pad = p_block->p_buffer[p_block->i_buffer - 1];
for(uint8_t i=0; i<pad && i<=16; i++)
{
if(p_block->p_buffer[p_block->i_buffer - i - 1] != pad)
break;
if(i==pad)
p_block->i_buffer -= pad;
}
gcry_cipher_close(ctx);
ctx = NULL;
}
}
}
}
else
#endif
if(encryption.method != SegmentEncryption::NONE)
{
p_block->i_buffer = 0;
}
}
void HLSSegment::setEncryption(SegmentEncryption &enc)
{
encryption = enc;
}
void HLSSegment::debug(vlc_object_t *obj, int indent) const
{
std::stringstream ss;
ss << std::string(indent, ' ') << debugName <<
" #" << sequence <<
" url=" << getUrlSegment().toString();
if(startByte!=endByte)
ss << " @" << startByte << ".." << endByte;
msg_Dbg(obj, "%s", ss.str().c_str());
}
int HLSSegment::compare(ISegment *segment) const
{
HLSSegment *hlssegment = dynamic_cast<HLSSegment *>(segment);
if(hlssegment)
{
if (sequence > hlssegment->sequence)
return 1;
else if(sequence < hlssegment->sequence)
return -1;
else
return 0;
}
else return ISegment::compare(segment);
}
/*
* HLSSegment.hpp
*****************************************************************************
* 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.
*****************************************************************************/
#ifndef HLSSEGMENT_HPP
#define HLSSEGMENT_HPP
#include "../adaptative/playlist/Segment.h"
#include <vector>
#include <gcrypt.h>
namespace hls
{
namespace playlist
{
using namespace adaptative::playlist;
class SegmentEncryption
{
public:
SegmentEncryption();
enum
{
NONE,
AES_128,
AES_SAMPLE,
} method;
std::vector<uint8_t> key;
std::vector<uint8_t> iv;
};
class HLSSegment : public Segment
{
public:
HLSSegment( ICanonicalUrl *parent, uint64_t sequence );
virtual ~HLSSegment();
void setEncryption(SegmentEncryption &);
void debug(vlc_object_t *, int) const; /* reimpl */
virtual int compare(ISegment *) const; /* reimpl */
protected:
virtual void onChunkDownload(block_t **, Chunk *, BaseRepresentation *); /* reimpl */
uint64_t sequence;
SegmentEncryption encryption;
#ifdef HAVE_GCRYPT
gcry_cipher_hd_t ctx;
#endif
};
}
}
#endif // HLSSEGMENT_HPP
/*
* M3U8.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.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "M3U8.hpp"
#include "Representation.hpp"
#include "../adaptative/playlist/BasePeriod.h"
#include "../adaptative/playlist/BaseAdaptationSet.h"
#include <vlc_common.h>
#include <vlc_stream.h>
using namespace hls::playlist;
M3U8::M3U8 (stream_t *stream_) :
AbstractPlaylist(stream_)
{
minUpdatePeriod.Set( 5 );
}
M3U8::~M3U8()
{
}
bool M3U8::isLive() const
{
std::vector<BasePeriod *>::const_iterator itp;
for(itp = periods.begin(); itp != periods.end(); ++itp)
{
const BasePeriod *period = *itp;
std::vector<BaseAdaptationSet *>::const_iterator ita;
for(ita = period->getAdaptationSets().begin(); ita != period->getAdaptationSets().end(); ++ita)
{
BaseAdaptationSet *adaptSet = *ita;
std::vector<BaseRepresentation *>::iterator itr;
for(itr = adaptSet->getRepresentations().begin(); itr != adaptSet->getRepresentations().end(); ++itr)
{
const Representation *rep = dynamic_cast<const Representation *>(*itr);
if(rep->isLive())
return true;
}
}
}
return false;
}
void M3U8::debug()
{
std::vector<BasePeriod *>::const_iterator i;
for(i = periods.begin(); i != periods.end(); ++i)
(*i)->debug(VLC_OBJECT(stream));
}
/*
* M3U8.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 M3U8_H_
#define M3U8_H_
#include "../adaptative/playlist/AbstractPlaylist.hpp"
namespace hls
{
namespace playlist
{
using namespace adaptative::playlist;
class M3U8 : public AbstractPlaylist
{
public:
M3U8(stream_t *);
virtual ~M3U8();
virtual bool isLive() const;
virtual void debug();
private:
std::string data;
};
}
}
#endif /* M3U8_H_ */
/*
* Parser.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.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "Parser.hpp"
#include "playlist/HLSSegment.hpp"
#include "playlist/Representation.hpp"
#include "../adaptative/playlist/BasePeriod.h"
#include "../adaptative/playlist/BaseAdaptationSet.h"
#include "../adaptative/playlist/SegmentList.h"
#include "../adaptative/tools/Retrieve.hpp"
#include "M3U8.hpp"
#include "Tags.hpp"
#include <vlc_strings.h>
#include <vlc_stream.h>
#include <cstdio>
#include <sstream>
using namespace adaptative;
using namespace adaptative::playlist;
using namespace hls::playlist;
Parser::Parser(stream_t *stream)
{
p_stream = stream;
}
Parser::~Parser ()
{
}
static std::list<Tag *> getTagsFromList(std::list<Tag *> &list, int tag)
{
std::list<Tag *> ret;
std::list<Tag *>::const_iterator it;
for(it = list.begin(); it != list.end(); ++it)
{
if( (*it)->getType() == tag )
ret.push_back(*it);
}
return ret;
}
static void releaseTagsList(std::list<Tag *> &list)
{
std::list<Tag *>::const_iterator it;
for(it = list.begin(); it != list.end(); ++it)
delete *it;
list.clear();
}
void Parser::parseAdaptationSet(BasePeriod *period, const AttributesTag *)
{
BaseAdaptationSet *adaptSet = new (std::nothrow) BaseAdaptationSet(period);
if(adaptSet)
{
period->addAdaptationSet(adaptSet);
}
}
void Parser::parseRepresentation(BaseAdaptationSet *adaptSet, const AttributesTag * streaminftag)
{
if(!streaminftag->getAttributeByName("URI"))
return;
Url url = Url(streaminftag->getAttributeByName("URI")->value);
if(!url.hasScheme())
url = url.prepend(adaptSet->getUrlSegment());
void *p_data;
const size_t i_data = Retrieve::HTTP((vlc_object_t*)p_stream, url.toString(), &p_data);
if(p_data)
{
stream_t *substream = stream_MemoryNew((vlc_object_t *)p_stream, (uint8_t *)p_data, i_data, false);
if(substream)
{
std::list<Tag *> tagslist = parseEntries(substream);
stream_Delete(substream);
parseRepresentation(adaptSet, streaminftag, tagslist);
releaseTagsList(tagslist);
}
}
}
void Parser::parseRepresentation(BaseAdaptationSet *adaptSet, const AttributesTag * streaminftag,
const std::list<Tag *> &tagslist)
{
const Attribute *uriAttr = streaminftag->getAttributeByName("URI");
const Attribute *bwAttr = streaminftag->getAttributeByName("BANDWIDTH");
Representation *rep = new (std::nothrow) Representation(adaptSet);
if(rep)
{
rep->setMimeType("video/mp2t");
if(uriAttr)
{
size_t pos = uriAttr->value.find_last_of('/');
if(pos != std::string::npos)
rep->baseUrl.Set(new Url(uriAttr->value.substr(0, pos+1)));
}
if(bwAttr)
rep->setBandwidth(bwAttr->decimal());
parseSegments(rep, tagslist);
adaptSet->addRepresentation(rep);
}
}
void Parser::parseSegments(Representation *rep, const std::list<Tag *> &tagslist)
{
SegmentList *segmentList = new (std::nothrow) SegmentList(rep);
rep->setSegmentList(segmentList);
rep->timescale.Set(100);
int64_t totalduration = 0;
int64_t nzStartTime = 0;
uint64_t sequenceNumber = 0;
std::size_t prevbyterangeoffset = 0;
const SingleValueTag *ctx_byterange = NULL;
SegmentEncryption encryption;
std::list<Tag *>::const_iterator it;
for(it = tagslist.begin(); it != tagslist.end(); ++it)
{
const Tag *tag = *it;
switch(tag->getType())
{
/* using static cast as attribute type permits avoiding class check */
case SingleValueTag::EXTXMEDIASEQUENCE:
{
sequenceNumber = (static_cast<const SingleValueTag*>(tag))->getValue().decimal();
}
break;
case URITag::EXTINF:
{
const URITag *uritag = static_cast<const URITag *>(tag);
HLSSegment *segment = new (std::nothrow) HLSSegment(rep, sequenceNumber++);
if(!segment)
break;
if(uritag->getAttributeByName("URI"))
segment->setSourceUrl(uritag->getAttributeByName("URI")->value);
if(uritag->getAttributeByName("DURATION"))
{
segment->duration.Set(uritag->getAttributeByName("DURATION")->floatingPoint() * rep->timescale.Get());
segment->startTime.Set(nzStartTime);
nzStartTime += segment->duration.Get();
totalduration += segment->duration.Get();
}
segmentList->addSegment(segment);
if(ctx_byterange)
{
std::pair<std::size_t,std::size_t> range = ctx_byterange->getValue().getByteRange();
if(range.first == 0)
range.first = prevbyterangeoffset;
prevbyterangeoffset = range.first + range.second;
segment->setByteRange(range.first, prevbyterangeoffset);
ctx_byterange = NULL;
}
if(encryption.method != SegmentEncryption::NONE)
segment->setEncryption(encryption);
}
break;
case SingleValueTag::EXTXPLAYLISTTYPE:
rep->b_live = (static_cast<const SingleValueTag *>(tag)->getValue().value != "VOD");
break;
case SingleValueTag::EXTXBYTERANGE:
ctx_byterange = static_cast<const SingleValueTag *>(tag);
break;
case AttributesTag::EXTXKEY:
{
const AttributesTag *keytag = static_cast<const AttributesTag *>(tag);
if( keytag->getAttributeByName("METHOD") &&
keytag->getAttributeByName("METHOD")->value == "AES-128" &&
keytag->getAttributeByName("URI") )
{
encryption.method = SegmentEncryption::AES_128;
encryption.key.clear();
uint8_t *p_data;
const uint64_t read = Retrieve::HTTP(VLC_OBJECT(p_stream),
keytag->getAttributeByName("URI")->quotedString(),
(void **) &p_data);
if(p_data)
{
if(read == 16)
{
encryption.key.resize(16);
memcpy(&encryption.key[0], p_data, 16);
}
free(p_data);
}
if(keytag->getAttributeByName("IV"))
{
encryption.iv.clear();
encryption.iv = keytag->getAttributeByName("IV")->hexSequence();
}
}
else
{
/* unsupported or invalid */
encryption.method = SegmentEncryption::NONE;
encryption.key.clear();
encryption.iv.clear();
}
}
break;
case Tag::EXTXENDLIST:
rep->b_live = false;
break;
}
}
if(rep->isLive())
{
rep->getPlaylist()->duration.Set(0);
}
else if(totalduration > rep->getPlaylist()->duration.Get())
{
rep->getPlaylist()->duration.Set(CLOCK_FREQ * totalduration / rep->timescale.Get());
}
}
M3U8 * Parser::parse(const std::string &playlisturl)
{
char *psz_line = stream_ReadLine(p_stream);
if(!psz_line || strcmp(psz_line, "#EXTM3U"))
{
free(psz_line);
return NULL;
}
M3U8 *playlist = new (std::nothrow) M3U8(p_stream);
if(!playlist)
return NULL;
if(!playlisturl.empty())
{
size_t pos = playlisturl.find_last_of('/');
if(pos != std::string::npos)
playlist->addBaseUrl(playlisturl.substr(0, pos + 1));
}
BasePeriod *period = new (std::nothrow) BasePeriod( playlist );
if(!period)
return playlist;
std::list<Tag *> tagslist = parseEntries(p_stream);
bool b_masterplaylist = !getTagsFromList(tagslist, AttributesTag::EXTXSTREAMINF).empty();
if(b_masterplaylist)
{
BaseAdaptationSet *adaptSet = new (std::nothrow) BaseAdaptationSet(period);
if(adaptSet)
{
std::list<Tag *> mediainfotags = getTagsFromList(tagslist, AttributesTag::EXTXSTREAMINF);
std::list<Tag *>::const_iterator it;
for(it = mediainfotags.begin(); it != mediainfotags.end(); ++it)
parseRepresentation(adaptSet, dynamic_cast<AttributesTag *>(*it));
period->addAdaptationSet(adaptSet);
}
}
else
{
BaseAdaptationSet *adaptSet = new (std::nothrow) BaseAdaptationSet(period);
if(adaptSet)
{
period->addAdaptationSet(adaptSet);
AttributesTag *tag = new AttributesTag(AttributesTag::EXTXSTREAMINF, "");
parseRepresentation(adaptSet, tag, tagslist);
delete tag;
}
}
playlist->addPeriod(period);
releaseTagsList(tagslist);
playlist->debug();
return playlist;
}
std::list<Tag *> Parser::parseEntries(stream_t *stream)
{
std::list<Tag *> entrieslist;
Tag *lastTag = NULL;
char *psz_line;
while((psz_line = stream_ReadLine(stream)))
{
if(*psz_line == '#')
{
if(!strncmp(psz_line, "#EXT", 4)) //tag
{
std::string key;
std::string attributes;
const char *split = strchr(psz_line, ':');
if(split)
{
key = std::string(psz_line + 1, split - psz_line - 1);
attributes = std::string(split + 1);
}
else
{
key = std::string(psz_line);
}
if(!key.empty())
{
Tag *tag = TagFactory::createTagByName(key, attributes);
if(tag)
entrieslist.push_back(tag);
lastTag = tag;
}
}
}
else if(*psz_line && lastTag)
{
AttributesTag *attrTag = dynamic_cast<AttributesTag *>(lastTag);
if(attrTag)
{
Attribute *uriAttr = new (std::nothrow) Attribute("URI", std::string(psz_line));
if(uriAttr)
attrTag->addAttribute(uriAttr);
}
lastTag = NULL;
}
else // drop
{
lastTag = NULL;
}
free(psz_line);
}
return entrieslist;
}
/*
* Parser.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 PARSER_HPP
#define PARSER_HPP
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "../adaptative/playlist/SegmentInfoCommon.h"
#include <cstdlib>
#include <sstream>
#include <vlc_common.h>
namespace adaptative
{
namespace playlist
{
class SegmentInformation;
class MediaSegmentTemplate;
class BasePeriod;
class BaseAdaptationSet;
}
}
namespace hls
{
namespace playlist
{
using namespace adaptative::playlist;
class M3U8;
class AttributesTag;
class Tag;
class Representation;
class Parser
{
public:
Parser (stream_t *p_stream);
virtual ~Parser ();
M3U8 * parse (const std::string &);
private:
void parseAdaptationSet(BasePeriod *, const AttributesTag *);
void parseRepresentation(BaseAdaptationSet *, const AttributesTag *);
void parseRepresentation(BaseAdaptationSet *, const AttributesTag *,
const std::list<Tag *>&);
void parseSegments(Representation *, const std::list<Tag *>&);
std::list<Tag *> parseEntries(stream_t *);
stream_t *p_stream;
};
}
}
#endif // PARSER_HPP
/*
* Representation.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.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <cstdlib>
#include "Representation.hpp"
#include "M3U8.hpp"
#include "../adaptative/playlist/BasePeriod.h"
#include "../adaptative/playlist/BaseAdaptationSet.h"
#include "../adaptative/playlist/SegmentList.h"
using namespace hls::playlist;
Representation::Representation ( BaseAdaptationSet *set ) :
BaseRepresentation( set )
{
b_live = true;
}
Representation::~Representation ()
{
}
bool Representation::isLive() const
{
return b_live;
}
void Representation::localMergeWithPlaylist(M3U8 *updated, mtime_t prunebarrier)
{
BasePeriod *period = updated->getFirstPeriod();
if(!period)
return;
BaseAdaptationSet *adapt = period->getAdaptationSets().front();
if(!adapt)
return;
BaseRepresentation *rep = adapt->getRepresentations().front();
if(!rep)
return;
this->mergeWith( rep, prunebarrier );
}
void Representation::mergeWith(SegmentInformation *seginfo, mtime_t prunebarrier)
{
BaseRepresentation::mergeWith(seginfo, prunebarrier);
}
/*
* Representation.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 HLSREPRESENTATION_H_
#define HLSREPRESENTATION_H_
#include "../adaptative/playlist/BaseRepresentation.h"
#include "../adaptative/tools/Properties.hpp"
namespace hls
{
namespace playlist
{
class M3U8;
using namespace adaptative::playlist;
class Representation : public BaseRepresentation
{
friend class Parser;
public:
Representation( BaseAdaptationSet * );
virtual ~Representation ();
void localMergeWithPlaylist(M3U8 *, mtime_t);
bool isLive() const;
virtual void mergeWith(SegmentInformation *, mtime_t); /* reimpl */
private:
bool b_live;
Property<std::string> playlistUrl;
Property<std::string> audio;
Property<std::string> video;
Property<std::string> subtitles;
Property<std::string> closedcaptions;
};
}
}
#endif /* HLSREPRESENTATION_H_ */
/*
* Tags.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 "Tags.hpp"
#include <sstream>
#include <stack>
#define __STDC_CONSTANT_MACROS
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
using namespace hls::playlist;
Attribute::Attribute(const std::string &name_, const std::string &value_)
{
name = name_;
value = value_;
}
uint64_t Attribute::decimal() const
{
std::istringstream is(value);
uint64_t ret;
is >> ret;
return ret;
}
double Attribute::floatingPoint() const
{
std::istringstream is(value);
is.imbue(std::locale(""));
double ret;
is >> ret;
return ret;
}
std::vector<uint8_t> Attribute::hexSequence() const
{
std::vector<uint8_t> ret;
if(value.length() > 2 && (value.substr(0, 2) == "0X" || value.substr(0, 2) == "0x") )
{
for(size_t i=2; i<=(value.length() - 2); i+=2)
{
unsigned val;
std::stringstream ss(value.substr(i, 2));
ss >> std::hex >> val;
ret.push_back(val);
}
}
return ret;
}
std::pair<std::size_t,std::size_t> Attribute::getByteRange() const
{
std::pair<std::size_t,std::size_t> ret;
std::size_t length = 0;
std::size_t offset = 0;
std::istringstream is(value);
if(!is.eof())
{
is >> length;
if(!is.eof())
{
char c = is.get();
if(c == '@' && !is.eof())
is >> offset;
}
}
ret = std::make_pair(offset, length);
return ret;
}
std::string Attribute::quotedString() const
{
if(value.length() < 2)
return "";
std::istringstream is(value.substr(1, value.length() - 2));
std::ostringstream os;
char c;
while(is.get(c))
{
if(c == '\\')
{
if(!is.get(c))
break;
}
os << c;
}
return os.str();
}
Tag::Tag(int type_)
{
type = type_;
}
Tag::~Tag()
{
}
int Tag::getType() const
{
return type;
}
SingleValueTag::SingleValueTag(int type, const std::string &v)
: Tag(type), attr("", v)
{
}
SingleValueTag::~SingleValueTag()
{
}
const Attribute &SingleValueTag::getValue() const
{
return attr;
}
AttributesTag::AttributesTag(int type, const std::string &v) : Tag(type)
{
parseAttributes(v);
}
AttributesTag::~AttributesTag()
{
std::list<Attribute *>::const_iterator it;
for(it = attributes.begin(); it != attributes.end(); ++it)
delete *it;
}
const Attribute * AttributesTag::getAttributeByName(const char *name) const
{
std::list<Attribute *>::const_iterator it;
for(it = attributes.begin(); it != attributes.end(); ++it)
if((*it)->name == name)
return *it;
return NULL;
}
void AttributesTag::addAttribute(Attribute *attr)
{
attributes.push_back(attr);
}
void AttributesTag::parseAttributes(const std::string &field)
{
std::istringstream iss(field);
std::ostringstream oss;
while(!iss.eof())
{
/* parse attribute name */
while(!iss.eof())
{
char c = iss.peek();
if((c >= 'A' && c <= 'Z') || c == '-')
{
oss.put((char)iss.get());
}
else if(c == '=')
{
iss.get();
break;
}
else /* out of range */
return;
}
std::string attrname = oss.str();
oss.str("");
/* parse attributes value */
bool b_quoted = false;
while(!iss.eof())
{
char c = iss.peek();
if(c == '\\' && b_quoted)
{
iss.get();
}
else if(c == ',' && !b_quoted)
{
iss.get();
break;
}
else if(c == '"')
{
b_quoted = !b_quoted;
}
if(!iss.eof())
oss.put((char)iss.get());
}
std::string attrvalue = oss.str();
oss.str("");
Attribute *attribute = new (std::nothrow) Attribute(attrname, attrvalue);
if(attribute)
attributes.push_back(attribute);
}
}
URITag::URITag(const std::string &v) : AttributesTag(URITag::EXTINF, v)
{
parseAttributes(v);
}
URITag::~URITag()
{
}
void URITag::parseAttributes(const std::string &field)
{
std::size_t pos = field.find(',');
if(pos != std::string::npos)
{
Attribute *attr = new (std::nothrow) Attribute("DURATION", field.substr(0, pos));
if(attr)
addAttribute(attr);
attr = new (std::nothrow) Attribute("TITLE", field.substr(pos));
if(attr)
addAttribute(attr);
}
}
Tag * TagFactory::createTagByName(const std::string &name, const std::string &value)
{
struct
{
const char *psz;
const int i;
} const exttagmapping[] = {
{"EXT-X-BYTERANGE", SingleValueTag::EXTXBYTERANGE},
{"EXT-X-DISCONTINUITY", Tag::EXTXDISCONTINUITY},
{"EXT-X-KEY", AttributesTag::EXTXKEY},
{"EXT-X-MAP", AttributesTag::EXTXMAP},
{"EXT-X-PROGRAM-DATE-TIME", SingleValueTag::EXTXPROGRAMDATETIME},
{"EXT-X-TARGETDURATION", SingleValueTag::EXTXTARGETDURATION},
{"EXT-X-MEDIA-SEQUENCE", SingleValueTag::EXTXMEDIASEQUENCE},
{"EXT-X-DISCONTINUITY-SEQUENCE", SingleValueTag::EXTXDISCONTINUITYSEQUENCE},
{"EXT-X-ENDLIST", Tag::EXTXENDLIST},
{"EXT-X-PLAYLIST-TYPE", SingleValueTag::EXTXPLAYLISTTYPE},
{"EXT-X-I-FRAMES-ONLY", Tag::EXTXIFRAMESONLY},
{"EXT-X-MEDIA", AttributesTag::EXTXMEDIA},
{"EXT-X-STREAM-INF", AttributesTag::EXTXSTREAMINF},
{"EXTINF", URITag::EXTINF},
{NULL, 0},
};
for(int i=0; exttagmapping[i].psz; i++)
{
if(name != exttagmapping[i].psz)
continue;
switch(exttagmapping[i].i)
{
case Tag::EXTXDISCONTINUITY:
case Tag::EXTXENDLIST:
case Tag::EXTXIFRAMESONLY:
return new (std::nothrow) Tag(exttagmapping[i].i);
case SingleValueTag::EXTXVERSION:
case SingleValueTag::EXTXBYTERANGE:
case SingleValueTag::EXTXPROGRAMDATETIME:
case SingleValueTag::EXTXTARGETDURATION:
case SingleValueTag::EXTXMEDIASEQUENCE:
case SingleValueTag::EXTXDISCONTINUITYSEQUENCE:
case SingleValueTag::EXTXPLAYLISTTYPE:
return new (std::nothrow) SingleValueTag(exttagmapping[i].i, value);
case AttributesTag::EXTXKEY:
case AttributesTag::EXTXMAP:
case AttributesTag::EXTXMEDIA:
case AttributesTag::EXTXSTREAMINF:
return new (std::nothrow) AttributesTag(exttagmapping[i].i, value);
case URITag::EXTINF:
return new URITag(value);
}
}
return NULL;
}
/*
* Tags.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 TAGS_HPP
#define TAGS_HPP
#ifndef __STDC_CONSTANT_MACROS
#define __STDC_CONSTANT_MACROS
#endif
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdint.h>
#include <string>
#include <vector>
#include <list>
#include <utility>
namespace hls
{
namespace playlist
{
class Attribute
{
public:
Attribute(const std::string &, const std::string &);
uint64_t decimal() const;
std::string quotedString() const;
double floatingPoint() const;
std::vector<uint8_t> hexSequence() const;
std::pair<std::size_t,std::size_t> getByteRange() const;
std::string name;
std::string value;
};
class Tag
{
public:
enum
{
EXTXDISCONTINUITY = 0,
EXTXENDLIST,
EXTXIFRAMESONLY,
};
Tag(int);
virtual ~Tag();
int getType() const;
private:
int type;
};
class SingleValueTag : public Tag
{
public:
enum
{
EXTXVERSION = 10,
EXTXBYTERANGE,
EXTXPROGRAMDATETIME,
EXTXTARGETDURATION,
EXTXMEDIASEQUENCE,
EXTXDISCONTINUITYSEQUENCE,
EXTXPLAYLISTTYPE,
};
SingleValueTag(int, const std::string &);
virtual ~SingleValueTag();
const Attribute & getValue() const;
private:
Attribute attr;
};
class AttributesTag : public Tag
{
public:
enum
{
EXTXKEY = 20,
EXTXMAP,
EXTXMEDIA,
EXTXSTREAMINF,
};
AttributesTag(int, const std::string &);
virtual ~AttributesTag();
const Attribute * getAttributeByName(const char *) const;
void addAttribute(Attribute *);
protected:
virtual void parseAttributes(const std::string &);
std::list<Attribute *> attributes;
};
class URITag : public AttributesTag
{
public:
enum
{
EXTINF = 30
};
URITag(const std::string &);
virtual ~URITag();
protected:
virtual void parseAttributes(const std::string &);
};
class TagFactory
{
public:
static Tag * createTagByName(const std::string &, const std::string &);
static Attribute * createAttributeByName(const std::string &);
};
}
}
#endif // TAGS_HPP
...@@ -465,6 +465,7 @@ modules/demux/demuxdump.c ...@@ -465,6 +465,7 @@ modules/demux/demuxdump.c
modules/demux/dirac.c modules/demux/dirac.c
modules/demux/flac.c modules/demux/flac.c
modules/demux/gme.c modules/demux/gme.c
modules/demux/hls/hls.cpp
modules/demux/image.c modules/demux/image.c
modules/demux/mjpeg.c modules/demux/mjpeg.c
modules/demux/mkv/chapter_command.cpp modules/demux/mkv/chapter_command.cpp
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment