|
| 1 | +/* |
| 2 | + * Copyright (C) 2016 Christian Browet |
| 3 | + * Copyright (C) 2016-2016 peak3d |
| 4 | + * http://xbmc.org |
| 5 | + * |
| 6 | + * This Program is free software; you can redistribute it and/or modify |
| 7 | + * it under the terms of the GNU General Public License as published by |
| 8 | + * the Free Software Foundation; either version 2, or (at your option) |
| 9 | + * any later version. |
| 10 | + * |
| 11 | + * This Program is distributed in the hope that it will be useful, |
| 12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | + * GNU General Public License for more details. |
| 15 | + * |
| 16 | + * You should have received a copy of the GNU General Public License |
| 17 | + * along with XBMC; see the file COPYING. If not, see |
| 18 | + * <http://www.gnu.org/licenses/>. |
| 19 | + * |
| 20 | + */ |
| 21 | + |
| 22 | +#include "DVDDemuxAdaptive.h" |
| 23 | + |
| 24 | +#include "DVDDemuxPacket.h" |
| 25 | +#include "DVDDemuxUtils.h" |
| 26 | +#include "DVDInputStreams/DVDInputStream.h" |
| 27 | + |
| 28 | +#include "adaptive/DASHByteStream.h" |
| 29 | + |
| 30 | +#ifdef TARGET_ANDROID |
| 31 | +#include "android/jni/SystemProperties.h" |
| 32 | +#endif |
| 33 | +#ifdef TARGET_WINDOWS |
| 34 | +#pragma comment(lib, "libexpat.lib") |
| 35 | +#pragma comment(lib, "ap4.lib") |
| 36 | +#endif |
| 37 | + |
| 38 | +#include "utils/StringUtils.h" |
| 39 | +#include "utils/log.h" |
| 40 | + |
| 41 | +CDVDDemuxAdaptive::CDVDDemuxAdaptive() |
| 42 | + : CDVDDemux() |
| 43 | +{ |
| 44 | + CLog::Log(LOGDEBUG, "CDVDDemuxAdaptive::%s", __FUNCTION__); |
| 45 | +} |
| 46 | + |
| 47 | +CDVDDemuxAdaptive::~CDVDDemuxAdaptive() |
| 48 | +{ |
| 49 | + CLog::Log(LOGDEBUG, "CDVDDemuxAdaptive::%s", __FUNCTION__); |
| 50 | +} |
| 51 | + |
| 52 | +bool CDVDDemuxAdaptive::Open(CDVDInputStream* pInput, uint32_t maxWidth, uint32_t maxHeight) |
| 53 | +{ |
| 54 | + CLog::Log(LOGINFO, "CDVDDemuxAdaptive - matching against %d x %d", maxWidth, maxHeight); |
| 55 | + |
| 56 | + CDASHSession::MANIFEST_TYPE type = CDASHSession::MANIFEST_TYPE_UNKNOWN; |
| 57 | + |
| 58 | + if (pInput->GetFileItem().GetMimeType() == "video/vnd.mpeg.dash.mpd" || pInput->GetFileItem().IsType(".mpd")) //MPD |
| 59 | + type = CDASHSession::MANIFEST_TYPE_MPD; |
| 60 | + else if (pInput->GetFileItem().GetMimeType() == "application/vnd.ms-sstr+xml" || pInput->GetFileItem().IsType(".ismc") || pInput->GetFileItem().IsType(".ism")) //ISM |
| 61 | + type = CDASHSession::MANIFEST_TYPE_ISM; |
| 62 | + |
| 63 | + if (type == CDASHSession::MANIFEST_TYPE_UNKNOWN) |
| 64 | + return false; |
| 65 | + |
| 66 | + m_session.reset(new CDASHSession(type, pInput->GetFileName(), maxWidth, maxHeight, "", "", "special://profile/")); |
| 67 | + |
| 68 | + if (!m_session->initialize()) |
| 69 | + { |
| 70 | + m_session = nullptr; |
| 71 | + return false; |
| 72 | + } |
| 73 | + return true; |
| 74 | +} |
| 75 | + |
| 76 | +void CDVDDemuxAdaptive::Dispose() |
| 77 | +{ |
| 78 | +} |
| 79 | + |
| 80 | +void CDVDDemuxAdaptive::Reset() |
| 81 | +{ |
| 82 | +} |
| 83 | + |
| 84 | +void CDVDDemuxAdaptive::Abort() |
| 85 | +{ |
| 86 | +} |
| 87 | + |
| 88 | +void CDVDDemuxAdaptive::Flush() |
| 89 | +{ |
| 90 | +} |
| 91 | + |
| 92 | +DemuxPacket*CDVDDemuxAdaptive::Read() |
| 93 | +{ |
| 94 | + if (!m_session) |
| 95 | + return NULL; |
| 96 | + |
| 97 | + CDASHFragmentedSampleReader *sr(m_session->GetNextSample()); |
| 98 | + |
| 99 | + if (m_session->CheckChange()) |
| 100 | + { |
| 101 | + DemuxPacket *p = CDVDDemuxUtils::AllocateDemuxPacket(0); |
| 102 | + p->iStreamId = DMX_SPECIALID_STREAMCHANGE; |
| 103 | + CLog::Log(LOGDEBUG, "DMX_SPECIALID_STREAMCHANGE"); |
| 104 | + return p; |
| 105 | + } |
| 106 | + |
| 107 | + if (sr) |
| 108 | + { |
| 109 | + DemuxPacket *p = CDVDDemuxUtils::AllocateDemuxPacket(sr->GetSampleDataSize()); |
| 110 | + p->dts = sr->DTS() * 1000000; |
| 111 | + p->pts = sr->PTS() * 1000000; |
| 112 | + p->duration = sr->GetDuration() * 1000000; |
| 113 | + p->iStreamId = sr->GetStreamId(); |
| 114 | + p->iGroupId = 0; |
| 115 | + p->iSize = sr->GetSampleDataSize(); |
| 116 | + memcpy(p->pData, sr->GetSampleData(), p->iSize); |
| 117 | + |
| 118 | + CLog::Log(LOGDEBUG, "DTS: %0.4f, PTS:%0.4f, ID: %u SZ: %d", p->dts, p->pts, p->iStreamId, p->iSize); |
| 119 | + |
| 120 | + sr->ReadSample(); |
| 121 | + return p; |
| 122 | + } |
| 123 | + return NULL; |
| 124 | +} |
| 125 | + |
| 126 | +bool CDVDDemuxAdaptive::SeekTime(int time, bool backwards, double* startpts) |
| 127 | +{ |
| 128 | + if (!m_session) |
| 129 | + return false; |
| 130 | + |
| 131 | + return m_session->SeekTime(static_cast<double>(time)*0.001f, 0, !backwards); |
| 132 | +} |
| 133 | + |
| 134 | +void CDVDDemuxAdaptive::SetSpeed(int speed) |
| 135 | +{ |
| 136 | +} |
| 137 | + |
| 138 | +int CDVDDemuxAdaptive::GetNrOfStreams() |
| 139 | +{ |
| 140 | + int n = 0; |
| 141 | + if (m_session) |
| 142 | + n = m_session->GetStreamCount(); |
| 143 | + |
| 144 | + return n; |
| 145 | +} |
| 146 | + |
| 147 | +CDemuxStream* CDVDDemuxAdaptive::GetStream(int streamid) |
| 148 | +{ |
| 149 | + CDASHSession::STREAM *stream(m_session->GetStream(streamid)); |
| 150 | + if (!stream) |
| 151 | + { |
| 152 | + CLog::Log(LOGERROR, "CDVDDemuxAdaptive::GetStream(%d): error getting stream", streamid); |
| 153 | + return nullptr; |
| 154 | + } |
| 155 | + |
| 156 | + return stream->dmuxstrm; |
| 157 | +} |
| 158 | + |
| 159 | +void CDVDDemuxAdaptive::EnableStream(int streamid, bool enable) |
| 160 | +{ |
| 161 | + CLog::Log(LOGDEBUG, "EnableStream(%d: %s)", streamid, enable?"true":"false"); |
| 162 | + |
| 163 | + if (!m_session) |
| 164 | + return; |
| 165 | + |
| 166 | + CDASHSession::STREAM *stream(m_session->GetStream(streamid)); |
| 167 | + if (!stream) |
| 168 | + return; |
| 169 | + |
| 170 | + if (enable) |
| 171 | + { |
| 172 | + if (stream->enabled) |
| 173 | + return; |
| 174 | + |
| 175 | + stream->enabled = true; |
| 176 | + |
| 177 | + stream->stream_.start_stream(~0, m_session->GetWidth(), m_session->GetHeight()); |
| 178 | + const adaptive::AdaptiveTree::Representation *rep(stream->stream_.getRepresentation()); |
| 179 | + CLog::Log(LOGDEBUG, "Selecting stream with conditions: w: %u, h: %u, bw: %u", |
| 180 | + stream->stream_.getWidth(), stream->stream_.getHeight(), stream->stream_.getBandwidth()); |
| 181 | + |
| 182 | + if (!stream->stream_.select_stream(true, false, stream->dmuxstrm->iPhysicalId >> 16)) |
| 183 | + { |
| 184 | + CLog::Log(LOGERROR, "Unable to select stream!"); |
| 185 | + return stream->disable(); |
| 186 | + } |
| 187 | + |
| 188 | + if(rep != stream->stream_.getRepresentation()) |
| 189 | + { |
| 190 | + m_session->UpdateStream(*stream); |
| 191 | + m_session->CheckChange(true); |
| 192 | + } |
| 193 | + |
| 194 | + stream->input_ = new CDASHByteStream(&stream->stream_); |
| 195 | + static const AP4_Track::Type TIDC[adaptive::AdaptiveTree::STREAM_TYPE_COUNT] = { |
| 196 | + AP4_Track::TYPE_UNKNOWN, |
| 197 | + AP4_Track::TYPE_VIDEO, |
| 198 | + AP4_Track::TYPE_AUDIO, |
| 199 | + AP4_Track::TYPE_TEXT }; |
| 200 | + |
| 201 | + AP4_Movie* movie = nullptr; |
| 202 | + if (m_session->GetManifestType() == CDASHSession::MANIFEST_TYPE_ISM && stream->stream_.getRepresentation()->get_initialization() == nullptr) |
| 203 | + { |
| 204 | + //We'll create a Movie out of the things we got from manifest file |
| 205 | + //note: movie will be deleted in destructor of stream->input_file_ |
| 206 | + movie = new AP4_Movie(); |
| 207 | + |
| 208 | + AP4_SyntheticSampleTable* sample_table = new AP4_SyntheticSampleTable(); |
| 209 | + AP4_SampleDescription *sample_descryption = new AP4_SampleDescription(AP4_SampleDescription::TYPE_UNKNOWN, 0, 0); |
| 210 | + if (stream->stream_.getAdaptationSet()->encrypted) |
| 211 | + { |
| 212 | + static const AP4_UI08 default_key[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; |
| 213 | + AP4_ContainerAtom schi(AP4_ATOM_TYPE_SCHI); |
| 214 | + schi.AddChild(new AP4_TencAtom(AP4_CENC_ALGORITHM_ID_CTR, 8, default_key)); |
| 215 | + sample_descryption = new AP4_ProtectedSampleDescription(0, sample_descryption, 0, AP4_PROTECTION_SCHEME_TYPE_PIFF, 0, "", &schi); |
| 216 | + } |
| 217 | + sample_table->AddSampleDescription(sample_descryption); |
| 218 | + |
| 219 | + 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)); |
| 220 | + //Create a dumy MOOV Atom to tell Bento4 its a fragmented stream |
| 221 | + AP4_MoovAtom *moov = new AP4_MoovAtom(); |
| 222 | + moov->AddChild(new AP4_ContainerAtom(AP4_ATOM_TYPE_MVEX)); |
| 223 | + movie->SetMoovAtom(moov); |
| 224 | + } |
| 225 | + |
| 226 | + stream->input_file_ = new AP4_File(*stream->input_, AP4_DefaultAtomFactory::Instance_, true, movie); |
| 227 | + movie = stream->input_file_->GetMovie(); |
| 228 | + if (movie == NULL) |
| 229 | + { |
| 230 | + CLog::Log(LOGERROR, "No MOOV in stream!"); |
| 231 | + return stream->disable(); |
| 232 | + } |
| 233 | + |
| 234 | + AP4_Track *track = movie->GetTrack(TIDC[stream->stream_.get_type()]); |
| 235 | + if (!track) |
| 236 | + { |
| 237 | + CLog::Log(LOGERROR, "No suitable track found in stream"); |
| 238 | + return stream->disable(); |
| 239 | + } |
| 240 | + |
| 241 | + stream->reader_ = new CDASHFragmentedSampleReader(stream->input_, movie, track, streamid, m_session->GetSingleSampleDecryptor(), m_session->GetPresentationTimeOffset()); |
| 242 | + stream->reader_->SetObserver(dynamic_cast<IDASHFragmentObserver*>(m_session.get())); |
| 243 | + |
| 244 | + if (!stream->dmuxstrm->ExtraSize && stream->reader_->GetExtraDataSize()) |
| 245 | + { |
| 246 | + // ExtraData is now available...... |
| 247 | + stream->dmuxstrm->ExtraSize = stream->reader_->GetExtraDataSize(); |
| 248 | + stream->dmuxstrm->ExtraData = (uint8_t*)malloc(stream->dmuxstrm->ExtraSize); |
| 249 | + memcpy((void*)stream->dmuxstrm->ExtraData, stream->reader_->GetExtraData(), stream->dmuxstrm->ExtraSize); |
| 250 | + // Set the session Changed to force new GetStreamInfo call from kodi -> addon |
| 251 | + m_session->CheckChange(true); |
| 252 | + } |
| 253 | + return; |
| 254 | + } |
| 255 | + CLog::Log(LOGDEBUG, ">>>> ERROR"); |
| 256 | + return stream->disable(); |
| 257 | +} |
| 258 | + |
| 259 | +int CDVDDemuxAdaptive::GetStreamLength() |
| 260 | +{ |
| 261 | + if (!m_session) |
| 262 | + return 0; |
| 263 | + |
| 264 | + return static_cast<int>(m_session->GetTotalTime()*1000); |
| 265 | +} |
| 266 | + |
| 267 | +std::string CDVDDemuxAdaptive::GetFileName() |
| 268 | +{ |
| 269 | + if (!m_session) |
| 270 | + return ""; |
| 271 | + |
| 272 | + return m_session->GetUrl(); |
| 273 | +} |
| 274 | + |
| 275 | +void CDVDDemuxAdaptive::GetStreamCodecName(int iStreamId, std::string& strName) |
| 276 | +{ |
| 277 | + strName = ""; |
| 278 | + |
| 279 | + CDASHSession::STREAM *stream(m_session->GetStream(iStreamId)); |
| 280 | + if (stream) |
| 281 | + strName = stream->codecName; |
| 282 | +} |
0 commit comments