diff --git a/XBMC.files b/XBMC.files index d8a0ab4f42..37f1e0b501 100644 --- a/XBMC.files +++ b/XBMC.files @@ -9845,25 +9845,6 @@ xbmc/interfaces/builtins/WeatherBuiltins.h tools/android/packaging/xbmc/src/org/xbmc/kodi/XBMCProperties.java.in xbmc/android/jni/AudioDeviceInfo.cpp xbmc/android/jni/AudioDeviceInfo.h -xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHByteStream.cpp -xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHByteStream.h -xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHCodecHandler.cpp -xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHCodecHandler.h -xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHFragmentedSampleReader.cpp -xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHFragmentedSampleReader.h -xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHFragmentObserver.h -xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHSession.cpp -xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHSession.h -xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHStream.cpp -xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHStream.h -xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHTree.cpp -xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHTree.h -xbmc/cores/dvdplayer/DVDDemuxers/dash/helpers.cpp -xbmc/cores/dvdplayer/DVDDemuxers/dash/helpers.h -xbmc/cores/dvdplayer/DVDDemuxers/dash/oscompat.cpp -xbmc/cores/dvdplayer/DVDDemuxers/dash/oscompat.h -xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxMPD.cpp -xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxMPD.h xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamNULL.h xbmc/cores/dvdplayer/Makefile.in xbmc/cores/dvdplayer/DVDDemuxers/Makefile.in @@ -9974,3 +9955,28 @@ xbmc/android/jni/PlaybackState.h xbmc/android/jni/PlaybackState.cpp xbmc/android/jni/BitmapFactory.h xbmc/android/jni/BitmapFactory.cpp +xbmc/cores/dvdplayer/DVDDemuxers/dash/SmoothTree.cpp +xbmc/cores/dvdplayer/DVDDemuxers/dash/SmoothTree.h +xbmc/cores/dvdplayer/DVDDemuxers/adaptive/common/AdaptiveTree.cpp +xbmc/cores/dvdplayer/DVDDemuxers/adaptive/common/AdaptiveTree.h +xbmc/cores/dvdplayer/DVDDemuxers/adaptive/parsers/DASHTree.cpp +xbmc/cores/dvdplayer/DVDDemuxers/adaptive/parsers/DASHTree.h +xbmc/cores/dvdplayer/DVDDemuxers/adaptive/parsers/SmoothTree.cpp +xbmc/cores/dvdplayer/DVDDemuxers/adaptive/parsers/SmoothTree.h +xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHByteStream.cpp +xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHByteStream.h +xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHCodecHandler.cpp +xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHCodecHandler.h +xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHFragmentedSampleReader.cpp +xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHFragmentedSampleReader.h +xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHFragmentObserver.h +xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHSession.cpp +xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHSession.h +xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHStream.cpp +xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHStream.h +xbmc/cores/dvdplayer/DVDDemuxers/adaptive/helpers.cpp +xbmc/cores/dvdplayer/DVDDemuxers/adaptive/helpers.h +xbmc/cores/dvdplayer/DVDDemuxers/adaptive/oscompat.cpp +xbmc/cores/dvdplayer/DVDDemuxers/adaptive/oscompat.h +xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxAdaptive.cpp +xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxAdaptive.h diff --git a/XBMC.includes b/XBMC.includes index 51a0589ea6..552fee1ee3 100644 --- a/XBMC.includes +++ b/XBMC.includes @@ -2866,7 +2866,6 @@ xbmc/video/jobs xbmc/video/videosync tools/android/packaging/xbmc/src/org/xbmc/kodi system/shaders -xbmc/cores/dvdplayer/DVDInputStreams/dash tools/android/packaging/xbmc/res/layout tools/android/packaging/xbmc/res/values . @@ -2882,3 +2881,6 @@ lib/libUPnP xbmc/network/android xbmc/listproviders tools/android/packaging/xbmc/src/org/xbmc/kodi/interfaces +xbmc/cores/dvdplayer/DVDDemuxers/adaptive/common +xbmc/cores/dvdplayer/DVDDemuxers/adaptive/parsers +xbmc/cores/dvdplayer/DVDDemuxers/adaptive diff --git a/tools/depends/target/bento4/Makefile b/tools/depends/target/bento4/Makefile index 2d481e4b6f..9a437dc233 100644 --- a/tools/depends/target/bento4/Makefile +++ b/tools/depends/target/bento4/Makefile @@ -3,7 +3,7 @@ DEPS= ../../Makefile.include Makefile # lib name, version LIBNAME=Bento4 -VERSION=HEAD +VERSION=inputstream SOURCE=archive ARCHIVE=$(VERSION).tar.gz GIT_BASE_URL=https://github.com/koying diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxAdaptive.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxAdaptive.cpp new file mode 100644 index 0000000000..15eee239e6 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxAdaptive.cpp @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2016 Christian Browet + * Copyright (C) 2016-2016 peak3d + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDDemuxAdaptive.h" + +#include "DVDDemuxPacket.h" +#include "DVDDemuxUtils.h" +#include "DVDInputStreams/DVDInputStream.h" + +#include "adaptive/DASHByteStream.h" + +#ifdef TARGET_ANDROID +#include "android/jni/SystemProperties.h" +#endif +#ifdef TARGET_WINDOWS +#pragma comment(lib, "libexpat.lib") +#pragma comment(lib, "ap4.lib") +#endif + +#include "utils/StringUtils.h" +#include "utils/log.h" + +CDVDDemuxAdaptive::CDVDDemuxAdaptive() + : CDVDDemux() +{ + CLog::Log(LOGDEBUG, "CDVDDemuxAdaptive::%s", __FUNCTION__); +} + +CDVDDemuxAdaptive::~CDVDDemuxAdaptive() +{ + CLog::Log(LOGDEBUG, "CDVDDemuxAdaptive::%s", __FUNCTION__); +} + +bool CDVDDemuxAdaptive::Open(CDVDInputStream* pInput, uint32_t maxWidth, uint32_t maxHeight) +{ + CLog::Log(LOGINFO, "CDVDDemuxAdaptive - matching against %d x %d", maxWidth, maxHeight); + + CDASHSession::MANIFEST_TYPE type = CDASHSession::MANIFEST_TYPE_UNKNOWN; + + if (pInput->GetFileItem().GetMimeType() == "video/vnd.mpeg.dash.mpd" || pInput->GetFileItem().IsType(".mpd")) //MPD + type = CDASHSession::MANIFEST_TYPE_MPD; + else if (pInput->GetFileItem().GetMimeType() == "application/vnd.ms-sstr+xml" || pInput->GetFileItem().IsType(".ismc") || pInput->GetFileItem().IsType(".ism")) //ISM + type = CDASHSession::MANIFEST_TYPE_ISM; + + if (type == CDASHSession::MANIFEST_TYPE_UNKNOWN) + return false; + + m_session.reset(new CDASHSession(type, pInput->GetFileName(), maxWidth, maxHeight, "", "", "special://profile/")); + + if (!m_session->initialize()) + { + m_session = nullptr; + return false; + } + return true; +} + +void CDVDDemuxAdaptive::Dispose() +{ +} + +void CDVDDemuxAdaptive::Reset() +{ +} + +void CDVDDemuxAdaptive::Abort() +{ +} + +void CDVDDemuxAdaptive::Flush() +{ +} + +DemuxPacket*CDVDDemuxAdaptive::Read() +{ + if (!m_session) + return NULL; + + CDASHFragmentedSampleReader *sr(m_session->GetNextSample()); + + if (m_session->CheckChange()) + { + DemuxPacket *p = CDVDDemuxUtils::AllocateDemuxPacket(0); + p->iStreamId = DMX_SPECIALID_STREAMCHANGE; + CLog::Log(LOGDEBUG, "DMX_SPECIALID_STREAMCHANGE"); + return p; + } + + if (sr) + { + DemuxPacket *p = CDVDDemuxUtils::AllocateDemuxPacket(sr->GetSampleDataSize()); + p->dts = sr->DTS() * 1000000; + p->pts = sr->PTS() * 1000000; + p->duration = sr->GetDuration() * 1000000; + p->iStreamId = sr->GetStreamId(); + p->iGroupId = 0; + p->iSize = sr->GetSampleDataSize(); + memcpy(p->pData, sr->GetSampleData(), p->iSize); + + CLog::Log(LOGDEBUG, "DTS: %0.4f, PTS:%0.4f, ID: %u SZ: %d", p->dts, p->pts, p->iStreamId, p->iSize); + + sr->ReadSample(); + return p; + } + return NULL; +} + +bool CDVDDemuxAdaptive::SeekTime(int time, bool backwards, double* startpts) +{ + if (!m_session) + return false; + + return m_session->SeekTime(static_cast(time)*0.001f, 0, !backwards); +} + +void CDVDDemuxAdaptive::SetSpeed(int speed) +{ +} + +int CDVDDemuxAdaptive::GetNrOfStreams() +{ + int n = 0; + if (m_session) + n = m_session->GetStreamCount(); + + return n; +} + +CDemuxStream* CDVDDemuxAdaptive::GetStream(int streamid) +{ + CDASHSession::STREAM *stream(m_session->GetStream(streamid)); + if (!stream) + { + CLog::Log(LOGERROR, "CDVDDemuxAdaptive::GetStream(%d): error getting stream", streamid); + return nullptr; + } + + return stream->dmuxstrm; +} + +void CDVDDemuxAdaptive::EnableStream(int streamid, bool enable) +{ + CLog::Log(LOGDEBUG, "EnableStream(%d: %s)", streamid, enable?"true":"false"); + + if (!m_session) + return; + + CDASHSession::STREAM *stream(m_session->GetStream(streamid)); + if (!stream) + return; + + if (enable) + { + if (stream->enabled) + return; + + stream->enabled = true; + + stream->stream_.start_stream(~0, m_session->GetWidth(), m_session->GetHeight()); + const adaptive::AdaptiveTree::Representation *rep(stream->stream_.getRepresentation()); + CLog::Log(LOGDEBUG, "Selecting stream with conditions: w: %u, h: %u, bw: %u", + stream->stream_.getWidth(), stream->stream_.getHeight(), stream->stream_.getBandwidth()); + + if (!stream->stream_.select_stream(true, false, stream->dmuxstrm->iPhysicalId >> 16)) + { + CLog::Log(LOGERROR, "Unable to select stream!"); + return stream->disable(); + } + + if(rep != stream->stream_.getRepresentation()) + { + m_session->UpdateStream(*stream); + m_session->CheckChange(true); + } + + stream->input_ = new CDASHByteStream(&stream->stream_); + static const AP4_Track::Type TIDC[adaptive::AdaptiveTree::STREAM_TYPE_COUNT] = { + AP4_Track::TYPE_UNKNOWN, + AP4_Track::TYPE_VIDEO, + AP4_Track::TYPE_AUDIO, + AP4_Track::TYPE_TEXT }; + + AP4_Movie* movie = nullptr; + if (m_session->GetManifestType() == CDASHSession::MANIFEST_TYPE_ISM && stream->stream_.getRepresentation()->get_initialization() == nullptr) + { + //We'll create a Movie out of the things we got from manifest file + //note: movie will be deleted in destructor of stream->input_file_ + movie = new AP4_Movie(); + + AP4_SyntheticSampleTable* sample_table = new AP4_SyntheticSampleTable(); + AP4_SampleDescription *sample_descryption = new AP4_SampleDescription(AP4_SampleDescription::TYPE_UNKNOWN, 0, 0); + if (stream->stream_.getAdaptationSet()->encrypted) + { + static const AP4_UI08 default_key[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; + AP4_ContainerAtom schi(AP4_ATOM_TYPE_SCHI); + schi.AddChild(new AP4_TencAtom(AP4_CENC_ALGORITHM_ID_CTR, 8, default_key)); + sample_descryption = new AP4_ProtectedSampleDescription(0, sample_descryption, 0, AP4_PROTECTION_SCHEME_TYPE_PIFF, 0, "", &schi); + } + sample_table->AddSampleDescription(sample_descryption); + + movie->AddTrack(new AP4_Track(TIDC[stream->stream_.get_type()], sample_table, ~0, stream->stream_.getRepresentation()->timescale_, 0, stream->stream_.getRepresentation()->timescale_, 0, "", 0, 0)); + //Create a dumy MOOV Atom to tell Bento4 its a fragmented stream + AP4_MoovAtom *moov = new AP4_MoovAtom(); + moov->AddChild(new AP4_ContainerAtom(AP4_ATOM_TYPE_MVEX)); + movie->SetMoovAtom(moov); + } + + stream->input_file_ = new AP4_File(*stream->input_, AP4_DefaultAtomFactory::Instance_, true, movie); + movie = stream->input_file_->GetMovie(); + if (movie == NULL) + { + CLog::Log(LOGERROR, "No MOOV in stream!"); + return stream->disable(); + } + + AP4_Track *track = movie->GetTrack(TIDC[stream->stream_.get_type()]); + if (!track) + { + CLog::Log(LOGERROR, "No suitable track found in stream"); + return stream->disable(); + } + + stream->reader_ = new CDASHFragmentedSampleReader(stream->input_, movie, track, streamid, m_session->GetSingleSampleDecryptor(), m_session->GetPresentationTimeOffset()); + stream->reader_->SetObserver(dynamic_cast(m_session.get())); + + if (!stream->dmuxstrm->ExtraSize && stream->reader_->GetExtraDataSize()) + { + // ExtraData is now available...... + stream->dmuxstrm->ExtraSize = stream->reader_->GetExtraDataSize(); + stream->dmuxstrm->ExtraData = (uint8_t*)malloc(stream->dmuxstrm->ExtraSize); + memcpy((void*)stream->dmuxstrm->ExtraData, stream->reader_->GetExtraData(), stream->dmuxstrm->ExtraSize); + // Set the session Changed to force new GetStreamInfo call from kodi -> addon + m_session->CheckChange(true); + } + return; + } + CLog::Log(LOGDEBUG, ">>>> ERROR"); + return stream->disable(); +} + +int CDVDDemuxAdaptive::GetStreamLength() +{ + if (!m_session) + return 0; + + return static_cast(m_session->GetTotalTime()*1000); +} + +std::string CDVDDemuxAdaptive::GetFileName() +{ + if (!m_session) + return ""; + + return m_session->GetUrl(); +} + +void CDVDDemuxAdaptive::GetStreamCodecName(int iStreamId, std::string& strName) +{ + strName = ""; + + CDASHSession::STREAM *stream(m_session->GetStream(iStreamId)); + if (stream) + strName = stream->codecName; +} diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxMPD.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxAdaptive.h similarity index 88% rename from xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxMPD.h rename to xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxAdaptive.h index 0cb7396cba..0c61dcd5da 100644 --- a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxMPD.h +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxAdaptive.h @@ -25,13 +25,13 @@ #include -#include "dash/DASHSession.h" +#include "adaptive/DASHSession.h" -class CDVDDemuxMPD : public CDVDDemux +class CDVDDemuxAdaptive : public CDVDDemux { public: - CDVDDemuxMPD(); - virtual ~CDVDDemuxMPD(); + CDVDDemuxAdaptive(); + virtual ~CDVDDemuxAdaptive(); bool Open(CDVDInputStream* pInput, uint32_t maxWidth, uint32_t maxHeight); void Dispose(); @@ -50,5 +50,5 @@ class CDVDDemuxMPD : public CDVDDemux virtual void GetStreamCodecName(int iStreamId, std::string &strName); protected: - std::shared_ptr m_MPDsession; + std::shared_ptr m_session; }; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxMPD.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxMPD.cpp deleted file mode 100644 index 0adb01cec7..0000000000 --- a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxMPD.cpp +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (C) 2016 Christian Browet - * Copyright (C) 2016-2016 peak3d - * http://xbmc.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBMC; see the file COPYING. If not, see - * . - * - */ - -#include "DVDDemuxMPD.h" - -#include "DVDDemuxPacket.h" -#include "DVDDemuxUtils.h" -#include "DVDInputStreams/DVDInputStream.h" - -#include "dash/DASHByteStream.h" - -#ifdef TARGET_ANDROID -#include "android/jni/SystemProperties.h" -#endif -#ifdef TARGET_WINDOWS -#pragma comment(lib, "libexpat.lib") -#pragma comment(lib, "ap4.lib") -#endif - -#include "utils/StringUtils.h" -#include "utils/log.h" - -CDVDDemuxMPD::CDVDDemuxMPD() - : CDVDDemux() -{ - CLog::Log(LOGDEBUG, "CDVDDemuxMPD::%s", __FUNCTION__); -} - -CDVDDemuxMPD::~CDVDDemuxMPD() -{ - CLog::Log(LOGDEBUG, "CDVDDemuxMPD::%s", __FUNCTION__); -} - -bool CDVDDemuxMPD::Open(CDVDInputStream* pInput, uint32_t maxWidth, uint32_t maxHeight) -{ - CLog::Log(LOGINFO, "CDVDInputStreamMPD - matching against %d x %d", maxWidth, maxHeight); - m_MPDsession.reset(new CDASHSession(pInput->GetFileName(), maxWidth, maxHeight, "", "", "special://profile/")); - - if (!m_MPDsession->initialize()) - { - m_MPDsession = nullptr; - return false; - } - return true; -} - -void CDVDDemuxMPD::Dispose() -{ -} - -void CDVDDemuxMPD::Reset() -{ -} - -void CDVDDemuxMPD::Abort() -{ -} - -void CDVDDemuxMPD::Flush() -{ -} - -DemuxPacket*CDVDDemuxMPD::Read() -{ - if (!m_MPDsession) - return NULL; - - CDASHFragmentedSampleReader *sr(m_MPDsession->GetNextSample()); - - if (m_MPDsession->CheckChange()) - { - DemuxPacket *p = CDVDDemuxUtils::AllocateDemuxPacket(0); - p->iStreamId = DMX_SPECIALID_STREAMCHANGE; - CLog::Log(LOGDEBUG, "DMX_SPECIALID_STREAMCHANGE"); - return p; - } - - if (sr) - { - DemuxPacket *p = CDVDDemuxUtils::AllocateDemuxPacket(sr->GetSampleDataSize()); - p->dts = sr->DTS() * 1000000; - p->pts = sr->PTS() * 1000000; - p->duration = sr->GetDuration() * 1000000; - p->iStreamId = sr->GetStreamId(); - p->iGroupId = 0; - p->iSize = sr->GetSampleDataSize(); - memcpy(p->pData, sr->GetSampleData(), p->iSize); - - //CLog::Log(LOGDEBUG, "DTS: %0.4f, PTS:%0.4f, ID: %u SZ: %d", p->dts, p->pts, p->iStreamId, p->iSize); - - sr->ReadSample(); - return p; - } - return NULL; -} - -bool CDVDDemuxMPD::SeekTime(int time, bool backwards, double* startpts) -{ - if (!m_MPDsession) - return false; - - return m_MPDsession->SeekTime(static_cast(time)*0.001f, 0, !backwards); -} - -void CDVDDemuxMPD::SetSpeed(int speed) -{ -} - -int CDVDDemuxMPD::GetNrOfStreams() -{ - int n = 0; - if (m_MPDsession) - n = m_MPDsession->GetStreamCount(); - - return n; -} - -CDemuxStream* CDVDDemuxMPD::GetStream(int streamid) -{ - CDASHSession::STREAM *stream(m_MPDsession->GetStream(streamid)); - if (!stream) - { - CLog::Log(LOGERROR, "CDVDDemuxMPD::GetStream(%d): error getting stream", streamid); - return nullptr; - } - - return stream->dmuxstrm; -} - -void CDVDDemuxMPD::EnableStream(int streamid, bool enable) -{ - CLog::Log(LOGDEBUG, "EnableStream(%d: %s)", streamid, enable?"true":"false"); - - if (!m_MPDsession) - return; - - CDASHSession::STREAM *stream(m_MPDsession->GetStream(streamid)); - if (!stream) - return; - - if (enable) - { - if (stream->enabled) - return; - - stream->enabled = true; - - stream->stream_.start_stream(~0, m_MPDsession->GetWidth(), m_MPDsession->GetHeight()); - const dash::DASHTree::Representation *rep(stream->stream_.getRepresentation()); - CLog::Log(LOGDEBUG, "Selecting stream with conditions: w: %u, h: %u, bw: %u", - stream->stream_.getWidth(), stream->stream_.getHeight(), stream->stream_.getBandwidth()); - - if (!stream->stream_.select_stream(true, false, stream->dmuxstrm->iPhysicalId >> 16)) - { - CLog::Log(LOGERROR, "Unable to select stream!"); - return stream->disable(); - } - - if(rep != stream->stream_.getRepresentation()) - { - m_MPDsession->UpdateStream(*stream); - m_MPDsession->CheckChange(true); - } - - stream->input_ = new CDASHByteStream(&stream->stream_); - stream->input_file_ = new AP4_File(*stream->input_, AP4_DefaultAtomFactory::Instance_, true); - AP4_Movie* movie = stream->input_file_->GetMovie(); - if (movie == NULL) - { - CLog::Log(LOGERROR, "No MOOV in stream!"); - return stream->disable(); - } - - static const AP4_Track::Type TIDC[dash::DASHTree::STREAM_TYPE_COUNT] = - { AP4_Track::TYPE_UNKNOWN, AP4_Track::TYPE_VIDEO, AP4_Track::TYPE_AUDIO, AP4_Track::TYPE_TEXT }; - - AP4_Track *track = movie->GetTrack(TIDC[stream->stream_.get_type()]); - if (!track) - { - CLog::Log(LOGERROR, "No suitable track found in stream"); - return stream->disable(); - } - - stream->reader_ = new CDASHFragmentedSampleReader(stream->input_, movie, track, streamid, m_MPDsession->GetSingleSampleDecryptor(), m_MPDsession->GetPresentationTimeOffset()); - stream->reader_->SetObserver(dynamic_cast(m_MPDsession.get())); - - if (!stream->dmuxstrm->ExtraSize) - { - // ExtraData is now available...... - stream->dmuxstrm->ExtraSize = stream->reader_->GetExtraDataSize(); - - // Set the session Changed to force new GetStreamInfo call from kodi -> addon - if (stream->dmuxstrm->ExtraSize) - { - stream->dmuxstrm->ExtraData = (uint8_t*)malloc(stream->dmuxstrm->ExtraSize); - memcpy((void*)stream->dmuxstrm->ExtraData, stream->reader_->GetExtraData(), stream->dmuxstrm->ExtraSize); - m_MPDsession->CheckChange(true); - } - } - return; - } - CLog::Log(LOGDEBUG, ">>>> ERROR"); - return stream->disable(); -} - -int CDVDDemuxMPD::GetStreamLength() -{ - if (!m_MPDsession) - return 0; - - return static_cast(m_MPDsession->GetTotalTime()*1000); -} - -std::string CDVDDemuxMPD::GetFileName() -{ - if (!m_MPDsession) - return ""; - - return m_MPDsession->GetMpdUrl(); -} - -void CDVDDemuxMPD::GetStreamCodecName(int iStreamId, std::string& strName) -{ - strName = ""; - - CDASHSession::STREAM *stream(m_MPDsession->GetStream(iStreamId)); - if (stream) - strName = stream->codecName; -} diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.cpp index 40524f5213..cb4fb4f361 100644 --- a/xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.cpp +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.cpp @@ -30,7 +30,7 @@ #include "DVDDemuxBXA.h" #include "DVDDemuxCDDA.h" #include "DVDDemuxPVRClient.h" -#include "DVDDemuxMPD.h" +#include "DVDDemuxAdaptive.h" #include "pvr/PVRManager.h" #include "pvr/addons/PVRClients.h" @@ -47,10 +47,12 @@ CDVDDemux* CDVDFactoryDemuxer::CreateDemuxer(CDVDInputStream* pInputStream, bool if (!pInputStream) return NULL; - // Try to open the MPD demuxer - if (pInputStream->GetFileItem().GetMimeType() == "video/vnd.mpeg.dash.mpd" || pInputStream->GetFileItem().IsType(".mpd")) + // Try to open the Adaptive demuxer + if (pInputStream->GetFileItem().GetMimeType() == "video/vnd.mpeg.dash.mpd" || pInputStream->GetFileItem().IsType(".mpd") //MPD + || pInputStream->GetFileItem().GetMimeType() == "application/vnd.ms-sstr+xml" || pInputStream->GetFileItem().IsType(".ismc") + ) { - std::unique_ptr demuxer(new CDVDDemuxMPD()); + std::unique_ptr demuxer(new CDVDDemuxAdaptive()); #ifdef TARGET_ANDROID CPointInt maxres = CXBMCApp::GetMaxDisplayResolution(); #else diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/Makefile.in b/xbmc/cores/dvdplayer/DVDDemuxers/Makefile.in index 8cdf860d89..4d8a3014e3 100644 --- a/xbmc/cores/dvdplayer/DVDDemuxers/Makefile.in +++ b/xbmc/cores/dvdplayer/DVDDemuxers/Makefile.in @@ -11,17 +11,19 @@ SRCS += DVDDemuxVobsub.cpp SRCS += DVDDemuxCC.cpp SRCS += DVDFactoryDemuxer.cpp -# MPD +# Adaptive SRCS += \ - DVDDemuxMPD.cpp \ - dash/DASHStream.cpp \ - dash/DASHTree.cpp \ - dash/DASHCodecHandler.cpp \ - dash/DASHByteStream.cpp \ - dash/DASHFragmentedSampleReader.cpp \ - dash/DASHSession.cpp \ - dash/helpers.cpp \ - dash/oscompat.cpp + DVDDemuxAdaptive.cpp \ + adaptive/DASHStream.cpp \ + adaptive/DASHCodecHandler.cpp \ + adaptive/DASHByteStream.cpp \ + adaptive/DASHFragmentedSampleReader.cpp \ + adaptive/DASHSession.cpp \ + adaptive/helpers.cpp \ + adaptive/oscompat.cpp \ + adaptive/common/AdaptiveTree.cpp \ + adaptive/parsers/DASHTree.cpp \ + adaptive/parsers/SmoothTree.cpp LIB = DVDDemuxers.a diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHByteStream.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHByteStream.cpp similarity index 100% rename from xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHByteStream.cpp rename to xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHByteStream.cpp diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHByteStream.h b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHByteStream.h similarity index 94% rename from xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHByteStream.h rename to xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHByteStream.h index 2845a9782a..0391b176ca 100644 --- a/xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHByteStream.h +++ b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHByteStream.h @@ -29,7 +29,7 @@ class CDASHByteStream : public AP4_ByteStream { public: // Constructor - CDASHByteStream(dash::DASHStream *dashStream) :dash_stream_(dashStream) {} + CDASHByteStream(dash::DASHStream *dashStream = nullptr) :dash_stream_(dashStream) {} // AP4_ByteStream methods AP4_Result ReadPartial(void* buffer, AP4_Size bytesToRead, AP4_Size& bytesRead) override; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHCodecHandler.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHCodecHandler.cpp similarity index 100% rename from xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHCodecHandler.cpp rename to xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHCodecHandler.cpp diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHCodecHandler.h b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHCodecHandler.h similarity index 98% rename from xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHCodecHandler.h rename to xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHCodecHandler.h index 0ca1356bd3..88a8b62d10 100644 --- a/xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHCodecHandler.h +++ b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHCodecHandler.h @@ -34,6 +34,8 @@ class CDASHCodecHandler , pictureId(0) , pictureIdPrev(0) {} + virtual ~CDASHCodecHandler() {} + virtual void UpdatePPSId(AP4_DataBuffer const&) {} virtual bool GetVideoInformation(int &width, int &height) { return false; } virtual bool GetAudioInformation(int &channels) { return false; } diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHFragmentObserver.h b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHFragmentObserver.h similarity index 100% rename from xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHFragmentObserver.h rename to xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHFragmentObserver.h diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHFragmentedSampleReader.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHFragmentedSampleReader.cpp similarity index 65% rename from xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHFragmentedSampleReader.cpp rename to xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHFragmentedSampleReader.cpp index 9b36f825c0..512cfaa933 100644 --- a/xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHFragmentedSampleReader.cpp +++ b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHFragmentedSampleReader.cpp @@ -30,6 +30,7 @@ CDASHFragmentedSampleReader::CDASHFragmentedSampleReader(AP4_ByteStream* input, , m_eos(false) , m_started(false) , m_StreamId(streamId) + , m_SampleDescIndex(1) , m_SingleSampleDecryptor(ssd) , m_Decrypter(0) , m_Protected_desc(0) @@ -37,6 +38,7 @@ CDASHFragmentedSampleReader::CDASHFragmentedSampleReader(AP4_ByteStream* input, , m_Observer(0) , m_DefaultKey(0) , m_presentationTimeOffset(pto) + , m_bSampleDescChanged(false) { EnableTrack(m_Track->GetId()); @@ -46,25 +48,7 @@ CDASHFragmentedSampleReader::CDASHFragmentedSampleReader(AP4_ByteStream* input, m_Protected_desc = static_cast(desc); desc = m_Protected_desc->GetOriginalSampleDescription(); } - switch (desc->GetFormat()) - { - case AP4_SAMPLE_FORMAT_AVC1: - case AP4_SAMPLE_FORMAT_AVC2: - case AP4_SAMPLE_FORMAT_AVC3: - case AP4_SAMPLE_FORMAT_AVC4: - m_codecHandler = new CAVCDASHCodecHandler(desc); - break; - case AP4_SAMPLE_FORMAT_HEV1: - case AP4_SAMPLE_FORMAT_HVC1: - m_codecHandler = new CHEVCDASHCodecHandler(desc); - break; - case AP4_SAMPLE_FORMAT_MP4A: - m_codecHandler = new CMPEGDASHCodecHandler(desc); - break; - default: - m_codecHandler = new CDASHCodecHandler(desc); - break; - } + UpdateSampleDescription(); } CDASHFragmentedSampleReader::~CDASHFragmentedSampleReader() @@ -126,7 +110,7 @@ AP4_Result CDASHFragmentedSampleReader::SeekSample(AP4_UI32 track_id, AP4_UI64 t return result; sample_index = 0; } - return SetSampleIndex(track_id, sample_index); + return SetSampleIndex(tracker->m_Track->GetId(), sample_index); } AP4_Result CDASHFragmentedSampleReader::ReadSample() @@ -203,6 +187,15 @@ bool CDASHFragmentedSampleReader::TimeSeek(double pts, bool preceeding) return false; } +uint64_t CDASHFragmentedSampleReader::GetFragmentDuration() +{ + Tracker* trk = FindTracker(m_Track->GetId()); + if (!trk->m_SampleTable) + return 0; + + return dynamic_cast(trk->m_SampleTable)->GetDuration(); +} + AP4_Result CDASHFragmentedSampleReader::ProcessMoof(AP4_ContainerAtom* moof, AP4_Position moof_offset, AP4_Position mdat_payload_offset) { AP4_Result result; @@ -210,38 +203,128 @@ AP4_Result CDASHFragmentedSampleReader::ProcessMoof(AP4_ContainerAtom* moof, AP4 if (m_Observer) m_Observer->BeginFragment(m_StreamId); - if (AP4_SUCCEEDED((result = AP4_LinearReader::ProcessMoof(moof, moof_offset, mdat_payload_offset))) && m_Protected_desc) + // create a new fragment + delete m_Fragment; + m_Fragment = new AP4_MovieFragment(moof); + + // update the trackers + AP4_Array ids; + m_Fragment->GetTrackIds(ids); + for (unsigned int i=0; im_SampleTableIsOwned) { + delete tracker->m_SampleTable; + } + tracker->m_SampleTable = NULL; + tracker->m_NextSampleIndex = 0; + for (unsigned int j=0; jm_Track->GetId()) { + AP4_FragmentSampleTable* sample_table = NULL; + result = m_Fragment->CreateSampleTable(&m_Movie, + ids[j], + m_FragmentStream, + moof_offset, + mdat_payload_offset, + tracker->m_NextDts, + sample_table); + if (AP4_FAILED(result)) break; + tracker->m_SampleTable = sample_table; + tracker->m_SampleTableIsOwned = true; + tracker->m_Eos = false; + break; + } + } + } + + if (AP4_SUCCEEDED(result)) { - //Setup the decryption - AP4_CencSampleInfoTable *sample_table; - AP4_UI32 algorithm_id = 0; - - delete m_Decrypter; - m_Decrypter = 0; + //Check if the sample table description has changed AP4_ContainerAtom *traf = AP4_DYNAMIC_CAST(AP4_ContainerAtom, moof->GetChild(AP4_ATOM_TYPE_TRAF, 0)); + AP4_TfhdAtom *tfhd = AP4_DYNAMIC_CAST(AP4_TfhdAtom, traf->GetChild(AP4_ATOM_TYPE_TFHD, 0)); + if (tfhd && tfhd->GetSampleDescriptionIndex() != m_SampleDescIndex) + { + m_SampleDescIndex = tfhd->GetSampleDescriptionIndex(); + UpdateSampleDescription(); + } + else if (m_SampleDescIndex != 1) + { + m_SampleDescIndex = 1; + UpdateSampleDescription(); + } - if (!m_Protected_desc || !traf) - return AP4_ERROR_INVALID_FORMAT; + if (m_Protected_desc) + { + //Setup the decryption + AP4_CencSampleInfoTable *sample_table; + AP4_UI32 algorithm_id = 0; - if (AP4_FAILED(result = AP4_CencSampleInfoTable::Create(m_Protected_desc, traf, algorithm_id, *m_FragmentStream, moof_offset, sample_table))) - return result; + delete m_Decrypter; + m_Decrypter = 0; - AP4_ContainerAtom *schi; - m_DefaultKey = 0; - if (m_Protected_desc->GetSchemeInfo() && (schi = m_Protected_desc->GetSchemeInfo()->GetSchiAtom())) - { - AP4_TencAtom* tenc(AP4_DYNAMIC_CAST(AP4_TencAtom, schi->GetChild(AP4_ATOM_TYPE_TENC, 0))); - if (tenc) - m_DefaultKey = tenc->GetDefaultKid(); - } + AP4_ContainerAtom *traf = AP4_DYNAMIC_CAST(AP4_ContainerAtom, moof->GetChild(AP4_ATOM_TYPE_TRAF, 0)); + + if (!m_Protected_desc || !traf) + return AP4_ERROR_INVALID_FORMAT; + + if (AP4_FAILED(result = AP4_CencSampleInfoTable::Create(m_Protected_desc, traf, algorithm_id, *m_FragmentStream, moof_offset, sample_table))) + return result; + + AP4_ContainerAtom *schi; + m_DefaultKey = 0; + if (m_Protected_desc->GetSchemeInfo() && (schi = m_Protected_desc->GetSchemeInfo()->GetSchiAtom())) + { + AP4_TencAtom* tenc(AP4_DYNAMIC_CAST(AP4_TencAtom, schi->GetChild(AP4_ATOM_TYPE_TENC, 0))); + if (tenc) + m_DefaultKey = tenc->GetDefaultKid(); + } // if (AP4_FAILED(result = AP4_CencSampleDecrypter::Create(sample_table, algorithm_id, 0, 0, 0, m_SingleSampleDecryptor, m_Decrypter))) // return result; + } } + if (m_Observer) m_Observer->EndFragment(m_StreamId); return result; } +void CDASHFragmentedSampleReader::UpdateSampleDescription() +{ + if (m_codecHandler) + delete m_codecHandler; + m_codecHandler = 0; + m_bSampleDescChanged = true; + + AP4_SampleDescription *desc(m_Track->GetSampleDescription(m_SampleDescIndex - 1)); + if (!desc) + return; + + if (desc->GetType() == AP4_SampleDescription::TYPE_PROTECTED) + { + m_Protected_desc = static_cast(desc); + desc = m_Protected_desc->GetOriginalSampleDescription(); + } + switch (desc->GetFormat()) + { + case AP4_SAMPLE_FORMAT_AVC1: + case AP4_SAMPLE_FORMAT_AVC2: + case AP4_SAMPLE_FORMAT_AVC3: + case AP4_SAMPLE_FORMAT_AVC4: + m_codecHandler = new CAVCDASHCodecHandler(desc); + break; + case AP4_SAMPLE_FORMAT_HEV1: + case AP4_SAMPLE_FORMAT_HVC1: + m_codecHandler = new CHEVCDASHCodecHandler(desc); + break; + case AP4_SAMPLE_FORMAT_MP4A: + m_codecHandler = new CMPEGDASHCodecHandler(desc); + break; + default: + m_codecHandler = new CDASHCodecHandler(desc); + break; + } +} + diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHFragmentedSampleReader.h b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHFragmentedSampleReader.h similarity index 90% rename from xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHFragmentedSampleReader.h rename to xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHFragmentedSampleReader.h index e530b98cfd..db8fb4d0f8 100644 --- a/xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHFragmentedSampleReader.h +++ b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHFragmentedSampleReader.h @@ -51,24 +51,29 @@ class CDASHFragmentedSampleReader : public AP4_LinearReader const AP4_Byte *GetSampleData() const { return m_sample_data_.GetData(); } double GetDuration() const { return (double)m_sample_.GetDuration() / (double)m_Track->GetMediaTimeScale(); } const AP4_UI08 *GetExtraData() { return m_codecHandler->extra_data; } - AP4_Size GetExtraDataSize() { return m_codecHandler->extra_data_size; } + AP4_Size GetExtraDataSize() { return m_codecHandler ? m_codecHandler->extra_data_size : 0; } bool GetVideoInformation(int &width, int &height) { return m_codecHandler->GetVideoInformation(width, height); } bool GetAudioInformation(int &channelCount) { return m_codecHandler->GetAudioInformation(channelCount); } void SetObserver(IDASHFragmentObserver *observer) { m_Observer = observer; } void SetPTSOffset(uint64_t offset) { FindTracker(m_Track->GetId())->m_NextDts = offset; } - uint64_t GetFragmentDuration() { return dynamic_cast(FindTracker(m_Track->GetId())->m_SampleTable)->GetDuration(); } - + uint64_t GetFragmentDuration(); + uint32_t GetTimeScale() { return m_Track->GetMediaTimeScale(); } + protected: virtual AP4_Result ProcessMoof(AP4_ContainerAtom* moof, AP4_Position moof_offset, AP4_Position mdat_payload_offset); + virtual void UpdateSampleDescription(); private: AP4_Track *m_Track; AP4_UI32 m_StreamId; + AP4_UI32 m_SampleDescIndex; + bool m_eos, m_started; double m_dts, m_pts; double m_presentationTimeOffset; + bool m_bSampleDescChanged; AP4_Sample m_sample_; AP4_DataBuffer m_encrypted, m_sample_data_; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHSession.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHSession.cpp similarity index 75% rename from xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHSession.cpp rename to xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHSession.cpp index f8a58248ff..b7792c53bf 100644 --- a/xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHSession.cpp +++ b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHSession.cpp @@ -23,18 +23,23 @@ #include "DVDDemuxers/DVDDemux.h" +#include "parsers/DASHTree.h" +#include "parsers/SmoothTree.h" + #include "system.h" #include "utils/log.h" #include "utils/URIUtils.h" #include "utils/StringUtils.h" #include "filesystem/File.h" -CDASHSession::CDASHSession(const std::string& strURL, int width, int height, const char *strLicType, const char* strLicKey, const char* profile_path) +CDASHSession::CDASHSession(const CDASHSession::MANIFEST_TYPE manifest_type, const std::string& strURL, int width, int height, const char *strLicType, const char* strLicKey, const char* profile_path) :single_sample_decryptor_(0) - , mpdFileURL_(strURL) + , manifest_type_(manifest_type) + , fileURL_(strURL) , license_type_(strLicType) , license_key_(strLicKey) , profile_path_(profile_path) + , adaptiveTree_(nullptr) , width_(width) , height_(height) , last_pts_(0) @@ -43,6 +48,17 @@ CDASHSession::CDASHSession(const std::string& strURL, int width, int height, con , changed_(false) , manual_streams_(false) { + switch (manifest_type) + { + case MANIFEST_TYPE_MPD: + adaptiveTree_ = new adaptive::DASHTree; + break; + case MANIFEST_TYPE_ISM: + adaptiveTree_ = new adaptive::SmoothTree; + break; + default:; + }; + XFILE::CFile f; std::string fn = URIUtils::AddFileToFolder(profile_path_, "bandwidth.bin"); @@ -50,13 +66,13 @@ CDASHSession::CDASHSession(const std::string& strURL, int width, int height, con { double val; f.Read((void*)&val, sizeof(double)); - dashtree_.bandwidth_ = static_cast(val); + adaptiveTree_->bandwidth_ = static_cast(val); f.Close(); } else - dashtree_.bandwidth_ = 4000000; - dashtree_.set_download_speed(dashtree_.bandwidth_); - CLog::Log(LOGDEBUG, "CDASHSession - Initial bandwidth: %u ", dashtree_.bandwidth_); + adaptiveTree_->bandwidth_ = 4000000; + adaptiveTree_->set_download_speed(adaptiveTree_->bandwidth_); + CLog::Log(LOGDEBUG, "CDASHSession - Initial bandwidth: %u ", adaptiveTree_->bandwidth_); manual_streams_ = false; } @@ -79,7 +95,7 @@ CDASHSession::~CDASHSession() std::string fn = URIUtils::AddFileToFolder(profile_path_, "bandwidth.bin"); if (f.OpenForWrite(fn, READ_NO_CACHE)) { - double val(dashtree_.get_average_download_speed()); + double val(adaptiveTree_->get_average_download_speed()); f.Write((const void*)&val, sizeof(double)); f.Close(); } @@ -87,7 +103,7 @@ CDASHSession::~CDASHSession() CLog::Log(LOGERROR, "CDASHSession - Cannot write bandwidth.bin"); } -CDASHSession::STREAM::STREAM(dash::DASHTree& t, dash::DASHTree::StreamType s) +CDASHSession::STREAM::STREAM(adaptive::AdaptiveTree& t, adaptive::AdaptiveTree::StreamType s) : stream_(t, s) , enabled(false) , current_segment_(0) @@ -184,27 +200,30 @@ bool CDASHSession::initialize() // Get URN's wich are supported by this addon // if (!license_type_.empty()) // { -// GetSupportedDecrypterURN(dashtree_.adp_pssh_); -// CLog::Log(LOGDEBUG, "Supported URN: %s", dashtree_.adp_pssh_.first.c_str()); +// GetSupportedDecrypterURN(dashtree_->adp_pssh_); +// CLog::Log(LOGDEBUG, "Supported URN: %s", dashtree_->adp_pssh_.first.c_str()); // } // Open mpd file - const char* delim(strrchr(mpdFileURL_.c_str(), '/')); - if (!delim) + size_t paramPos = fileURL_.find('?'); + adaptiveTree_->base_url_ = (paramPos == std::string::npos) ? fileURL_ : fileURL_.substr(0, paramPos); + + paramPos = adaptiveTree_->base_url_.find_last_of('/', adaptiveTree_->base_url_.length()); + if (paramPos == std::string::npos) { - CLog::Log(LOGERROR, "Invalid mpdURL: / expected (%s)", mpdFileURL_.c_str()); + CLog::Log(LOGERROR, "Invalid adaptive URL: / expected (%s)", fileURL_.c_str()); return false; } - dashtree_.base_url_ = std::string(mpdFileURL_.c_str(), (delim - mpdFileURL_.c_str()) + 1); + adaptiveTree_->base_url_.resize(paramPos + 1); - if (!dashtree_.open(mpdFileURL_.c_str()) || dashtree_.empty()) + if (!adaptiveTree_->open(fileURL_.c_str()) || adaptiveTree_->empty()) { - CLog::Log(LOGERROR, "Could not open / parse mpdURL (%s)", mpdFileURL_.c_str()); + CLog::Log(LOGERROR, "Could not open / parse adaptive URL (%s)", fileURL_.c_str()); return false; } - CLog::Log(LOGINFO, "Successfully parsed .mpd file. #Streams: %d Download speed: %0.4f Bytes/s", dashtree_.periods_[0]->adaptationSets_.size(), dashtree_.download_speed_); + CLog::Log(LOGINFO, "Successfully parsed adaptive manifest. #Streams: %d Download speed: %0.4f Bytes/s", adaptiveTree_->periods_[0]->adaptationSets_.size(), adaptiveTree_->download_speed_); - if (dashtree_.encryptionState_ == dash::DASHTree::ENCRYTIONSTATE_ENCRYPTED) + if (adaptiveTree_->encryptionState_ == adaptive::AdaptiveTree::ENCRYTIONSTATE_ENCRYPTED) { CLog::Log(LOGERROR, "Unable to handle decryption. Unsupported!"); return false; @@ -220,30 +239,30 @@ bool CDASHSession::initialize() */ // create CDASHSession::STREAM objects. One for each AdaptationSet - const dash::DASHTree::AdaptationSet *adp; + const adaptive::AdaptiveTree::AdaptationSet *adp; for (std::vector::iterator b(streams_.begin()), e(streams_.end()); b != e; ++b) SAFE_DELETE(*b); streams_.clear(); - for (unsigned int i=0; adp = dashtree_.GetAdaptationSet(i); ++i) + for (unsigned int i=0; (adp = adaptiveTree_->GetAdaptationSet(i)); ++i) { size_t repId = manual_streams_ ? adp->repesentations_.size() : 0; do { - streams_.push_back(new STREAM(dashtree_, adp->type_)); + streams_.push_back(new STREAM(*adaptiveTree_, adp->type_)); STREAM &stream(*streams_.back()); stream.stream_.prepare_stream(adp, width_, height_, min_bandwidth, max_bandwidth, repId); switch (adp->type_) { - case dash::DASHTree::VIDEO: + case adaptive::AdaptiveTree::VIDEO: stream.dmuxstrm = new CDemuxStreamVideo(); break; - case dash::DASHTree::AUDIO: + case adaptive::AdaptiveTree::AUDIO: stream.dmuxstrm = new CDemuxStreamAudio(); break; - case dash::DASHTree::TEXT: + case adaptive::AdaptiveTree::TEXT: stream.dmuxstrm = new CDemuxStreamTeletext(); break; default: @@ -261,19 +280,19 @@ bool CDASHSession::initialize() } // // Try to initialize an SingleSampleDecryptor -// if (dashtree_.encryptionState_) +// if (dashtree_->encryptionState_) // { // AP4_DataBuffer init_data; -// if (dashtree_.adp_pssh_.second == "FILE") +// if (dashtree_->adp_pssh_.second == "FILE") // { -// std::string strkey(dashtree_.adp_pssh_.first.substr(9)); +// std::string strkey(dashtree_->adp_pssh_.first.substr(9)); // size_t pos; // while ((pos = strkey.find('-')) != std::string::npos) // strkey.erase(pos, 1); // if (strkey.size() != 32) // { -// CLog::Log(LOGERROR, "Key system mismatch (%s)!", dashtree_.adp_pssh_.first.c_str()); +// CLog::Log(LOGERROR, "Key system mismatch (%s)!", dashtree_->adp_pssh_.first.c_str()); // return false; // } @@ -313,10 +332,17 @@ bool CDASHSession::initialize() // } // else // { -// init_data.SetBufferSize(1024); -// unsigned int init_data_size(1024); -// b64_decode(dashtree_.pssh_.second.data(), dashtree_.pssh_.second.size(), init_data.UseData(), init_data_size); -// init_data.SetDataSize(init_data_size); +// if (manifest_type_ == MANIFEST_TYPE_ISM) +// { +// create_ism_license(adaptiveTree_->defaultKID_, license_data_, init_data); +// } +// else +// { +// init_data.SetBufferSize(1024); +// unsigned int init_data_size(1024); +// b64_decode(dashtree_->pssh_.second.data(), dashtree_->pssh_.second.size(), init_data.UseData(), init_data_size); +// init_data.SetDataSize(init_data_size); +// } // } // return (single_sample_decryptor_ = CreateSingleSampleDecrypter(init_data))!=0; // } @@ -325,7 +351,7 @@ bool CDASHSession::initialize() void CDASHSession::UpdateStream(STREAM &stream) { - const dash::DASHTree::Representation *rep(stream.stream_.getRepresentation()); + const adaptive::AdaptiveTree::Representation *rep(stream.stream_.getRepresentation()); // we currently use only the first track! std::string::size_type pos = rep->codecs_.find(","); @@ -334,11 +360,11 @@ void CDASHSession::UpdateStream(STREAM &stream) stream.codecInternalName = rep->codecs_.substr(0, pos); - if (rep->codecs_.find("mp4a") == 0) + if (rep->codecs_.find("mp4a") == 0 || rep->codecs_.find("aacl") == 0) stream.codecName = "aac"; else if (rep->codecs_.find("ec-3") == 0 || rep->codecs_.find("ac-3") == 0) stream.codecName = "eac3"; - else if (rep->codecs_.find("avc") == 0) + else if (rep->codecs_.find("avc") == 0 || rep->codecs_.find("h264") == 0) stream.codecName = "h264"; else if (rep->codecs_.find("hevc") == 0) stream.codecName = "hevc"; @@ -348,6 +374,10 @@ void CDASHSession::UpdateStream(STREAM &stream) stream.codecName = "opus"; else if (rep->codecs_.find("vorbis") == 0) stream.codecName = "vorbis"; + else if (rep->codecs_.find("wvc1") == 0) + stream.codecName = "vc1"; + else if (rep->codecs_.find("wmap") == 0) + stream.codecName = "wmapro"; AVCodec *codec = avcodec_find_decoder_by_name(stream.codecName.c_str()); if (codec) @@ -365,7 +395,7 @@ void CDASHSession::UpdateStream(STREAM &stream) vstrm->fAspect = rep->aspect_; vstrm->iFpsRate = rep->fpsRate_; vstrm->iFpsScale = rep->fpsScale_; - vstrm->sStreamInfo = StringUtils::Format("MPD Video: %s / %d x %d / %d kbps", stream.codecName.c_str(), rep->width_, rep->height_, rep->bandwidth_ / 1024); + vstrm->sStreamInfo = StringUtils::Format("ADP Video: %s / %d x %d / %d kbps", stream.codecName.c_str(), rep->width_, rep->height_, rep->bandwidth_ / 1024); if (!vstrm->ExtraSize && rep->codec_private_data_.size()) { @@ -379,7 +409,7 @@ void CDASHSession::UpdateStream(STREAM &stream) CDemuxStreamAudio* astrm = static_cast(stream.dmuxstrm); astrm->iSampleRate = rep->samplingRate_; astrm->iChannels = rep->channelCount_; - astrm->sStreamInfo = StringUtils::Format("MPD Audio: %s / %d ch / %d Hz / %d kbps", stream.codecName.c_str(), rep->channelCount_, rep->samplingRate_, rep->bandwidth_ / 1024); + astrm->sStreamInfo = StringUtils::Format("ADP Audio: %s / %d ch / %d Hz / %d kbps", stream.codecName.c_str(), rep->channelCount_, rep->samplingRate_, rep->bandwidth_ / 1024); } } @@ -448,6 +478,14 @@ bool CDASHSession::SeekTime(double seekTime, unsigned int streamId, bool preceed return ret; } +const AP4_UI08*CDASHSession::GetDefaultKeyId() const +{ + static const AP4_UI08 default_key[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; + if (adaptiveTree_->defaultKID_.size() == 16) + return reinterpret_cast(adaptiveTree_->defaultKID_.data()); + return default_key; +} + void CDASHSession::BeginFragment(AP4_UI32 streamId) { STREAM *s(streams_[streamId]); @@ -457,7 +495,13 @@ void CDASHSession::BeginFragment(AP4_UI32 streamId) void CDASHSession::EndFragment(AP4_UI32 streamId) { STREAM *s(streams_[streamId]); - dashtree_.SetFragmentDuration(s->stream_.getAdaptationSet(), s->stream_.getRepresentation(), s->stream_.getSegmentPos(), s->reader_->GetFragmentDuration()); + adaptiveTree_->SetFragmentDuration( + s->stream_.getAdaptationSet(), + s->stream_.getRepresentation(), + s->stream_.getSegmentPos(), + s->reader_->GetFragmentDuration(), + s->reader_->GetTimeScale() + ); } diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHSession.h b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHSession.h similarity index 74% rename from xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHSession.h rename to xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHSession.h index abcaf3c51d..8a22fca6de 100644 --- a/xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHSession.h +++ b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHSession.h @@ -27,22 +27,17 @@ #include "DASHFragmentObserver.h" #include "DASHFragmentedSampleReader.h" #include "DASHStream.h" -#include "DASHTree.h" +#include "common/AdaptiveTree.h" #include "DVDDemuxers/DVDDemux.h" class CDASHSession: public IDASHFragmentObserver { public: - CDASHSession(const std::string& strURL, int width, int height, const char *strLicType, const char* strLicKey, const char* profile_path); - virtual ~CDASHSession(); - bool initialize(); - CDASHFragmentedSampleReader *GetNextSample(); - class STREAM { public: - STREAM(dash::DASHTree &t, dash::DASHTree::StreamType s); + STREAM(adaptive::AdaptiveTree &t, adaptive::AdaptiveTree::StreamType s); ~STREAM(); void disable(); @@ -58,21 +53,35 @@ class CDASHSession: public IDASHFragmentObserver CDASHFragmentedSampleReader *reader_; }; + enum MANIFEST_TYPE + { + MANIFEST_TYPE_UNKNOWN, + MANIFEST_TYPE_MPD, + MANIFEST_TYPE_ISM + }; + + CDASHSession(const CDASHSession::MANIFEST_TYPE manifest_type, const std::string& strURL, int width, int height, const char *strLicType, const char* strLicKey, const char* profile_path); + virtual ~CDASHSession(); + bool initialize(); + CDASHFragmentedSampleReader *GetNextSample(); + void UpdateStream(STREAM &stream); - std::string GetMpdUrl() { return mpdFileURL_; } + std::string GetUrl() { return fileURL_; } STREAM *GetStream(unsigned int sid) const { return (sid < streams_.size() ? streams_[sid] : 0); } unsigned int GetStreamCount() const { return streams_.size(); } std::uint16_t GetWidth() const { return width_; } std::uint16_t GetHeight() const { return height_; } AP4_CencSingleSampleDecrypter * GetSingleSampleDecryptor() const { return single_sample_decryptor_; } - double GetPresentationTimeOffset() { return dashtree_.minPresentationOffset < DBL_MAX? dashtree_.minPresentationOffset:0; } - double GetTotalTime() const { return dashtree_.overallSeconds_; } + double GetPresentationTimeOffset() { return adaptiveTree_->minPresentationOffset < DBL_MAX? adaptiveTree_->minPresentationOffset:0; } + double GetTotalTime() const { return adaptiveTree_->overallSeconds_; } double GetPTS() const { return last_pts_; } bool CheckChange(bool bSet = false); void SetVideoResolution(unsigned int w, unsigned int h); bool SeekTime(double seekTime, unsigned int streamId = 0, bool preceeding=true); - bool IsLive() const { return dashtree_.live_start_ != 0; } + bool IsLive() const { return adaptiveTree_->has_timeshift_buffer_; } + MANIFEST_TYPE GetManifestType() const { return manifest_type_; } + const AP4_UI08 *GetDefaultKeyId() const; //Observer Section void BeginFragment(AP4_UI32 streamId) override; @@ -83,12 +92,13 @@ class CDASHSession: public IDASHFragmentObserver // AP4_CencSingleSampleDecrypter *CreateSingleSampleDecrypter(AP4_DataBuffer &streamCodec); private: - std::string mpdFileURL_; + MANIFEST_TYPE manifest_type_; + std::string fileURL_; std::string license_key_, license_type_; std::string profile_path_; void * decrypterModule_; - dash::DASHTree dashtree_; + adaptive::AdaptiveTree* adaptiveTree_; std::vector streams_; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHStream.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHStream.cpp similarity index 70% rename from xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHStream.cpp rename to xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHStream.cpp index e4379a7cd4..dd88544f25 100644 --- a/xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHStream.cpp +++ b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHStream.cpp @@ -26,7 +26,7 @@ using namespace dash; using namespace XFILE; -DASHStream::DASHStream(DASHTree &tree, DASHTree::StreamType type) +DASHStream::DASHStream(adaptive::AdaptiveTree &tree, adaptive::AdaptiveTree::StreamType type) :tree_(tree) , type_(type) , observer_(0) @@ -48,19 +48,26 @@ bool DASHStream::download_segment() std::string strURL; char rangebuf[128], *rangeHeader(0); - if ((current_rep_->flags_ & DASHTree::Representation::SEGMENTBASE)) + if (current_rep_->flags_ & adaptive::AdaptiveTree::Representation::STARTTIMETPL) { strURL = current_rep_->url_; - sprintf(rangebuf, "bytes=%" PRIu64 "-%" PRIu64, current_seg_->range_begin_, current_seg_->range_end_); - rangeHeader = rangebuf; + sprintf(rangebuf, "%" PRIu64, tree_.base_time_ + current_seg_->range_end_); + strURL.replace(strURL.find("{start time}"), 12, rangebuf); } - else if ((current_rep_->flags_ & DASHTree::Representation::SEGMENTMEDIA)) + else if ((current_rep_->flags_ & adaptive::AdaptiveTree::Representation::SEGMENTMEDIA)) { strURL = current_rep_->url_ + current_seg_->media_; } - else if ((current_rep_->flags_ & DASHTree::Representation::TEMPLATE)) + else if (!(current_rep_->flags_ & adaptive::AdaptiveTree::Representation::SEGMENTBASE)) { - if (~current_seg_->range_end_) //templated segment + if (!(current_rep_->flags_ & adaptive::AdaptiveTree::Representation::TEMPLATE)) + { + strURL = current_rep_->url_; + sprintf(rangebuf, "bytes=%" PRIu64 "-%" PRIu64, current_seg_->range_begin_, current_seg_->range_end_); + rangeHeader = rangebuf; + absolute_position_ = current_seg_->range_begin_; + } + else if (~current_seg_->range_end_) //templated segment { std::string media = current_rep_->segtpl_.media; std::string::size_type lenReplace(7); @@ -89,11 +96,11 @@ bool DASHStream::download_segment() } else { - sprintf(rangebuf, "/range/%" PRIu64 "-%" PRIu64, current_seg_->range_begin_, current_seg_->range_end_); - strURL = current_rep_->url_ + rangebuf; - absolute_position_ = current_seg_->range_begin_; - } - + strURL = current_rep_->url_; + sprintf(rangebuf, "bytes=%" PRIu64 "-%" PRIu64, current_seg_->range_begin_, current_seg_->range_end_); + rangeHeader = rangebuf; + } + return download(strURL.c_str(), rangeHeader); } @@ -103,12 +110,12 @@ bool DASHStream::write_data(const void *buffer, size_t buffer_size) return true; } -bool DASHStream::prepare_stream(const DASHTree::AdaptationSet *adp, +bool DASHStream::prepare_stream(const adaptive::AdaptiveTree::AdaptationSet *adp, const uint32_t width, const uint32_t height, uint32_t min_bandwidth, uint32_t max_bandwidth, unsigned int repId) { - width_ = type_ == DASHTree::VIDEO ? width : 0; - height_ = type_ == DASHTree::VIDEO ? height : 0; + width_ = type_ == adaptive::AdaptiveTree::VIDEO ? width : 0; + height_ = type_ == adaptive::AdaptiveTree::VIDEO ? height : 0; uint32_t avg_bandwidth = tree_.bandwidth_; @@ -120,7 +127,7 @@ bool DASHStream::prepare_stream(const DASHTree::AdaptationSet *adp, stopped_ = false; - bandwidth_ = static_cast(bandwidth_ *(type_ == DASHTree::VIDEO ? 0.9 : 0.1)); + bandwidth_ = static_cast(bandwidth_ *(type_ == adaptive::AdaptiveTree::VIDEO ? 0.9 : 0.1)); current_adp_ = adp; @@ -129,7 +136,7 @@ bool DASHStream::prepare_stream(const DASHTree::AdaptationSet *adp, bool DASHStream::start_stream(const uint32_t seg_offset, uint16_t width, uint16_t height) { - if (!~seg_offset && tree_.live_start_ && current_rep_->segments_.data.size()>1) + if (!~seg_offset && tree_.available_time_ && current_rep_->segments_.data.size()>1) { //go at least 12 secs back std::int32_t pos(static_cast(current_rep_->segments_.data.size() - 1)); @@ -147,8 +154,8 @@ bool DASHStream::start_stream(const uint32_t seg_offset, uint16_t width, uint16_ } else { - width_ = type_ == DASHTree::VIDEO ? width : 0; - height_ = type_ == DASHTree::VIDEO ? height : 0; + width_ = type_ == adaptive::AdaptiveTree::VIDEO ? width : 0; + height_ = type_ == adaptive::AdaptiveTree::VIDEO ? height : 0; absolute_position_ = current_rep_->get_next_segment(current_seg_)->range_begin_; stopped_ = false; @@ -219,7 +226,7 @@ bool DASHStream::seek_time(double seek_seconds, double current_seconds, bool &ne if (choosen_seg && current_rep_->get_segment(choosen_seg)->startPTS_ > sec_in_ts) --choosen_seg; - const DASHTree::Segment* old_seg(current_seg_); + const adaptive::AdaptiveTree::Segment* old_seg(current_seg_); if ((current_seg_ = current_rep_->get_segment(choosen_seg))) { needReset = true; @@ -308,41 +315,84 @@ bool DASHStream::parseIndexRange() } byteStream->Seek(0); - AP4_Atom *atom(NULL); - if(AP4_FAILED(AP4_DefaultAtomFactory::Instance_.CreateAtomFromStream(*byteStream, atom)) || AP4_DYNAMIC_CAST(AP4_SidxAtom, atom)==0) + adaptive::AdaptiveTree::AdaptationSet *adp(const_cast(getAdaptationSet())); + adaptive::AdaptiveTree::Representation *rep(const_cast(getRepresentation())); + + if (!getRepresentation()->indexRangeMin_) { - CLog::Log(LOGERROR, "Unable to create SIDX from IndexRange bytes"); - byteStream->Release(); - return false; + AP4_File f(*byteStream, AP4_DefaultAtomFactory::Instance_, true); + AP4_Movie* movie = f.GetMovie(); + if (movie == NULL) + { + CLog::Log(LOGERROR, "No MOOV in stream!"); + byteStream->Release(); + return false; + } + rep->flags_ |= adaptive::AdaptiveTree::Representation::INITIALIZATION; + rep->initialization_.range_begin_ = 0; + AP4_Position pos; + byteStream->Tell(pos); + rep->initialization_.range_end_ = pos - 1; } - byteStream->Release(); - AP4_SidxAtom *sidx(AP4_DYNAMIC_CAST(AP4_SidxAtom, atom)); - - dash::DASHTree::AdaptationSet *adp(const_cast(getAdaptationSet())); - dash::DASHTree::Representation *rep(const_cast(getRepresentation())); - rep->timescale_ = sidx->GetTimeScale(); - - const AP4_Array &reps(sidx->GetReferences()); - dash::DASHTree::Segment seg; - seg.range_end_ = rep->indexRangeMax_; + adaptive::AdaptiveTree::Segment seg; seg.startPTS_ = 0; + unsigned int numSIDX(1); - for (unsigned int i(0); i < reps.ItemCount(); ++i) + do { - seg.range_begin_ = seg.range_end_ + 1; - seg.range_end_ = seg.range_begin_ + reps[i].m_ReferencedSize - 1; - rep->segments_.data.push_back(seg); - if (adp->segment_durations_.data.size() < rep->segments_.data.size() - 1) - adp->segment_durations_.data.push_back(reps[i].m_SubsegmentDuration); - seg.startPTS_ += reps[i].m_SubsegmentDuration; - } + AP4_Atom *atom(NULL); + if (AP4_FAILED(AP4_DefaultAtomFactory::Instance_.CreateAtomFromStream(*byteStream, atom))) + { + CLog::Log(LOGERROR, "Unable to create SIDX from IndexRange bytes"); + byteStream->Release(); + return false; + } + + if (atom->GetType() == AP4_ATOM_TYPE_MOOF) + { + delete atom; + break; + } + else if (atom->GetType() != AP4_ATOM_TYPE_SIDX) + { + delete atom; + continue; + } + + AP4_SidxAtom *sidx(AP4_DYNAMIC_CAST(AP4_SidxAtom, atom)); + const AP4_Array &refs(sidx->GetReferences()); + if (refs[0].m_ReferenceType == 1) + { + numSIDX = refs.ItemCount(); + delete atom; + continue; + } + AP4_Position pos; + byteStream->Tell(pos); + seg.range_end_ = pos + getRepresentation()->indexRangeMin_ + sidx->GetFirstOffset() - 1; + rep->timescale_ = sidx->GetTimeScale(); + + for (unsigned int i(0); i < refs.ItemCount(); ++i) + { + seg.range_begin_ = seg.range_end_ + 1; + seg.range_end_ = seg.range_begin_ + refs[i].m_ReferencedSize - 1; + rep->segments_.data.push_back(seg); + if (adp->segment_durations_.data.size() < rep->segments_.data.size() - 1) + adp->segment_durations_.data.push_back(refs[i].m_SubsegmentDuration); + seg.startPTS_ += refs[i].m_SubsegmentDuration; + } + delete atom; + --numSIDX; + } while (numSIDX); + + byteStream->Release(); return true; } bool DASHStream::select_stream(bool force, bool justInit, unsigned int repId) { - const DASHTree::Representation *new_rep(0), *min_rep(0); + const adaptive::AdaptiveTree::Representation *new_rep(0), *min_rep(0); if (force && absolute_position_ == 0) //already selected return true; @@ -351,7 +401,7 @@ bool DASHStream::select_stream(bool force, bool justInit, unsigned int repId) { unsigned int bestScore(~0); - for (std::vector::const_iterator br(current_adp_->repesentations_.begin()), er(current_adp_->repesentations_.end()); br != er; ++br) + for (std::vector::const_iterator br(current_adp_->repesentations_.begin()), er(current_adp_->repesentations_.end()); br != er; ++br) { unsigned int score; if ((*br)->bandwidth_ <= bandwidth_ @@ -390,7 +440,7 @@ bool DASHStream::select_stream(bool force, bool justInit, unsigned int repId) /* If we have indexRangeExact SegmentBase, update SegmentList from SIDX */ if (current_rep_->indexRangeMax_) { - DASHTree::Representation *rep(const_cast(current_rep_)); + adaptive::AdaptiveTree::Representation *rep(const_cast(current_rep_)); if (!parseIndexRange()) return false; rep->indexRangeMin_ = rep->indexRangeMax_ = 0; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHStream.h b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHStream.h similarity index 77% rename from xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHStream.h rename to xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHStream.h index 14408bf4f5..0a539b7724 100644 --- a/xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHStream.h +++ b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/DASHStream.h @@ -11,7 +11,7 @@ #pragma once -#include "DASHTree.h" +#include "common/AdaptiveTree.h" #include namespace dash @@ -27,10 +27,10 @@ namespace dash class DASHStream { public: - DASHStream(DASHTree &tree, DASHTree::StreamType type); + DASHStream(adaptive::AdaptiveTree &tree, adaptive::AdaptiveTree::StreamType type); ~DASHStream(); void set_observer(DASHStreamObserver *observer){ observer_ = observer; }; - bool prepare_stream(const DASHTree::AdaptationSet *adp, + bool prepare_stream(const adaptive::AdaptiveTree::AdaptationSet *adp, const uint32_t width, const uint32_t height, uint32_t min_bandwidth, uint32_t max_bandwidth, unsigned int repId); bool start_stream(const uint32_t seg_offset, uint16_t width, uint16_t height); @@ -49,8 +49,8 @@ namespace dash uint64_t tell(){ read(0, 0); return absolute_position_; }; bool seek(uint64_t const pos); bool seek_time(double seek_seconds, double current_seconds, bool &needReset); - DASHTree::AdaptationSet const *getAdaptationSet() { return current_adp_; }; - DASHTree::Representation const *getRepresentation(){ return current_rep_; }; + adaptive::AdaptiveTree::AdaptationSet const *getAdaptationSet() { return current_adp_; }; + adaptive::AdaptiveTree::Representation const *getRepresentation(){ return current_rep_; }; double get_download_speed() const { return tree_.get_download_speed(); }; void set_download_speed(double speed) { tree_.set_download_speed(speed); }; size_t getSegmentPos() { return current_rep_->segments_.pos(current_seg_); }; @@ -62,14 +62,14 @@ namespace dash private: bool download_segment(); - DASHTree &tree_; - DASHTree::StreamType type_; + adaptive::AdaptiveTree &tree_; + adaptive::AdaptiveTree::StreamType type_; DASHStreamObserver *observer_; // Active configuration - const DASHTree::Period *current_period_; - const DASHTree::AdaptationSet *current_adp_; - const DASHTree::Representation *current_rep_; - const DASHTree::Segment *current_seg_; + const adaptive::AdaptiveTree::Period *current_period_; + const adaptive::AdaptiveTree::AdaptationSet *current_adp_; + const adaptive::AdaptiveTree::Representation *current_rep_; + const adaptive::AdaptiveTree::Segment *current_seg_; //We assume that a single segment can build complete frames std::string segment_buffer_; std::size_t segment_read_pos_; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/common/AdaptiveTree.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/common/AdaptiveTree.cpp new file mode 100644 index 0000000000..e8d91487cb --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/common/AdaptiveTree.cpp @@ -0,0 +1,145 @@ +/* +* Copyright (C) 2016-2016 peak3d +* http://www.peak3d.de +* +* This Program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2, 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 General Public License for more details. +* +* . +* +*/ + +#include "AdaptiveTree.h" +#include + +#include "URL.h" +#include "utils/URIUtils.h" +#include "filesystem/File.h" + +namespace adaptive +{ + void AdaptiveTree::Segment::SetRange(const char *range) + { + const char *delim(strchr(range, '-')); + if (delim) + { + range_begin_ = strtoull(range, 0, 10); + range_end_ = strtoull(delim + 1, 0, 10); + } + else + range_begin_ = range_end_ = 0; + } + + AdaptiveTree::AdaptiveTree() + : current_period_(0) + , parser_(0) + , currentNode_(0) + , segcount_(0) + , overallSeconds_(0.0) + , stream_start_(0) + , available_time_(0) + , publish_time_(0) + , base_time_(0) + , minPresentationOffset(0.0) + , has_timeshift_buffer_(false) + , download_speed_(0.0) + , average_download_speed_(0.0f) + , encryptionState_(ENCRYTIONSTATE_UNENCRYPTED) + { + } + + bool AdaptiveTree::has_type(StreamType t) + { + if (periods_.empty()) + return false; + + for (std::vector::const_iterator b(periods_[0]->adaptationSets_.begin()), e(periods_[0]->adaptationSets_.end()); b != e; ++b) + if ((*b)->type_ == t) + return true; + return false; + } + + uint32_t AdaptiveTree::estimate_segcount(uint32_t duration, uint32_t timescale) + { + double tmp(duration); + duration /= timescale; + return static_cast((overallSeconds_ / duration)*1.01); + } + + void AdaptiveTree::set_download_speed(double speed) + { + download_speed_ = speed; + if (!average_download_speed_) + average_download_speed_ = download_speed_; + else + average_download_speed_ = average_download_speed_*0.9 + download_speed_*0.1; + }; + + void AdaptiveTree::SetFragmentDuration(const AdaptationSet* adp, const Representation* rep, size_t pos, uint32_t fragmentDuration, uint32_t movie_timescale) + { + if (!has_timeshift_buffer_) + return; + + //Get a modifiable adaptationset + AdaptationSet *adpm(static_cast((void*)adp)); + + // Check if its the last frame we watch + if (adp->segment_durations_.data.size()) + { + if (pos == adp->segment_durations_.data.size() - 1) + { + adpm->segment_durations_.insert(static_cast(fragmentDuration)*adp->timescale_ / movie_timescale); + } + else + return; + } + else if (pos != rep->segments_.data.size() - 1) + return; + + fragmentDuration = static_cast(static_cast(fragmentDuration)*rep->timescale_ / movie_timescale); + + Segment seg(*(rep->segments_[pos])); + if(~seg.range_begin_) + seg.range_begin_ += fragmentDuration; + seg.range_end_ += (rep->flags_ & (Representation::STARTTIMETPL | Representation::TIMETEMPLATE)) ? fragmentDuration : 1; + seg.startPTS_ += fragmentDuration; + + for (std::vector::iterator b(adpm->repesentations_.begin()), e(adpm->repesentations_.end()); b != e; ++b) + (*b)->segments_.insert(seg); + } + + bool AdaptiveTree::download(const char* url) + { + // open the file + CURL uUrl(url); + if (URIUtils::IsInternetStream(uUrl)) + { + uUrl.SetProtocolOption("seekable", "0"); + uUrl.SetProtocolOption("acceptencoding", "gzip"); + } + + XFILE::CFile* file = new XFILE::CFile(); + if (!file->Open(uUrl, READ_CHUNKED | READ_NO_CACHE)) + return false; + + // read the file + static const unsigned int CHUNKSIZE = 16384; + char buf[CHUNKSIZE]; + size_t nbRead; + while ((nbRead = file->Read(buf, CHUNKSIZE)) > 0 && ~nbRead && write_data(buf, nbRead)); + + //download_speed_ = xbmc->GetFileDownloadSpeed(file); + + file->Close(); + delete file; + + return nbRead == 0; + } +} diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHTree.h b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/common/AdaptiveTree.h old mode 100644 new mode 100755 similarity index 75% rename from xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHTree.h rename to xbmc/cores/dvdplayer/DVDDemuxers/adaptive/common/AdaptiveTree.h index f08fc76e3e..d5f6be3378 --- a/xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHTree.h +++ b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/common/AdaptiveTree.h @@ -1,13 +1,20 @@ /* -* DASHTree.h -***************************************************************************** -* Copyright (C) 2015, liberty_developer +* Copyright (C) 2016-2016 peak3d +* http://www.peak3d.de * -* Email: liberty.developer@xmail.net +* This Program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2, or (at your option) +* any later version. * -* This source code and its use and distribution, is subject to the terms -* and conditions of the applicable license agreement. -*****************************************************************************/ +* 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 General Public License for more details. +* +* . +* +*/ #pragma once @@ -17,7 +24,7 @@ #include #include "expat.h" -namespace dash +namespace adaptive { template struct SPINCACHE @@ -61,7 +68,7 @@ namespace dash std::vector data; }; - class DASHTree + class AdaptiveTree { public: enum StreamType @@ -95,8 +102,8 @@ namespace dash struct Representation { - Representation() :timescale_(0), duration_(0), bandwidth_(0), samplingRate_(0), width_(0), height_(0), - aspect_(1.0f), fpsRate_(0), fpsScale_(1), channelCount_(0), flags_(0), indexRangeMin_(0), indexRangeMax_(0){}; + Representation() :bandwidth_(0), samplingRate_(0), width_(0), height_(0), fpsRate_(0), fpsScale_(1), aspect_(1.0f), + flags_(0), indexRangeMin_(0), indexRangeMax_(0), channelCount_(0), duration_(0), timescale_(0){}; std::string url_; std::string id; std::string codecs_; @@ -114,11 +121,13 @@ namespace dash static const unsigned int INITIALIZATION = 8; static const unsigned int TIMETEMPLATE = 16; static const unsigned int SEGMENTBASE = 32; - static const unsigned int SEGMENTMEDIA = 64; + static const unsigned int SEGMENTMEDIA = 64; + static const unsigned int STARTTIMETPL = 128; + uint32_t flags_; uint32_t indexRangeMin_, indexRangeMax_; - uint8_t channelCount_; + uint8_t channelCount_, nalLengthSize_; SegmentTemplate segtpl_; //SegmentList uint32_t duration_, timescale_; @@ -146,7 +155,7 @@ namespace dash struct AdaptationSet { - AdaptationSet() :type_(NOTYPE), timescale_(0), startPTS_(0){ language_ = "unk"; }; + AdaptationSet() :type_(NOTYPE), timescale_(0), startPTS_(0), encrypted(false){ language_ = "unk"; }; ~AdaptationSet(){ for (std::vector::const_iterator b(repesentations_.begin()), e(repesentations_.end()); b != e; ++b) delete *b; }; StreamType type_; uint32_t timescale_; @@ -162,6 +171,7 @@ namespace dash return *segment_durations_[pos]; }; SegmentTemplate segtpl_; + bool encrypted; }*current_adaptationset_; struct Period @@ -180,14 +190,16 @@ namespace dash uint32_t currentNode_; uint32_t segcount_; double overallSeconds_; - uint64_t stream_start_, live_start_, publish_time_, base_time_; + uint64_t stream_start_, available_time_, publish_time_, base_time_; double minPresentationOffset; + bool has_timeshift_buffer_; uint32_t bandwidth_; double download_speed_, average_download_speed_; - + std::pair pssh_, adp_pssh_; + std::string defaultKID_; enum { @@ -197,41 +209,26 @@ namespace dash }; unsigned int encryptionState_; uint8_t adpChannelCount_; + uint16_t adpwidth_, adpheight_; + uint32_t adpfpsRate_; - enum - { - MPDNODE_MPD = 1 << 0, - MPDNODE_PERIOD = 1 << 1, - MPDNODE_ADAPTIONSET = 1 << 2, - MPDNODE_CONTENTPROTECTION = 1 << 3, - MPDNODE_REPRESENTATION = 1 << 4, - MPDNODE_BASEURL = 1 << 5, - MPDNODE_SEGMENTLIST = 1 << 6, - MPDNODE_INITIALIZATION = 1 << 7, - MPDNODE_SEGMENTURL = 1 << 8, - MPDNODE_SEGMENTDURATIONS = 1 << 9, - MPDNODE_S = 1 << 11, - MPDNODE_PSSH = 1 << 12, - MPDNODE_SEGMENTTEMPLATE = 1 << 13, - MPDNODE_SEGMENTTIMELINE = 1 << 14 - }; std::string strXMLText_; - DASHTree(); - ~DASHTree(); - bool open(const char *url); + AdaptiveTree(); bool has_type(StreamType t); uint32_t estimate_segcount(uint32_t duration, uint32_t timescale); double get_download_speed() const { return download_speed_; }; double get_average_download_speed() const { return average_download_speed_; }; void set_download_speed(double speed); - void SetFragmentDuration(const AdaptationSet* adp, const Representation* rep, size_t pos, uint32_t fragmentDuration); + void SetFragmentDuration(const AdaptationSet* adp, const Representation* rep, size_t pos, uint32_t fragmentDuration, uint32_t movie_timescale); - bool empty(); + bool empty(){ return !current_period_ || current_period_->adaptationSets_.empty(); }; const AdaptationSet *GetAdaptationSet(unsigned int pos) const { return current_period_ && pos < current_period_->adaptationSets_.size() ? current_period_->adaptationSets_[pos] : 0; }; -protected: - virtual bool download(const char* url); - bool write_data(void *buffer, size_t buffer_size); + + virtual bool open(const char *url) = 0; + protected: + virtual bool download(const char* url); + virtual bool write_data(void *buffer, size_t buffer_size) = 0; }; } diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/dash/helpers.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/helpers.cpp similarity index 78% rename from xbmc/cores/dvdplayer/DVDDemuxers/dash/helpers.cpp rename to xbmc/cores/dvdplayer/DVDDemuxers/adaptive/helpers.cpp index 7a44835e1e..84aecb944e 100644 --- a/xbmc/cores/dvdplayer/DVDDemuxers/dash/helpers.cpp +++ b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/helpers.cpp @@ -20,6 +20,7 @@ #include #include "oscompat.h" #include +#include "ap4/Ap4DataBuffer.h" #ifndef BYTE typedef unsigned char BYTE; @@ -253,3 +254,69 @@ std::string annexb_to_avc(const char *b16_data) return result; } + +void prkid2wvkid(const char *input, char *output) +{ + static const uint8_t remap[16] = { 3,2,1,0,5,4,7,6,8,9,10,11,12,13,14,15 }; + for (unsigned int i(0); i < 16; ++i) + output[i] = input[remap[i]]; +} + +bool create_ism_license(std::string key, std::string license_data, AP4_DataBuffer &init_data) +{ + if (key.size() != 16 || license_data.empty()) + { + init_data.SetDataSize(0); + return false; + } + + uint8_t ld[1024]; + unsigned int ld_size(1024); + b64_decode(license_data.c_str(), license_data.size(), ld, ld_size); + + const uint8_t *uuid((uint8_t*)strstr((const char*)ld, "{UUID}")); + unsigned int license_size = uuid ? ld_size + 36 - 6 : ld_size; + + //Build up proto header + init_data.Reserve(512); + uint8_t *protoptr(init_data.UseData()); + *protoptr++ = 18; //id=16>>3=2, type=2(flexlen) + *protoptr++ = 16; //length of key + memcpy(protoptr, key.data(), 16); + protoptr += 16; + //----------- + *protoptr++ = 34;//id=32>>3=4, type=2(flexlen) + do { + *protoptr++ = static_cast(license_size & 127); + license_size >>= 7; + if (license_size) + *(protoptr - 1) |= 128; + else + break; + } while (1); + if (uuid) + { + static const uint8_t hexmap[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; + memcpy(protoptr, ld, uuid - ld); + protoptr += uuid - ld; + + for (unsigned int i(0); i < 16; ++i) + { + if(i == 4 || i == 6 || i == 8 || i == 10) + *protoptr++ = '-'; + *protoptr++ = hexmap[(uint8_t)(key.data()[i]) >> 4]; + *protoptr++ = hexmap[(uint8_t)(key.data()[i]) & 15]; + } + unsigned int sizeleft = ld_size - ((uuid - ld) + 6); + memcpy(protoptr, uuid + 6, sizeleft); + protoptr += sizeleft; + } + else + { + memcpy(protoptr, ld, ld_size); + protoptr += ld_size; + } + init_data.SetDataSize(protoptr - init_data.UseData()); + + return true; +} diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/dash/helpers.h b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/helpers.h similarity index 85% rename from xbmc/cores/dvdplayer/DVDDemuxers/dash/helpers.h rename to xbmc/cores/dvdplayer/DVDDemuxers/adaptive/helpers.h index c28400a4a3..7052cc6ff6 100644 --- a/xbmc/cores/dvdplayer/DVDDemuxers/dash/helpers.h +++ b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/helpers.h @@ -22,6 +22,9 @@ #include #include + +class AP4_DataBuffer; + bool b64_decode(const char *in, unsigned int in_len, uint8_t *out, unsigned int &out_len); std::string b64_encode(unsigned char const* in, unsigned int in_len, bool urlEncode); @@ -33,3 +36,6 @@ std::string &trim(std::string &src); std::string url_decode(std::string text); std::string annexb_to_avc(const char *b16_data); + +void prkid2wvkid(const char *input, char *output); +bool create_ism_license(std::string key, std::string license_data, AP4_DataBuffer &init_data); diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/dash/oscompat.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/oscompat.cpp similarity index 100% rename from xbmc/cores/dvdplayer/DVDDemuxers/dash/oscompat.cpp rename to xbmc/cores/dvdplayer/DVDDemuxers/adaptive/oscompat.cpp diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/dash/oscompat.h b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/oscompat.h similarity index 100% rename from xbmc/cores/dvdplayer/DVDDemuxers/dash/oscompat.h rename to xbmc/cores/dvdplayer/DVDDemuxers/adaptive/oscompat.h diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHTree.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/parsers/DASHTree.cpp similarity index 84% rename from xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHTree.cpp rename to xbmc/cores/dvdplayer/DVDDemuxers/adaptive/parsers/DASHTree.cpp index b0b520f293..886b15d163 100644 --- a/xbmc/cores/dvdplayer/DVDDemuxers/dash/DASHTree.cpp +++ b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/parsers/DASHTree.cpp @@ -15,18 +15,14 @@ #include #include "DASHTree.h" -#include "oscompat.h" -#include "helpers.h" +#include "../oscompat.h" +#include "../helpers.h" -#include "URL.h" -#include "filesystem/File.h" #include "utils/log.h" -#include "utils/URIUtils.h" //#define DEBUG_VERBOSE 1 -using namespace dash; -using namespace XFILE; +using namespace adaptive; const char* TRANSLANG[370] = { "aa", "aar", @@ -230,15 +226,6 @@ static const char* ltranslate(const char * in) } DASHTree::DASHTree() - :download_speed_(0.0) - , average_download_speed_(0.0f) - , parser_(0) - , encryptionState_(ENCRYTIONSTATE_UNENCRYPTED) - , current_period_(0) - , live_start_(0) - , stream_start_(0) - , base_time_(0) - , publish_time_(0) { } @@ -357,7 +344,7 @@ start(void *data, const char *el, const char **attr) else if (strcmp((const char*)*attr, "media") == 0) { seg.media_ = (const char*)*(attr + 1); - dash->current_representation_->flags_ |= DASHTree::Representation::SEGMENTMEDIA; + dash->current_representation_->flags_ |= adaptive::AdaptiveTree::Representation::SEGMENTMEDIA; } attr += 2; } @@ -372,11 +359,11 @@ start(void *data, const char *el, const char **attr) else if (strcmp((const char*)*attr, "sourceURL") == 0) { seg.media_ = (const char*)*(attr + 1); - dash->current_representation_->flags_ |= DASHTree::Representation::SEGMENTMEDIA; + dash->current_representation_->flags_ |= adaptive::AdaptiveTree::Representation::SEGMENTMEDIA; } attr += 2; } - dash->current_representation_->flags_ |= DASHTree::Representation::INITIALIZATION; + dash->current_representation_->flags_ |= adaptive::AdaptiveTree::Representation::INITIALIZATION; dash->current_representation_->initialization_ = seg; } else @@ -402,13 +389,13 @@ start(void *data, const char *el, const char **attr) } if (d && r) { - DASHTree::Segment s; + adaptive::AdaptiveTree::Segment s; if (dash->current_representation_->segments_.data.empty()) { if (dash->current_representation_->segtpl_.duration && dash->current_representation_->segtpl_.timescale) dash->current_representation_->segments_.data.reserve((unsigned int)(dash->overallSeconds_ / (((double)dash->current_representation_->segtpl_.duration) / dash->current_representation_->segtpl_.timescale)) + 1); - if (dash->current_representation_->flags_ & DASHTree::Representation::INITIALIZATION) + if (dash->current_representation_->flags_ & adaptive::AdaptiveTree::Representation::INITIALIZATION) { s.range_begin_ = s.range_end_ = ~0; dash->current_representation_->initialization_ = s; @@ -436,7 +423,7 @@ start(void *data, const char *el, const char **attr) } else if (strcmp(el, "SegmentTimeline") == 0) { - dash->current_representation_->flags_ |= DASHTree::Representation::TIMELINE; + dash->current_representation_->flags_ |= adaptive::AdaptiveTree::Representation::TIMELINE; dash->currentNode_ |= DASHTree::MPDNODE_SEGMENTTIMELINE; } } @@ -475,11 +462,11 @@ start(void *data, const char *el, const char **attr) if (strcmp((const char*)*attr, "indexRange") == 0) sscanf((const char*)*(attr + 1), "%u-%u" , &dash->current_representation_->indexRangeMin_, &dash->current_representation_->indexRangeMax_); else if (strcmp((const char*)*attr, "indexRangeExact") == 0 && strcmp((const char*)*(attr + 1), "true") == 0) - dash->current_representation_->flags_ |= DASHTree::Representation::INDEXRANGEEXACT; - dash->current_representation_->flags_ |= DASHTree::Representation::SEGMENTBASE; + dash->current_representation_->flags_ |= adaptive::AdaptiveTree::Representation::INDEXRANGEEXACT; + dash->current_representation_->flags_ |= adaptive::AdaptiveTree::Representation::SEGMENTBASE; attr += 2; } - if((dash->current_representation_->flags_ & DASHTree::Representation::INDEXRANGEEXACT) && dash->current_representation_->indexRangeMax_) + if((dash->current_representation_->flags_ & adaptive::AdaptiveTree::Representation::INDEXRANGEEXACT) && dash->current_representation_->indexRangeMax_) dash->currentNode_ |= DASHTree::MPDNODE_SEGMENTLIST; } else if (strcmp(el, "SegmentTemplate") == 0) @@ -487,10 +474,10 @@ start(void *data, const char *el, const char **attr) dash->current_representation_->segtpl_ = dash->current_adaptationset_->segtpl_; ParseSegmentTemplate(attr, dash->current_representation_->url_, dash->current_representation_->segtpl_, false); - dash->current_representation_->flags_ |= DASHTree::Representation::TEMPLATE; + dash->current_representation_->flags_ |= adaptive::AdaptiveTree::Representation::TEMPLATE; if (!dash->current_representation_->segtpl_.initialization.empty()) { - dash->current_representation_->flags_ |= DASHTree::Representation::INITIALIZATION; + dash->current_representation_->flags_ |= adaptive::AdaptiveTree::Representation::INITIALIZATION; dash->current_representation_->url_ += dash->current_representation_->segtpl_.initialization; dash->current_representation_->timescale_ = dash->current_representation_->segtpl_.timescale; } @@ -544,9 +531,9 @@ start(void *data, const char *el, const char **attr) if (strcmp((const char*)*attr, "contentType") == 0) { dash->current_adaptationset_->type_ = - stricmp((const char*)*(attr + 1), "video") == 0 ? DASHTree::VIDEO - : stricmp((const char*)*(attr + 1), "audio") == 0 ? DASHTree::AUDIO - : DASHTree::NOTYPE; + stricmp((const char*)*(attr + 1), "video") == 0 ? adaptive::AdaptiveTree::VIDEO + : stricmp((const char*)*(attr + 1), "audio") == 0 ? adaptive::AdaptiveTree::AUDIO + : adaptive::AdaptiveTree::NOTYPE; break; } attr += 2; @@ -563,7 +550,7 @@ start(void *data, const char *el, const char **attr) } else if (strcmp(el, "Representation") == 0) { - dash->current_representation_ = new DASHTree::Representation(); + dash->current_representation_ = new adaptive::AdaptiveTree::Representation(); dash->current_representation_->channelCount_ = dash->adpChannelCount_; dash->current_representation_->codecs_ = dash->current_adaptationset_->codecs_; dash->current_representation_->url_ = dash->current_adaptationset_->base_url_; @@ -617,7 +604,7 @@ start(void *data, const char *el, const char **attr) else if (strcmp(el, "ContentProtection") == 0) { dash->strXMLText_.clear(); - dash->encryptionState_ |= DASHTree::ENCRYTIONSTATE_ENCRYPTED; + dash->encryptionState_ |= adaptive::AdaptiveTree::ENCRYTIONSTATE_ENCRYPTED; bool urnFound(false); for (; *attr;) { @@ -631,7 +618,7 @@ start(void *data, const char *el, const char **attr) if (urnFound) { dash->currentNode_ |= DASHTree::MPDNODE_CONTENTPROTECTION; - dash->encryptionState_ |= DASHTree::ENCRYTIONSTATE_SUPPORTED; + dash->encryptionState_ |= adaptive::AdaptiveTree::ENCRYTIONSTATE_SUPPORTED; } } else if (strcmp(el, "AudioChannelConfiguration") == 0) @@ -647,7 +634,7 @@ start(void *data, const char *el, const char **attr) else if (strcmp(el, "AdaptationSet") == 0) { // - dash->current_adaptationset_ = new DASHTree::AdaptationSet(); + dash->current_adaptationset_ = new adaptive::AdaptiveTree::AdaptationSet(); dash->current_period_->adaptationSets_.push_back(dash->current_adaptationset_); dash->current_adaptationset_->base_url_ = dash->current_period_->base_url_; dash->adp_pssh_.second.clear(); @@ -657,9 +644,9 @@ start(void *data, const char *el, const char **attr) { if (strcmp((const char*)*attr, "contentType") == 0) dash->current_adaptationset_->type_ = - stricmp((const char*)*(attr + 1), "video") == 0 ? DASHTree::VIDEO - : stricmp((const char*)*(attr + 1), "audio") == 0 ? DASHTree::AUDIO - : DASHTree::NOTYPE; + stricmp((const char*)*(attr + 1), "video") == 0 ? adaptive::AdaptiveTree::VIDEO + : stricmp((const char*)*(attr + 1), "audio") == 0 ? adaptive::AdaptiveTree::AUDIO + : adaptive::AdaptiveTree::NOTYPE; else if (strcmp((const char*)*attr, "lang") == 0) dash->current_adaptationset_->language_ = ltranslate((const char*)*(attr + 1)); else if (strcmp((const char*)*attr, "mimeType") == 0) @@ -681,7 +668,7 @@ start(void *data, const char *el, const char **attr) } else if (strcmp(el, "Period") == 0) { - dash->current_period_ = new DASHTree::Period(); + dash->current_period_ = new adaptive::AdaptiveTree::Period(); dash->current_period_->base_url_ = dash->base_url_; dash->periods_.push_back(dash->current_period_); dash->currentNode_ |= DASHTree::MPDNODE_PERIOD; @@ -697,9 +684,16 @@ start(void *data, const char *el, const char **attr) if (strcmp((const char*)*attr, "mediaPresentationDuration") == 0) mpt = (const char*)*(attr + 1); else if (strcmp((const char*)*attr, "timeShiftBufferDepth") == 0) + { tsbd = (const char*)*(attr + 1); + dash->has_timeshift_buffer_ = true; + } else if (strcmp((const char*)*attr, "availabilityStartTime") == 0) - dash->live_start_ = getTime((const char*)*(attr + 1)); + { + dash->available_time_ = getTime((const char*)*(attr + 1)); + if (!dash->available_time_) + dash->available_time_ = ~0ULL; + } else if (strcmp((const char*)*attr, "publishTime") == 0) dash->publish_time_ = getTime((const char*)*(attr + 1)); attr += 2; @@ -724,8 +718,8 @@ start(void *data, const char *el, const char **attr) if (next) dash->overallSeconds_ += atof(mpt); } - if (dash->publish_time_ && dash->live_start_ && dash->publish_time_ - dash->live_start_ > dash->overallSeconds_) - dash->base_time_ = dash->publish_time_ - dash->live_start_ - dash->overallSeconds_; + if (dash->publish_time_ && dash->available_time_ && dash->publish_time_ - dash->available_time_ > dash->overallSeconds_) + dash->base_time_ = dash->publish_time_ - dash->available_time_ - dash->overallSeconds_; dash->minPresentationOffset = DBL_MAX; dash->currentNode_ |= DASHTree::MPDNODE_MPD; @@ -824,7 +818,7 @@ end(void *data, const char *el) if (dash->current_representation_->segments_.data.empty()) { bool isSegmentTpl(!dash->current_representation_->segtpl_.media.empty()); - DASHTree::SegmentTemplate &tpl(isSegmentTpl ? dash->current_representation_->segtpl_ : dash->current_adaptationset_->segtpl_); + adaptive::AdaptiveTree::SegmentTemplate &tpl(isSegmentTpl ? dash->current_representation_->segtpl_ : dash->current_adaptationset_->segtpl_); if (!tpl.media.empty() && dash->overallSeconds_ > 0 && tpl.timescale > 0 && (tpl.duration > 0 || dash->current_adaptationset_->segment_durations_.data.size())) @@ -834,10 +828,10 @@ end(void *data, const char *el) if (countSegs < 65536) { - DASHTree::Segment seg; + adaptive::AdaptiveTree::Segment seg; seg.range_begin_ = ~0; - dash->current_representation_->flags_ |= DASHTree::Representation::TEMPLATE; + dash->current_representation_->flags_ |= adaptive::AdaptiveTree::Representation::TEMPLATE; dash->current_representation_->segments_.data.reserve(countSegs); if (!tpl.initialization.empty()) @@ -852,20 +846,20 @@ end(void *data, const char *el) } dash->current_representation_->initialization_ = seg; - dash->current_representation_->flags_ |= DASHTree::Representation::INITIALIZATION; + dash->current_representation_->flags_ |= adaptive::AdaptiveTree::Representation::INITIALIZATION; } std::vector::const_iterator sdb(dash->current_adaptationset_->segment_durations_.data.begin()), sde(dash->current_adaptationset_->segment_durations_.data.end()); bool timeBased = sdb!=sde && tpl.media.find("$Time") != std::string::npos; if(timeBased) - dash->current_representation_->flags_ |= DASHTree::Representation::TIMETEMPLATE; + dash->current_representation_->flags_ |= adaptive::AdaptiveTree::Representation::TIMETEMPLATE; seg.range_end_ = timeBased ? dash->current_adaptationset_->startPTS_ : tpl.startNumber; seg.startPTS_ = dash->current_adaptationset_->startPTS_; - if (!timeBased && dash->live_start_ /*&& !dash->publish_time_*/ && dash->stream_start_ - dash->live_start_ > dash->overallSeconds_) //we need to adjust the start-segment - seg.range_end_ += ((dash->stream_start_ - dash->live_start_ - dash->overallSeconds_)*tpl.timescale) / tpl.duration; + if (!timeBased && dash->available_time_ /*&& !dash->publish_time_*/ && dash->stream_start_ - dash->available_time_ > dash->overallSeconds_) //we need to adjust the start-segment + seg.range_end_ += ((dash->stream_start_ - dash->available_time_ - dash->overallSeconds_)*tpl.timescale) / tpl.duration; for (;countSegs;--countSegs) { @@ -876,7 +870,7 @@ end(void *data, const char *el) return; } } - else if (dash->current_representation_->flags_ & DASHTree::Representation::SEGMENTMEDIA) + else if (dash->current_representation_->flags_ & adaptive::AdaptiveTree::Representation::SEGMENTMEDIA) { if (dash->current_representation_->segments_.data.size() != dash->current_adaptationset_->segment_durations_.data.size()) { @@ -890,7 +884,7 @@ end(void *data, const char *el) t += dash->current_adaptationset_->segment_durations_.data[i]; } } - else if (dash->current_representation_->flags_ & DASHTree::Representation::SEGMENTBASE) + else if (dash->current_representation_->flags_ & adaptive::AdaptiveTree::Representation::SEGMENTBASE) {} else { @@ -947,8 +941,8 @@ end(void *data, const char *el) else if (strcmp(el, "AdaptationSet") == 0) { dash->currentNode_ &= ~DASHTree::MPDNODE_ADAPTIONSET; - if (dash->current_adaptationset_->type_ == DASHTree::NOTYPE - || ((dash->encryptionState_ & DASHTree::ENCRYTIONSTATE_ENCRYPTED) && dash->adp_pssh_.second.empty()) + if (dash->current_adaptationset_->type_ == adaptive::AdaptiveTree::NOTYPE + || ((dash->encryptionState_ & adaptive::AdaptiveTree::ENCRYTIONSTATE_ENCRYPTED) && dash->adp_pssh_.second.empty()) || dash->current_adaptationset_->repesentations_.empty()) { delete dash->current_adaptationset_; @@ -961,7 +955,7 @@ end(void *data, const char *el) if (dash->current_adaptationset_->segment_durations_.data.empty() && !dash->current_adaptationset_->segtpl_.media.empty()) { - for (std::vector::iterator + for (std::vector::iterator b(dash->current_adaptationset_->repesentations_.begin()), e(dash->current_adaptationset_->repesentations_.end()); b != e; ++b) { @@ -975,16 +969,16 @@ end(void *data, const char *el) else if (!dash->current_adaptationset_->segment_durations_.data.empty()) //If representation are not timelined, we have to adjust startPTS_ in rep::segments { - for (std::vector::iterator + for (std::vector::iterator b(dash->current_adaptationset_->repesentations_.begin()), e(dash->current_adaptationset_->repesentations_.end()); b != e; ++b) { - if ((*b)->flags_ & DASHTree::Representation::TIMELINE) + if ((*b)->flags_ & adaptive::AdaptiveTree::Representation::TIMELINE) continue; std::vector::const_iterator sdb(dash->current_adaptationset_->segment_durations_.data.begin()), sde(dash->current_adaptationset_->segment_durations_.data.end()); uint64_t spts(0); - for (std::vector::iterator sb((*b)->segments_.data.begin()), se((*b)->segments_.data.end()); sb != se && sdb != sde; ++sb, ++sdb) + for (std::vector::iterator sb((*b)->segments_.data.begin()), se((*b)->segments_.data.end()); sb != se && sdb != sde; ++sb, ++sdb) { sb->startPTS_ = spts; spts += *sdb; @@ -1010,18 +1004,6 @@ end(void *data, const char *el) | DASHTree +---------------------------------------------------------------------*/ -void DASHTree::Segment::SetRange(const char *range) -{ - const char *delim(strchr(range, '-')); - if (delim) - { - range_begin_ = strtoull(range, 0, 10); - range_end_ = strtoull(delim + 1, 0, 10); - } - else - range_begin_ = range_end_ = 0; -} - bool DASHTree::open(const char *url) { parser_ = XML_ParserCreate(NULL); @@ -1054,92 +1036,3 @@ bool DASHTree::write_data(void *buffer, size_t buffer_size) } return true; } - -bool DASHTree::has_type(StreamType t) -{ - if (periods_.empty()) - return false; - - for (std::vector::const_iterator b(periods_[0]->adaptationSets_.begin()), e(periods_[0]->adaptationSets_.end()); b != e; ++b) - if ((*b)->type_ == t) - return true; - return false; -} - -uint32_t DASHTree::estimate_segcount(uint32_t duration, uint32_t timescale) -{ - duration /= timescale; - return static_cast((overallSeconds_ / duration)*1.01); -} - -void DASHTree::set_download_speed(double speed) -{ - download_speed_ = speed; - if (!average_download_speed_) - average_download_speed_ = download_speed_; - else - average_download_speed_ = average_download_speed_*0.9 + download_speed_*0.1; -}; - -void DASHTree::SetFragmentDuration(const AdaptationSet* adp, const Representation* rep, size_t pos, uint32_t fragmentDuration) -{ - if (!live_start_ /*|| !(rep->flags_ & DASHTree::Representation::TIMELINE)*/) - return; - - //Get a modifiable adaptationset - AdaptationSet *adpm(static_cast((void*)adp)); - - // Check if its the last frame we watch - if (adp->segment_durations_.data.size()) - { - if (pos == adp->segment_durations_.data.size() - 1) - { - adpm->segment_durations_.insert(fragmentDuration); - } - else - return; - } - else if (pos != rep->segments_.data.size() - 1) - return; - - Segment seg(*(rep->segments_[pos])); - seg.range_begin_ += fragmentDuration; - seg.range_end_ += (rep->flags_ & DASHTree::Representation::TIMETEMPLATE)?fragmentDuration:1; - seg.startPTS_ += fragmentDuration; - - for (std::vector::iterator b(adpm->repesentations_.begin()), e(adpm->repesentations_.end()); b != e; ++b) - (*b)->segments_.insert(seg); -} - -bool DASHTree::empty() -{ - return (!current_period_ || current_period_->adaptationSets_.empty()); -} - -bool DASHTree::download(const char* url) -{ - // open the file - CURL uUrl(url); - if (URIUtils::IsInternetStream(uUrl)) - { - uUrl.SetProtocolOption("seekable", "0"); - uUrl.SetProtocolOption("acceptencoding", "gzip"); - } - - CFile* file = new CFile(); - if (!file->Open(uUrl, READ_CHUNKED | READ_NO_CACHE)) - return false; - - // read the file - static const unsigned int CHUNKSIZE = 16384; - char buf[CHUNKSIZE]; - size_t nbRead; - while ((nbRead = file->Read(buf, CHUNKSIZE)) > 0 && ~nbRead && write_data(buf, nbRead)); - - //download_speed_ = xbmc->GetFileDownloadSpeed(file); - - file->Close(); - delete file; - - return nbRead == 0; -} diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/parsers/DASHTree.h b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/parsers/DASHTree.h new file mode 100644 index 0000000000..8544796434 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/parsers/DASHTree.h @@ -0,0 +1,53 @@ +/* +* DASHTree.h +***************************************************************************** +* Copyright (C) 2015, liberty_developer +* +* Email: liberty.developer@xmail.net +* +* This source code and its use and distribution, is subject to the terms +* and conditions of the applicable license agreement. +*****************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include "expat.h" + +#include "../common/AdaptiveTree.h" + +namespace adaptive +{ + + class DASHTree : public AdaptiveTree + { + public: + enum + { + MPDNODE_MPD = 1 << 0, + MPDNODE_PERIOD = 1 << 1, + MPDNODE_ADAPTIONSET = 1 << 2, + MPDNODE_CONTENTPROTECTION = 1 << 3, + MPDNODE_REPRESENTATION = 1 << 4, + MPDNODE_BASEURL = 1 << 5, + MPDNODE_SEGMENTLIST = 1 << 6, + MPDNODE_INITIALIZATION = 1 << 7, + MPDNODE_SEGMENTURL = 1 << 8, + MPDNODE_SEGMENTDURATIONS = 1 << 9, + MPDNODE_S = 1 << 11, + MPDNODE_PSSH = 1 << 12, + MPDNODE_SEGMENTTEMPLATE = 1 << 13, + MPDNODE_SEGMENTTIMELINE = 1 << 14 + }; + + DASHTree(); + virtual ~DASHTree(); + + virtual bool open(const char *url) override; + virtual bool write_data(void *buffer, size_t buffer_size) override; +}; + +} diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/parsers/SmoothTree.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/parsers/SmoothTree.cpp new file mode 100755 index 0000000000..b29959d8a6 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/parsers/SmoothTree.cpp @@ -0,0 +1,402 @@ +/* +* Copyright (C) 2016-2016 peak3d +* http://www.peak3d.de +* +* This Program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2, 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 General Public License for more details. +* +* . +* +*/ + +#include +#include +#include +#include +#include + +#include "SmoothTree.h" +#include "../oscompat.h" +#include "../helpers.h" + +using namespace adaptive; + +uint32_t gTimeScale = 10000000; + +SmoothTree::SmoothTree() +{ + current_period_ = new AdaptiveTree::Period; + periods_.push_back(current_period_); +} + +/*---------------------------------------------------------------------- +| expat start ++---------------------------------------------------------------------*/ +static void XMLCALL +start(void *data, const char *el, const char **attr) +{ + SmoothTree *dash(reinterpret_cast(data)); + + if (dash->currentNode_ & SmoothTree::SSMNODE_SSM) + { + if (dash->currentNode_ & SmoothTree::SSMNODE_PROTECTION) + { + if (strcmp(el, "ProtectionHeader") == 0) + { + for (; *attr;) + { + if (strcmp((const char*)*attr, "SystemID") == 0) + { + if (strstr((const char*)*(attr + 1), "9A04F079-9840-4286-AB92-E65BE0885F95") + || strstr((const char*)*(attr + 1), "9a04f079-9840-4286-ab92-e65be0885f95")) + { + dash->strXMLText_.clear(); + dash->currentNode_ |= SmoothTree::SSMNODE_PROTECTIONHEADER| SmoothTree::SSMNODE_PROTECTIONTEXT; + } + break; + } + attr += 2; + } + } + } + else if (dash->currentNode_ & SmoothTree::SSMNODE_STREAMINDEX) + { + if (strcmp(el, "QualityLevel") == 0) + { + // + // + + std::string::size_type pos = dash->current_adaptationset_->base_url_.find("{bitrate}"); + if (pos == std::string::npos) + return; + + dash->current_representation_ = new SmoothTree::Representation(); + dash->current_representation_->url_ = dash->current_adaptationset_->base_url_; + dash->current_representation_->timescale_ = dash->current_adaptationset_->timescale_; + dash->current_representation_->flags_ |= SmoothTree::Representation::STARTTIMETPL; + + const char *bw = "0"; + + for (; *attr;) + { + if (strcmp((const char*)*attr, "Bitrate") == 0) + bw = (const char*)*(attr + 1); + else if (strcmp((const char*)*attr, "FourCC") == 0) + { + dash->current_representation_->codecs_ = (const char*)*(attr + 1); + std::transform(dash->current_representation_->codecs_.begin(), dash->current_representation_->codecs_.end(), dash->current_representation_->codecs_.begin(), ::tolower); + } + else if (strcmp((const char*)*attr, "MaxWidth") == 0) + dash->current_representation_->width_ = static_cast(atoi((const char*)*(attr + 1))); + else if (strcmp((const char*)*attr, "MaxHeight") == 0) + dash->current_representation_->height_ = static_cast(atoi((const char*)*(attr + 1))); + else if (strcmp((const char*)*attr, "SamplingRate") == 0) + dash->current_representation_->samplingRate_ = static_cast(atoi((const char*)*(attr + 1))); + else if (strcmp((const char*)*attr, "Channels") == 0) + dash->current_representation_->channelCount_ = static_cast(atoi((const char*)*(attr + 1))); + else if (strcmp((const char*)*attr, "Index") == 0) + dash->current_representation_->id = (const char*)*(attr + 1); + else if (strcmp((const char*)*attr, "CodecPrivateData") == 0) + dash->current_representation_->codec_private_data_ = annexb_to_avc((const char*)*(attr + 1)); + else if (strcmp((const char*)*attr, "NALUnitLengthField") == 0) + dash->current_representation_->nalLengthSize_ = static_cast(atoi((const char*)*(attr + 1))); + attr += 2; + } + dash->current_representation_->url_.replace(pos, 9, bw); + dash->current_representation_->bandwidth_ = atoi(bw); + dash->current_adaptationset_->repesentations_.push_back(dash->current_representation_); + } + else if (strcmp(el, "c") == 0) + { + // + uint32_t push_duration(~0); + + for (; *attr;) + { + if (*(const char*)*attr == 't') + { + uint64_t lt(atoll((const char*)*(attr + 1))); + if (!dash->current_adaptationset_->segment_durations_.data.empty()) + dash->current_adaptationset_->segment_durations_.data.back() = static_cast(lt - dash->pts_helper_); + else + dash->current_adaptationset_->startPTS_ = lt; + dash->pts_helper_ = lt; + push_duration = 0; + } + else if (*(const char*)*attr == 'd') + { + push_duration = atoi((const char*)*(attr + 1)); + break; + } + attr += 2; + } + if (~push_duration) + dash->current_adaptationset_->segment_durations_.data.push_back(push_duration); + } + } + else if (strcmp(el, "StreamIndex") == 0) + { + // + dash->current_adaptationset_ = new SmoothTree::AdaptationSet(); + dash->current_period_->adaptationSets_.push_back(dash->current_adaptationset_); + dash->current_adaptationset_->encrypted = dash->encryptionState_ == SmoothTree::ENCRYTIONSTATE_SUPPORTED; + dash->current_adaptationset_->timescale_ = gTimeScale; + + for (; *attr;) + { + if (strcmp((const char*)*attr, "Type") == 0) + dash->current_adaptationset_->type_ = + stricmp((const char*)*(attr + 1), "video") == 0 ? SmoothTree::VIDEO + : stricmp((const char*)*(attr + 1), "audio") == 0 ? SmoothTree::AUDIO + : SmoothTree::NOTYPE; + else if (strcmp((const char*)*attr, "Language") == 0) + dash->current_adaptationset_->language_ = (const char*)*(attr + 1); + else if (strcmp((const char*)*attr, "TimeScale") == 0) + dash->current_adaptationset_->timescale_ = atoi((const char*)*(attr + 1)); + else if (strcmp((const char*)*attr, "Chunks") == 0) + dash->current_adaptationset_->segment_durations_.data.reserve(atoi((const char*)*(attr + 1))); + else if (strcmp((const char*)*attr, "Url") == 0) + dash->current_adaptationset_->base_url_ = dash->base_url_ + (const char*)*(attr + 1); + attr += 2; + } + dash->segcount_ = 0; + dash->currentNode_ |= SmoothTree::SSMNODE_STREAMINDEX; + } + else if (strcmp(el, "Protection") == 0) + { + dash->currentNode_ |= SmoothTree::SSMNODE_PROTECTION; + dash->encryptionState_ = SmoothTree::ENCRYTIONSTATE_SUPPORTED; + } + } + else if (strcmp(el, "SmoothStreamingMedia") == 0) + { + uint64_t duration = 0; + dash->overallSeconds_ = 0; + for (; *attr;) + { + if (strcmp((const char*)*attr, "TimeScale") == 0) + gTimeScale = atoll((const char*)*(attr + 1)); + else if (strcmp((const char*)*attr, "Duration") == 0) + duration = atoll((const char*)*(attr + 1)); + else if (strcmp((const char*)*attr, "IsLive") == 0) + { + dash->has_timeshift_buffer_ = strcmp((const char*)*(attr + 1), "TRUE") == 0; + if (dash->has_timeshift_buffer_) + { + dash->stream_start_ = time(0); + dash->available_time_ = dash->stream_start_; + } + } + attr += 2; + } + dash->overallSeconds_ = (double)duration / gTimeScale; + dash->currentNode_ |= SmoothTree::SSMNODE_SSM; + dash->minPresentationOffset = DBL_MAX; + dash->base_time_ = ~0ULL; + } +} + +/*---------------------------------------------------------------------- +| expat text ++---------------------------------------------------------------------*/ +static void XMLCALL +text(void *data, const char *s, int len) +{ + SmoothTree *dash(reinterpret_cast(data)); + + if (dash->currentNode_ & SmoothTree::SSMNODE_PROTECTIONTEXT) + dash->strXMLText_ += std::string(s, len); +} + +/*---------------------------------------------------------------------- +| expat end ++---------------------------------------------------------------------*/ +static void XMLCALL +end(void *data, const char *el) +{ + SmoothTree *dash(reinterpret_cast(data)); + + if (dash->currentNode_ & SmoothTree::SSMNODE_SSM) + { + if (dash->currentNode_ & SmoothTree::SSMNODE_PROTECTION) + { + if (dash->currentNode_ & SmoothTree::SSMNODE_PROTECTIONHEADER) + { + if (strcmp(el, "ProtectionHeader") == 0) + dash->currentNode_ &= ~SmoothTree::SSMNODE_PROTECTIONHEADER; + } + else if (strcmp(el, "Protection") == 0) + { + dash->currentNode_ &= ~(SmoothTree::SSMNODE_PROTECTION| SmoothTree::SSMNODE_PROTECTIONTEXT); + dash->parse_protection(); + } + } + else if (dash->currentNode_ & SmoothTree::SSMNODE_STREAMINDEX) + { + if (strcmp(el, "StreamIndex") == 0) + { + if (dash->current_adaptationset_->repesentations_.empty() + || dash->current_adaptationset_->segment_durations_.data.empty()) + dash->current_period_->adaptationSets_.pop_back(); + else + { + if (dash->current_adaptationset_->startPTS_ < dash->base_time_) + dash->base_time_ = dash->current_adaptationset_->startPTS_; + } + dash->currentNode_ &= ~SmoothTree::SSMNODE_STREAMINDEX; + } + } + else if (strcmp(el, "SmoothStreamingMedia") == 0) + dash->currentNode_ &= ~SmoothTree::SSMNODE_SSM; + } +} + +/*---------------------------------------------------------------------- +| expat protection start ++---------------------------------------------------------------------*/ +static void XMLCALL +protection_start(void *data, const char *el, const char **attr) +{ + SmoothTree *dash(reinterpret_cast(data)); + dash->strXMLText_.clear(); +} + +/*---------------------------------------------------------------------- +| expat protection text ++---------------------------------------------------------------------*/ +static void XMLCALL +protection_text(void *data, const char *s, int len) +{ + SmoothTree *dash(reinterpret_cast(data)); + dash->strXMLText_ += std::string(s, len); +} + +/*---------------------------------------------------------------------- +| expat protection end ++---------------------------------------------------------------------*/ +static void XMLCALL +protection_end(void *data, const char *el) +{ + SmoothTree *dash(reinterpret_cast(data)); + if (strcmp(el, "KID") == 0) + { + uint8_t buffer[32]; + unsigned int buffer_size(32); + b64_decode(dash->strXMLText_.data(), dash->strXMLText_.size(), buffer, buffer_size); + + if (buffer_size == 16) + { + dash->defaultKID_.resize(16); + prkid2wvkid(reinterpret_cast(buffer), &dash->defaultKID_[0]); + } + } +} + +/*---------------------------------------------------------------------- +| SmoothTree ++---------------------------------------------------------------------*/ + +bool SmoothTree::open(const char *url) +{ + parser_ = XML_ParserCreate(NULL); + if (!parser_) + return false; + XML_SetUserData(parser_, (void*)this); + XML_SetElementHandler(parser_, start, end); + XML_SetCharacterDataHandler(parser_, text); + currentNode_ = 0; + strXMLText_.clear(); + + bool ret = download(url); + + XML_ParserFree(parser_); + parser_ = 0; + + if (!ret) + return false; + + for (std::vector::iterator ba(current_period_->adaptationSets_.begin()), ea(current_period_->adaptationSets_.end()); ba != ea; ++ba) + { + for (std::vector::iterator b((*ba)->repesentations_.begin()), e((*ba)->repesentations_.end()); b != e; ++b) + { + (*b)->segments_.data.resize((*ba)->segment_durations_.data.size()); + std::vector::iterator bsd((*ba)->segment_durations_.data.begin()); + uint64_t cummulated = (*ba)->startPTS_ - base_time_; + for (std::vector::iterator bs((*b)->segments_.data.begin()), es((*b)->segments_.data.end()); bs != es; ++bsd, ++bs) + { + bs->range_begin_ = ~0; + bs->range_end_ = bs->startPTS_ = cummulated; + cummulated += *bsd; + } + } + } + return true; +} + +bool SmoothTree::write_data(void *buffer, size_t buffer_size) +{ + bool done(false); + XML_Status retval = XML_Parse(parser_, (const char*)buffer, buffer_size, done); + + if (retval == XML_STATUS_ERROR) + { + unsigned int byteNumber = XML_GetErrorByteIndex(parser_); + return false; + } + return true; +} + +void SmoothTree::parse_protection() +{ + if (strXMLText_.empty()) + return; + + //(p)repair the content + std::string::size_type pos = 0; + while ((pos = strXMLText_.find('\n', 0)) != std::string::npos) + strXMLText_.erase(pos, 1); + + while (strXMLText_.size() & 3) + strXMLText_ += "="; + + unsigned int xml_size = strXMLText_.size(); + uint8_t *buffer = (uint8_t*)malloc(xml_size), *xml_start(buffer); + + if (!b64_decode(strXMLText_.c_str(), xml_size, buffer, xml_size)) + { + free(buffer); + return; + } + + while (xml_size && *xml_start != '<') + { + xml_start++; + xml_size--; + } + + XML_Parser pp = XML_ParserCreate("UTF-16"); + if (!pp) + { + free(buffer); + return; + } + + XML_SetUserData(pp, (void*)this); + XML_SetElementHandler(pp, protection_start, protection_end); + XML_SetCharacterDataHandler(pp, protection_text); + + bool done(false); + XML_Parse(pp, (const char*)(xml_start), xml_size, done); + + XML_ParserFree(pp); + free(buffer); + + strXMLText_.clear(); +} diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/parsers/SmoothTree.h b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/parsers/SmoothTree.h new file mode 100644 index 0000000000..6b32daa6c3 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/adaptive/parsers/SmoothTree.h @@ -0,0 +1,47 @@ +/* +* Copyright (C) 2016-2016 peak3d +* http://www.peak3d.de +* +* This Program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2, 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 General Public License for more details. +* +* . +* +*/ + +#pragma once + +#include "../common/AdaptiveTree.h" + +namespace adaptive +{ + + class SmoothTree : public AdaptiveTree + { + public: + SmoothTree(); + virtual bool open(const char *url) override; + virtual bool write_data(void *buffer, size_t buffer_size) override; + + void parse_protection(); + + enum + { + SSMNODE_SSM = 1 << 0, + SSMNODE_PROTECTION = 1 << 1, + SSMNODE_STREAMINDEX = 1 << 2, + SSMNODE_PROTECTIONHEADER = 1 << 3, + SSMNODE_PROTECTIONTEXT = 1 << 4 + }; + + uint64_t pts_helper_; + }; + +} diff --git a/xbmc/settings/AdvancedSettings.cpp b/xbmc/settings/AdvancedSettings.cpp index 9574ff01a7..af01d8888c 100755 --- a/xbmc/settings/AdvancedSettings.cpp +++ b/xbmc/settings/AdvancedSettings.cpp @@ -419,7 +419,7 @@ void CAdvancedSettings::Initialize() m_pictureExtensions = ".png|.jpg|.jpeg|.bmp|.gif|.ico|.tif|.tiff|.tga|.pcx|.cbz|.zip|.cbr|.rar|.dng|.nef|.cr2|.crw|.orf|.arw|.erf|.3fr|.dcr|.x3f|.mef|.raf|.mrw|.pef|.sr2|.rss"; m_musicExtensions = ".nsv|.m4a|.flac|.aac|.strm|.pls|.rm|.rma|.mpa|.wav|.wma|.ogg|.mp3|.mp2|.m3u|.gdm|.imf|.m15|.sfx|.uni|.ac3|.dts|.cue|.aif|.aiff|.wpl|.ape|.mac|.mpc|.mp+|.mpp|.shn|.zip|.rar|.wv|.dsp|.xsp|.xwav|.waa|.wvs|.wam|.gcm|.idsp|.mpdsp|.mss|.spt|.rsd|.sap|.cmc|.cmr|.dmc|.mpt|.mpd|.rmt|.tmc|.tm8|.tm2|.oga|.url|.pxml|.tta|.rss|.wtv|.mka|.tak|.opus|.dff|.dsf"; - m_videoExtensions = ".m4v|.3g2|.3gp|.nsv|.tp|.ts|.ty|.strm|.pls|.rm|.rmvb|.mpd|.m3u|.m3u8|.ifo|.mov|.qt|.divx|.xvid|.bivx|.vob|.nrg|.img|.iso|.pva|.wmv|.asf|.asx|.ogm|.m2v|.avi|.bin|.dat|.mpg|.mpeg|.mp4|.mkv|.mk3d|.avc|.vp3|.svq3|.nuv|.viv|.dv|.fli|.flv|.rar|.001|.wpl|.zip|.vdr|.dvr-ms|.xsp|.mts|.m2t|.m2ts|.evo|.ogv|.sdp|.avs|.rec|.url|.pxml|.vc1|.h264|.rcv|.rss|.mpls|.webm|.bdmv|.wtv|.ssif"; + m_videoExtensions = ".m4v|.3g2|.3gp|.nsv|.tp|.ts|.ty|.strm|.pls|.rm|.rmvb|.mpd|.ism|.ismc|.m3u|.m3u8|.ifo|.mov|.qt|.divx|.xvid|.bivx|.vob|.nrg|.img|.iso|.pva|.wmv|.asf|.asx|.ogm|.m2v|.avi|.bin|.dat|.mpg|.mpeg|.mp4|.mkv|.mk3d|.avc|.vp3|.svq3|.nuv|.viv|.dv|.fli|.flv|.rar|.001|.wpl|.zip|.vdr|.dvr-ms|.xsp|.mts|.m2t|.m2ts|.evo|.ogv|.sdp|.avs|.rec|.url|.pxml|.vc1|.h264|.rcv|.rss|.mpls|.webm|.bdmv|.wtv|.ssif"; m_subtitlesExtensions = ".utf|.utf8|.utf-8|.sub|.srt|.smi|.rt|.txt|.ssa|.text|.ssa|.aqt|.jss|.ass|.idx|.ifo|.rar|.zip"; m_discStubExtensions = ".disc"; // internal music extensions