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

demux: adaptative: add smooth streaming support

parent 9ec2d132
......@@ -389,8 +389,32 @@ libadaptative_hls_SOURCES = \
demux/hls/HLSStreams.hpp \
demux/hls/HLSStreams.cpp
libadaptative_smooth_SOURCES = \
demux/smooth/mp4/IndexReader.cpp \
demux/smooth/mp4/IndexReader.hpp \
demux/smooth/playlist/ForgedInitSegment.hpp \
demux/smooth/playlist/ForgedInitSegment.cpp \
demux/smooth/playlist/Manifest.hpp \
demux/smooth/playlist/Manifest.cpp \
demux/smooth/playlist/MemoryChunk.hpp \
demux/smooth/playlist/MemoryChunk.cpp \
demux/smooth/playlist/Parser.hpp \
demux/smooth/playlist/Parser.cpp \
demux/smooth/playlist/Representation.hpp \
demux/smooth/playlist/Representation.cpp \
demux/smooth/playlist/SmoothSegment.hpp \
demux/smooth/playlist/SmoothSegment.cpp \
demux/smooth/SmoothManager.hpp \
demux/smooth/SmoothManager.cpp \
demux/smooth/SmoothStreamFormat.hpp \
demux/smooth/SmoothStream.hpp \
demux/smooth/SmoothStream.cpp
libadaptative_smooth_SOURCES += mux/mp4/libmp4mux.c mux/mp4/libmp4mux.h \
packetizer/h264_nal.c packetizer/h264_nal.h
libadaptative_plugin_la_SOURCES += $(libadaptative_hls_SOURCES)
libadaptative_plugin_la_SOURCES += $(libadaptative_dash_SOURCES)
libadaptative_plugin_la_SOURCES += $(libadaptative_smooth_SOURCES)
libadaptative_plugin_la_SOURCES += demux/adaptative/adaptative.cpp
libadaptative_plugin_la_SOURCES += demux/mp4/libmp4.c demux/mp4/libmp4.h
libadaptative_plugin_la_CXXFLAGS = $(AM_CFLAGS) -I$(srcdir)/demux/adaptative
......
......@@ -43,7 +43,9 @@
#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/Parser.hpp"
using namespace adaptative::logic;
using namespace adaptative::playlist;
......@@ -52,6 +54,8 @@ using namespace dash::mpd;
using namespace dash;
using namespace hls;
using namespace hls::playlist;
using namespace smooth;
using namespace smooth::playlist;
/*****************************************************************************
* Module descriptor
......@@ -156,6 +160,29 @@ static int Open(vlc_object_t *p_obj)
new (std::nothrow) HLSStreamFactory,
static_cast<AbstractAdaptationLogic::LogicType>(logic));
}
else if(SmoothManager::isSmoothStreaming(p_demux->s))
{
//Build a XML tree
DOMParser parser(p_demux->s);
if( !parser.parse() )
{
msg_Err( p_demux, "Could not parse Manifest" );
return VLC_EGENERIC;
}
ManifestParser *mparser = new ManifestParser(parser.getRootNode(), p_demux->s, playlisturl);
smooth::playlist::Manifest *p_playlist = mparser->parse();
delete mparser;
if(p_playlist == NULL)
{
msg_Err( p_demux, "Cannot create Manifest");
return VLC_EGENERIC;
}
p_manager = new SmoothManager( p_demux, p_playlist,
new (std::nothrow) SmoothStreamFactory,
static_cast<AbstractAdaptationLogic::LogicType>(logic) );
}
if(!p_manager || !p_manager->start())
{
......
......@@ -47,6 +47,11 @@ TimescaleAble::~TimescaleAble()
{
}
void TimescaleAble::setParentTimescale(TimescaleAble *parent)
{
parentTimescale = parent;
}
uint64_t TimescaleAble::inheritTimescale() const
{
if(timescale.Get())
......
......@@ -44,6 +44,7 @@ namespace adaptative
public:
TimescaleAble( TimescaleAble * = NULL );
~TimescaleAble();
void setParentTimescale( TimescaleAble * );
uint64_t inheritTimescale() const;
Property<uint64_t> timescale;
......
/*
* SmoothManager.cpp
*****************************************************************************
* 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.
*****************************************************************************/
#include "SmoothManager.hpp"
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "../adaptative/logic/RateBasedAdaptationLogic.h"
#include "../adaptative/tools/Retrieve.hpp"
#include "playlist/Parser.hpp"
#include "../adaptative/xml/DOMParser.h"
#include <vlc_stream.h>
#include <vlc_demux.h>
#include <vlc_charset.h>
#include <time.h>
using namespace adaptative;
using namespace adaptative::logic;
using namespace smooth;
using namespace smooth::playlist;
SmoothManager::SmoothManager(demux_t *demux_, Manifest *playlist,
AbstractStreamFactory *factory,
AbstractAdaptationLogic::LogicType type) :
PlaylistManager(demux_, playlist, factory, type)
{
}
SmoothManager::~SmoothManager()
{
}
Manifest * SmoothManager::fetchManifest()
{
std::string playlisturl(p_demux->psz_access);
playlisturl.append("://");
playlisturl.append(p_demux->psz_location);
block_t *p_block = Retrieve::HTTP(VLC_OBJECT(p_demux), playlisturl);
if(!p_block)
return NULL;
stream_t *memorystream = stream_MemoryNew(p_demux, p_block->p_buffer, p_block->i_buffer, true);
if(!memorystream)
{
block_Release(p_block);
return NULL;
}
xml::DOMParser parser(memorystream);
if(!parser.parse())
{
stream_Delete(memorystream);
block_Release(p_block);
return NULL;
}
Manifest *manifest = NULL;
ManifestParser *manifestParser = new (std::nothrow) ManifestParser(parser.getRootNode(), memorystream, playlisturl);
if(manifestParser)
{
manifest = manifestParser->parse();
delete manifestParser;
}
stream_Delete(memorystream);
block_Release(p_block);
return manifest;
}
bool SmoothManager::updatePlaylist()
{
/* FIXME: do update from manifest after resuming from pause */
if(!playlist->isLive() || !playlist->minUpdatePeriod.Get())
return true;
time_t now = time(NULL);
if(nextPlaylistupdate && now < nextPlaylistupdate)
return true;
mtime_t mininterval = 0;
mtime_t maxinterval = 0;
std::vector<AbstractStream *>::iterator it;
for(it=streams.begin(); it!=streams.end(); it++)
(*it)->prune();
/* Timelines updates should be inlined in tfrf atoms.
We'll just care about pruning live timeline then. */
#if 0
if(nextPlaylistupdate)
{
Manifest *newManifest = fetchManifest();
if(newManifest)
{
newManifest->getPlaylistDurationsRange(&mininterval, &maxinterval);
playlist->mergeWith(newManifest, 0);
delete newManifest;
std::vector<AbstractStream *>::iterator it;
for(it=streams.begin(); it!=streams.end(); it++)
(*it)->prune();
#ifdef NDEBUG
playlist->debug();
#endif
}
}
#endif
/* Compute new Manifest update time */
if(!mininterval && !maxinterval)
playlist->getPlaylistDurationsRange(&mininterval, &maxinterval);
if(playlist->minUpdatePeriod.Get() > mininterval)
mininterval = playlist->minUpdatePeriod.Get();
if(mininterval < 5 * CLOCK_FREQ)
mininterval = 5 * CLOCK_FREQ;
if(maxinterval < mininterval)
maxinterval = mininterval;
nextPlaylistupdate = now + (mininterval + (maxinterval - mininterval) / 2) / CLOCK_FREQ;
// msg_Dbg(p_demux, "Updated Manifest, next update in %" PRId64 "s (%" PRId64 "..%" PRId64 ")",
// nextPlaylistupdate - now, mininterval/ CLOCK_FREQ, maxinterval/ CLOCK_FREQ );
return true;
}
bool SmoothManager::isSmoothStreaming(stream_t *stream)
{
const uint8_t *peek;
int i_size = stream_Peek( stream, &peek, 512 );
if( i_size < 512 )
return false;
char *peeked = (char*) malloc( 512 );
if( unlikely( peeked == NULL ) )
return false;
memcpy( peeked, peek, 512 );
peeked[511] = peeked[510] = '\0';
char *str;
if( !memcmp( peeked, "\xFF\xFE", 2 ) )
{
str = FromCharset( "UTF-16LE", peeked, 512 );
free( peeked );
}
else if( !memcmp( peeked, "\xFE\xFF", 2 ) )
{
str = FromCharset( "UTF-16BE", peeked, 512 );
free( peeked );
}
else
str = peeked;
if( str == NULL )
return false;
bool ret = strstr( str, "<SmoothStreamingMedia" ) != NULL;
free( str );
return ret;
}
AbstractAdaptationLogic *SmoothManager::createLogic(AbstractAdaptationLogic::LogicType type)
{
switch(type)
{
case AbstractAdaptationLogic::FixedRate:
{
size_t bps = var_InheritInteger(p_demux, "adaptative-bw") * 8192;
return new (std::nothrow) FixedRateAdaptationLogic(bps);
}
case AbstractAdaptationLogic::Default:
case AbstractAdaptationLogic::RateBased:
{
int width = var_InheritInteger(p_demux, "adaptative-width");
int height = var_InheritInteger(p_demux, "adaptative-height");
return new (std::nothrow) RateBasedAdaptationLogic(width, height);
}
default:
return PlaylistManager::createLogic(type);
}
}
/*
* SmoothManager.hpp
*****************************************************************************
* 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.
*****************************************************************************/
#ifndef SMOOTHMANAGER_HPP
#define SMOOTHMANAGER_HPP
#include "../adaptative/PlaylistManager.h"
#include "../adaptative/logic/AbstractAdaptationLogic.h"
#include "playlist/Manifest.hpp"
namespace smooth
{
using namespace adaptative;
class SmoothManager : public PlaylistManager
{
public:
SmoothManager( demux_t *, playlist::Manifest *,
AbstractStreamFactory *,
logic::AbstractAdaptationLogic::LogicType type );
virtual ~SmoothManager();
virtual bool updatePlaylist(); //reimpl
virtual AbstractAdaptationLogic *createLogic(AbstractAdaptationLogic::LogicType);
static bool isSmoothStreaming(stream_t *);
private:
playlist::Manifest * fetchManifest();
};
}
#endif // SMOOTHMANAGER_HPP
/*
* SmoothStream.cpp
*****************************************************************************
* Copyright (C) 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.
*****************************************************************************/
#include "SmoothStream.hpp"
#include "SmoothStreamFormat.hpp"
#include <vlc_demux.h>
using namespace smooth;
SmoothStream::SmoothStream(demux_t *demux, const StreamFormat &format)
:AbstractStream(demux, format)
{
}
AbstractDemuxer * SmoothStream::createDemux(const StreamFormat &format)
{
AbstractDemuxer *ret = NULL;
switch((unsigned)format)
{
case SmoothStreamFormat::MP4:
ret = new Demuxer(p_realdemux, "mp4", fakeesout->getEsOut(), demuxersource);
break;
default:
case StreamFormat::UNSUPPORTED:
break;
}
if(ret && !ret->create())
{
delete ret;
ret = NULL;
}
else fakeesout->commandsqueue.Commit();
return ret;
}
block_t * SmoothStream::checkBlock(block_t *p_block, bool)
{
return p_block;
}
AbstractStream * SmoothStreamFactory::create(demux_t *realdemux, const StreamFormat &format,
AbstractAdaptationLogic *logic, SegmentTracker *tracker,
HTTPConnectionManager *manager) const
{
SmoothStream *stream;
try
{
stream = new SmoothStream(realdemux, format);
} catch (int) {
return NULL;
}
stream->bind(logic, tracker, manager);
return stream;
}
/*
* SmoothStream.hpp
*****************************************************************************
* Copyright (C) 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.
*****************************************************************************/
#ifndef SMOOTHSTREAM_HPP
#define SMOOTHSTREAM_HPP
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "../adaptative/Streams.hpp"
namespace smooth
{
using namespace adaptative;
class SmoothStream : public AbstractStream
{
public:
SmoothStream(demux_t *, const StreamFormat &);
protected:
virtual AbstractDemuxer * createDemux(const StreamFormat &); /* impl */
virtual block_t *checkBlock(block_t *, bool) /* impl */;
};
class SmoothStreamFactory : public AbstractStreamFactory
{
public:
virtual AbstractStream *create(demux_t*, const StreamFormat &,
AbstractAdaptationLogic *, SegmentTracker *,
HTTPConnectionManager *) const;
};
}
#endif // SMOOTHSTREAM_HPP
/*
* SmoothStreamFormat.hpp
*****************************************************************************
* Copyright (C) 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.
*****************************************************************************/
#ifndef SMOOTHSTREAMFORMAT_HPP
#define SMOOTHSTREAMFORMAT_HPP
#include "../adaptative/StreamFormat.hpp"
namespace smooth
{
using namespace adaptative;
class SmoothStreamFormat : public StreamFormat
{
public:
static const unsigned MP4 = StreamFormat::UNSUPPORTED + 1;
};
}
#endif // SMOOTHSTREAMFORMAT_HPP
/*
* IndexReader.cpp
*****************************************************************************
* Copyright (C) 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.
*****************************************************************************/
#include "IndexReader.hpp"
#include "../adaptative/playlist/BaseRepresentation.h"
#include "../adaptative/playlist/BaseAdaptationSet.h"
#include "../adaptative/playlist/SegmentTemplate.h"
#include "../adaptative/playlist/SegmentTimeline.h"
#include "../adaptative/playlist/AbstractPlaylist.hpp"
using namespace adaptative::mp4;
using namespace smooth::mp4;
IndexReader::IndexReader(vlc_object_t *obj)
: AtomsReader(obj)
{
}
bool IndexReader::parseIndex(block_t *p_block, BaseRepresentation *rep)
{
if(!rep || !parseBlock(p_block))
return false;
const MP4_Box_t *uuid_box = MP4_BoxGet( rootbox, "moof/traf/uuid" );
while( uuid_box && uuid_box->i_type == ATOM_uuid )
{
if ( !CmpUUID( &uuid_box->i_uuid, &TfrfBoxUUID ) )
break;
uuid_box = uuid_box->p_next;
}
SegmentTimeline *timelineadd = new (std::nothrow) SegmentTimeline(rep->inheritTimescale());
if (uuid_box && timelineadd)
{
const MP4_Box_data_tfrf_t *p_tfrfdata = uuid_box->data.p_tfrf;
for ( uint8_t i=0; i<p_tfrfdata->i_fragment_count; i++ )
{
uint64_t dur = p_tfrfdata->p_tfrf_data_fields[i].i_fragment_duration;
uint64_t stime = p_tfrfdata->p_tfrf_data_fields[i].i_fragment_abs_time;
timelineadd->addElement(i+1, dur, 0, stime);
}
rep->mergeWithTimeline(timelineadd);
delete timelineadd;
#ifndef NDEBUG
msg_Dbg(rep->getPlaylist()->getVLCObject(), "Updated timeline from tfrf");
rep->debug(rep->getPlaylist()->getVLCObject(), 0);
#endif
}
return true;
}
/*
* IndexReader.hpp
*****************************************************************************
* Copyright (C) 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.
*****************************************************************************/
#ifndef SMOOTHINDEXREADER_HPP
#define SMOOTHINDEXREADER_HPP
#include "../adaptative/mp4/AtomsReader.hpp"
namespace adaptative
{
namespace playlist
{
class BaseRepresentation;
}
}
namespace smooth
{
namespace mp4
{
using namespace adaptative::mp4;
using namespace adaptative::playlist;
class IndexReader : public AtomsReader
{
public:
IndexReader(vlc_object_t *);
bool parseIndex(block_t *, BaseRepresentation *);
};
}
}
#endif // SMOOTHINDEXREADER_HPP
/*
* ForgedInitSegment.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.
*****************************************************************************/
#include "ForgedInitSegment.hpp"
#include "MemoryChunk.hpp"
#include "../adaptative/playlist/SegmentChunk.hpp"
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
extern "C"
{
#include "../../mux/mp4/libmp4mux.h"
#include "../../demux/mp4/libmp4.h" /* majors */
}
using namespace adaptative::playlist;
using namespace smooth::playlist;
using namespace smooth::http;
ForgedInitSegment::ForgedInitSegment(ICanonicalUrl *parent,
const std::string &type_,
uint64_t timescale_,
uint64_t duration_) :
InitSegment(parent), TimescaleAble()
{
type = type_;
duration.Set(duration_);
extradata = NULL;
i_extradata = 0;
timescale.Set(timescale_);
formatex.cbSize = 0;
formatex.nAvgBytesPerSec = 0;
formatex.nBlockAlign = 0;
formatex.nChannels = 0;
formatex.nSamplesPerSec = 0;
formatex.wBitsPerSample = 0;
formatex.wFormatTag = 0;
width = height = 0;
fourcc = 0;
es_type = 0;
track_id = 1;
}
ForgedInitSegment::~ForgedInitSegment()
{
free(extradata);
}
static uint8_t *HexDecode(const std::string &s, size_t *decoded_size)
{
*decoded_size = s.size() / 2;
uint8_t *data = new (std::nothrow) uint8_t[*decoded_size];
if(data)
{
for(size_t i=0; i<*decoded_size; i++)