3 *****************************************************************************
4 * Copyright (C) 2014 - VideoLAN and VLC authors
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published
8 * by the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
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 Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
25 #include "Streams.hpp"
26 #include "logic/AbstractAdaptationLogic.h"
27 #include "http/HTTPConnection.hpp"
28 #include "http/HTTPConnectionManager.h"
29 #include "playlist/BaseRepresentation.h"
30 #include "playlist/SegmentChunk.hpp"
31 #include "plumbing/SourceStream.hpp"
32 #include "plumbing/CommandsQueue.hpp"
33 #include "tools/Debug.hpp"
34 #include <vlc_demux.h>
38 using namespace adaptive
;
39 using namespace adaptive::http
;
41 AbstractStream::AbstractStream(demux_t
* demux_
)
44 format
= StreamFormat::UNSUPPORTED
;
49 discontinuity
= false;
52 segmentTracker
= NULL
;
57 last_buffer_status
= buffering_lessthanmin
;
58 vlc_mutex_init(&lock
);
61 bool AbstractStream::init(const StreamFormat
&format_
, SegmentTracker
*tracker
, AbstractConnectionManager
*conn
)
63 /* Don't even try if not supported or already init */
64 if((unsigned)format_
== StreamFormat::UNSUPPORTED
|| demuxersource
)
67 demuxersource
= new (std::nothrow
) ChunksSourceStream( VLC_OBJECT(p_realdemux
), this );
70 CommandsFactory
*factory
= new (std::nothrow
) CommandsFactory();
73 commandsqueue
= new (std::nothrow
) CommandsQueue(factory
);
76 fakeesout
= new (std::nothrow
) FakeESOut(p_realdemux
->out
, commandsqueue
);
80 fakeesout
->setExtraInfoProvider( this );
82 segmentTracker
= tracker
;
83 segmentTracker
->registerListener(this);
84 segmentTracker
->notifyBufferingState(true);
102 AbstractStream::~AbstractStream()
106 segmentTracker
->notifyBufferingState(false);
107 delete segmentTracker
;
110 delete demuxersource
;
112 delete commandsqueue
;
114 vlc_mutex_destroy(&lock
);
117 void AbstractStream::prepareRestart(bool b_discontinuity
)
121 /* Enqueue Del Commands for all current ES */
124 /* Enqueue Del Commands for all current ES */
125 fakeesout
->scheduleAllForDeletion();
127 fakeesout
->schedulePCRReset();
128 commandsqueue
->Commit();
129 /* ignoring demuxer's own Del commands */
130 commandsqueue
->setDrop(true);
132 commandsqueue
->setDrop(false);
137 void AbstractStream::setLanguage(const std::string
&lang
)
142 void AbstractStream::setDescription(const std::string
&desc
)
147 mtime_t
AbstractStream::getPCR() const
149 vlc_mutex_lock(const_cast<vlc_mutex_t
*>(&lock
));
150 mtime_t pcr
= isDisabled() ? VLC_TS_INVALID
: commandsqueue
->getPCR();
151 vlc_mutex_unlock(const_cast<vlc_mutex_t
*>(&lock
));
155 mtime_t
AbstractStream::getMinAheadTime() const
159 return segmentTracker
->getMinAheadTime();
162 mtime_t
AbstractStream::getFirstDTS() const
165 vlc_mutex_lock(const_cast<vlc_mutex_t
*>(&lock
));
168 dts
= VLC_TS_INVALID
;
172 dts
= commandsqueue
->getFirstDTS();
173 if(dts
== VLC_TS_INVALID
)
174 dts
= commandsqueue
->getPCR();
176 vlc_mutex_unlock(const_cast<vlc_mutex_t
*>(&lock
));
180 int AbstractStream::esCount() const
182 return fakeesout
->esCount();
185 bool AbstractStream::seekAble() const
188 !fakeesout
->restarting() &&
190 !commandsqueue
->isDraining() );
193 bool AbstractStream::isSelected() const
195 return fakeesout
->hasSelectedEs();
198 bool AbstractStream::reactivate(mtime_t basetime
)
200 if(setPosition(basetime
, false))
207 eof
= true; /* can't reactivate */
212 bool AbstractStream::startDemux()
217 demuxersource
->Reset();
218 demuxer
= createDemux(format
);
219 if(!demuxer
&& format
!= StreamFormat())
220 msg_Err(p_realdemux
, "Failed to create demuxer %p %s", (void *)demuxer
,
221 format
.str().c_str());
226 bool AbstractStream::restartDemux()
231 b_ret
= startDemux();
233 else if(demuxer
->needsRestartOnSeek())
236 /* Push all ES as recycling candidates */
237 fakeesout
->recycleAll();
238 /* Restart with ignoring es_Del pushes to queue when terminating demux */
239 commandsqueue
->setDrop(true);
241 commandsqueue
->setDrop(false);
242 b_ret
= demuxer
->create();
247 commandsqueue
->Commit();
252 void AbstractStream::setDisabled(bool b
)
255 segmentTracker
->notifyBufferingState(!b
);
259 bool AbstractStream::isDisabled() const
261 return dead
|| disabled
;
264 bool AbstractStream::canActivate() const
269 bool AbstractStream::decodersDrained()
271 return fakeesout
->decodersDrained();
274 AbstractStream::buffering_status
AbstractStream::getLastBufferStatus() const
276 return last_buffer_status
;
279 mtime_t
AbstractStream::getDemuxedAmount() const
281 return commandsqueue
->getDemuxedAmount();
284 AbstractStream::buffering_status
AbstractStream::bufferize(mtime_t nz_deadline
,
285 unsigned i_min_buffering
, unsigned i_extra_buffering
)
287 last_buffer_status
= doBufferize(nz_deadline
, i_min_buffering
, i_extra_buffering
);
288 return last_buffer_status
;
291 AbstractStream::buffering_status
AbstractStream::doBufferize(mtime_t nz_deadline
,
292 unsigned i_min_buffering
, unsigned i_extra_buffering
)
294 vlc_mutex_lock(&lock
);
296 /* Ensure it is configured */
297 if(!segmentTracker
|| !connManager
|| dead
)
299 vlc_mutex_unlock(&lock
);
300 return AbstractStream::buffering_end
;
303 /* Disable streams that are not selected (alternate streams) */
304 if(esCount() && !isSelected() && !fakeesout
->restarting())
307 segmentTracker
->reset();
308 commandsqueue
->Abort(false);
309 msg_Dbg(p_realdemux
, "deactivating stream %s", format
.str().c_str());
310 vlc_mutex_unlock(&lock
);
311 return AbstractStream::buffering_end
;
314 if(commandsqueue
->isDraining())
316 vlc_mutex_unlock(&lock
);
317 return AbstractStream::buffering_suspended
;
322 format
= segmentTracker
->getCurrentFormat();
325 /* If demux fails because of probing failure / wrong format*/
328 msg_Dbg( p_realdemux
, "Draining on format change" );
330 discontinuity
= false;
331 commandsqueue
->setDraining();
332 vlc_mutex_unlock(&lock
);
333 return AbstractStream::buffering_ongoing
;
335 dead
= true; /* Prevent further retries */
336 commandsqueue
->setEOF();
337 vlc_mutex_unlock(&lock
);
338 return AbstractStream::buffering_end
;
342 const int64_t i_total_buffering
= i_min_buffering
+ i_extra_buffering
;
344 mtime_t i_demuxed
= commandsqueue
->getDemuxedAmount();
345 segmentTracker
->notifyBufferingLevel(i_min_buffering
, i_demuxed
, i_total_buffering
);
346 if(i_demuxed
< i_total_buffering
) /* not already demuxed */
348 if(!segmentTracker
->segmentsListReady()) /* Live Streams */
350 vlc_mutex_unlock(&lock
);
351 return AbstractStream::buffering_suspended
;
354 mtime_t nz_extdeadline
= commandsqueue
->getBufferingLevel() +
355 (i_total_buffering
- commandsqueue
->getDemuxedAmount()) / (CLOCK_FREQ
/4);
356 nz_deadline
= std::max(nz_deadline
, nz_extdeadline
);
358 /* need to read, demuxer still buffering, ... */
359 vlc_mutex_unlock(&lock
);
360 int i_ret
= demuxer
->demux(nz_deadline
);
361 vlc_mutex_lock(&lock
);
362 if(i_ret
!= VLC_DEMUXER_SUCCESS
)
364 if(discontinuity
|| needrestart
)
366 msg_Dbg(p_realdemux
, "Restarting demuxer");
367 prepareRestart(discontinuity
);
370 msg_Dbg(p_realdemux
, "Draining on discontinuity");
371 commandsqueue
->setDraining();
372 discontinuity
= false;
375 vlc_mutex_unlock(&lock
);
376 return AbstractStream::buffering_ongoing
;
378 commandsqueue
->setEOF();
379 vlc_mutex_unlock(&lock
);
380 return AbstractStream::buffering_end
;
382 i_demuxed
= commandsqueue
->getDemuxedAmount();
383 segmentTracker
->notifyBufferingLevel(i_min_buffering
, i_demuxed
, i_total_buffering
);
385 vlc_mutex_unlock(&lock
);
387 if(i_demuxed
< i_total_buffering
) /* need to read more */
389 if(i_demuxed
< i_min_buffering
)
390 return AbstractStream::buffering_lessthanmin
; /* high prio */
391 return AbstractStream::buffering_ongoing
;
393 return AbstractStream::buffering_full
;
396 AbstractStream::status
AbstractStream::dequeue(mtime_t nz_deadline
, mtime_t
*pi_pcr
)
398 vlc_mutex_locker
locker(&lock
);
400 *pi_pcr
= nz_deadline
;
402 if(commandsqueue
->isDraining())
404 AdvDebug(msg_Dbg(p_realdemux
, "Stream %s pcr %" PRId64
" dts %" PRId64
" deadline %" PRId64
" [DRAINING]",
405 description
.c_str(), commandsqueue
->getPCR(), commandsqueue
->getFirstDTS(),
408 *pi_pcr
= commandsqueue
->Process(p_realdemux
->out
, VLC_TS_0
+ nz_deadline
);
409 if(!commandsqueue
->isEmpty())
410 return AbstractStream::status_demuxed
;
412 if(!commandsqueue
->isEOF())
414 commandsqueue
->Abort(true); /* reset buffering level and flags */
415 return AbstractStream::status_discontinuity
;
419 if(isDisabled() || commandsqueue
->isEOF())
421 *pi_pcr
= nz_deadline
;
422 return AbstractStream::status_eof
;
425 AdvDebug(msg_Dbg(p_realdemux
, "Stream %s pcr %" PRId64
" dts %" PRId64
" deadline %" PRId64
" buflevel %" PRId64
,
426 description
.c_str(), commandsqueue
->getPCR(), commandsqueue
->getFirstDTS(),
427 nz_deadline
, commandsqueue
->getBufferingLevel()));
429 if(nz_deadline
+ VLC_TS_0
<= commandsqueue
->getBufferingLevel()) /* demuxed */
431 *pi_pcr
= commandsqueue
->Process( p_realdemux
->out
, VLC_TS_0
+ nz_deadline
);
432 return AbstractStream::status_demuxed
;
435 return AbstractStream::status_buffering
;
438 block_t
* AbstractStream::readNextBlock()
440 if (currentChunk
== NULL
&& !eof
)
441 currentChunk
= segmentTracker
->getNextChunk(!fakeesout
->restarting(), connManager
);
443 if(discontinuity
|| needrestart
)
445 msg_Info(p_realdemux
, "Encountered discontinuity");
446 /* Force stream/demuxer to end for this call */
450 if(currentChunk
== NULL
)
456 const bool b_segment_head_chunk
= (currentChunk
->getBytesRead() == 0);
458 block_t
*block
= currentChunk
->readBlock();
466 if (currentChunk
->isEmpty())
472 block
= checkBlock(block
, b_segment_head_chunk
);
477 bool AbstractStream::setPosition(mtime_t time
, bool tryonly
)
482 bool ret
= segmentTracker
->setPositionByTime(time
, demuxer
->needsRestartOnSeek(), tryonly
);
485 if(demuxer
->needsRestartOnSeek())
493 setTimeOffset(segmentTracker
->getPlaybackTime());
495 if( !restartDemux() )
498 else commandsqueue
->Abort( true );
500 es_out_Control(p_realdemux
->out
, ES_OUT_SET_NEXT_DISPLAY_TIME
,
506 mtime_t
AbstractStream::getPlaybackTime() const
508 return segmentTracker
->getPlaybackTime();
511 void AbstractStream::runUpdates()
514 segmentTracker
->updateSelected();
517 void AbstractStream::fillExtraFMTInfo( es_format_t
*p_fmt
) const
519 if(!p_fmt
->psz_language
&& !language
.empty())
520 p_fmt
->psz_language
= strdup(language
.c_str());
521 if(!p_fmt
->psz_description
&& !description
.empty())
522 p_fmt
->psz_description
= strdup(description
.c_str());
525 void AbstractStream::setTimeOffset(mtime_t i_offset
)
527 /* Check if we need to set an offset as the demuxer
528 * will start from zero from seek point */
529 if(i_offset
< 0) /* reset */
531 fakeesout
->setExpectedTimestampOffset(0);
535 fakeesout
->setExpectedTimestampOffset(i_offset
);
539 void AbstractStream::trackerEvent(const SegmentTrackerEvent
&event
)
543 case SegmentTrackerEvent::DISCONTINUITY
:
544 discontinuity
= true;
547 case SegmentTrackerEvent::FORMATCHANGE
:
548 /* Check if our current demux is still valid */
549 if(*event
.u
.format
.f
!= format
)
551 /* Format has changed between segments, we need to drain and change demux */
552 msg_Info(p_realdemux
, "Changing stream format %s -> %s",
553 format
.str().c_str(), event
.u
.format
.f
->str().c_str());
554 format
= *event
.u
.format
.f
;
556 /* This is an implict discontinuity */
557 discontinuity
= true;
561 case SegmentTrackerEvent::SWITCHING
:
562 if(demuxer
&& demuxer
->needsRestartOnSwitch() && !inrestart
)