1 /*
2  * Streams.cpp
3  *****************************************************************************
4  * Copyright (C) 2014 - VideoLAN and VLC authors
5  *
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.
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 Lesser General Public License for more details.
15  *
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  *****************************************************************************/
20 
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24 
25 #include "Streams.hpp"
26 #include "logic/AbstractAdaptationLogic.h"
27 #include "http/HTTPConnection.hpp"
28 #include "http/HTTPConnectionManager.h"
29 #include "playlist/BaseAdaptationSet.h"
30 #include "playlist/BaseRepresentation.h"
31 #include "playlist/SegmentChunk.hpp"
32 #include "plumbing/SourceStream.hpp"
33 #include "plumbing/CommandsQueue.hpp"
34 #include "tools/FormatNamespace.hpp"
35 #include "tools/Debug.hpp"
36 #include <vlc_demux.h>
37 
38 #include <algorithm>
39 
40 using namespace adaptive;
41 using namespace adaptive::http;
42 
AbstractStream(demux_t * demux_)43 AbstractStream::AbstractStream(demux_t * demux_)
44 {
45     p_realdemux = demux_;
46     format = StreamFormat::UNKNOWN;
47     currentChunk = NULL;
48     eof = false;
49     valid = true;
50     disabled = false;
51     discontinuity = false;
52     needrestart = false;
53     inrestart = false;
54     demuxfirstchunk = false;
55     segmentTracker = NULL;
56     demuxersource = NULL;
57     demuxer = NULL;
58     fakeesout = NULL;
59     notfound_sequence = 0;
60     last_buffer_status = buffering_lessthanmin;
61     vlc_mutex_init(&lock);
62 }
63 
init(const StreamFormat & format_,SegmentTracker * tracker,AbstractConnectionManager * conn)64 bool AbstractStream::init(const StreamFormat &format_, SegmentTracker *tracker, AbstractConnectionManager *conn)
65 {
66     /* Don't even try if not supported or already init */
67     if((unsigned)format_ == StreamFormat::UNSUPPORTED || demuxersource)
68         return false;
69 
70     demuxersource = new (std::nothrow) BufferedChunksSourceStream( VLC_OBJECT(p_realdemux), this );
71     if(demuxersource)
72     {
73         CommandsFactory *factory = new (std::nothrow) CommandsFactory();
74         if(factory)
75         {
76             CommandsQueue *commandsqueue = new (std::nothrow) CommandsQueue(factory);
77             if(commandsqueue)
78             {
79                 fakeesout = new (std::nothrow) FakeESOut(p_realdemux->out, commandsqueue);
80                 if(fakeesout)
81                 {
82                     /* All successfull */
83                     fakeesout->setExtraInfoProvider( this );
84                     const Role & streamRole = tracker->getStreamRole();
85                     if(streamRole.isDefault() && streamRole.autoSelectable())
86                         fakeesout->setPriority(ES_PRIORITY_MIN + 10);
87                     else if(!streamRole.autoSelectable())
88                         fakeesout->setPriority(ES_PRIORITY_NOT_DEFAULTABLE);
89                     format = format_;
90                     segmentTracker = tracker;
91                     segmentTracker->registerListener(this);
92                     segmentTracker->notifyBufferingState(true);
93                     connManager = conn;
94                     fakeesout->setExpectedTimestamp(segmentTracker->getPlaybackTime());
95                     declaredCodecs();
96                     return true;
97                 }
98                 delete commandsqueue;
99                 commandsqueue = NULL;
100             }
101             else
102             {
103                 delete factory;
104             }
105         }
106         delete demuxersource;
107     }
108 
109     return false;
110 }
111 
~AbstractStream()112 AbstractStream::~AbstractStream()
113 {
114     delete currentChunk;
115     if(segmentTracker)
116         segmentTracker->notifyBufferingState(false);
117     delete segmentTracker;
118 
119     delete demuxer;
120     delete demuxersource;
121     delete fakeesout;
122 
123     vlc_mutex_destroy(&lock);
124 }
125 
prepareRestart(bool b_discontinuity)126 void AbstractStream::prepareRestart(bool b_discontinuity)
127 {
128     if(demuxer)
129     {
130         /* Enqueue Del Commands for all current ES */
131         demuxer->drain();
132         fakeEsOut()->resetTimestamps();
133         /* Enqueue Del Commands for all current ES */
134         fakeEsOut()->scheduleAllForDeletion();
135         if(b_discontinuity)
136             fakeEsOut()->schedulePCRReset();
137         fakeEsOut()->commandsQueue()->Commit();
138         /* ignoring demuxer's own Del commands */
139         fakeEsOut()->commandsQueue()->setDrop(true);
140         delete demuxer;
141         fakeEsOut()->commandsQueue()->setDrop(false);
142         demuxer = NULL;
143     }
144 }
145 
setLanguage(const std::string & lang)146 void AbstractStream::setLanguage(const std::string &lang)
147 {
148     language = lang;
149 }
150 
setDescription(const std::string & desc)151 void AbstractStream::setDescription(const std::string &desc)
152 {
153     description = desc;
154 }
155 
getPCR() const156 mtime_t AbstractStream::getPCR() const
157 {
158     vlc_mutex_locker locker(const_cast<vlc_mutex_t *>(&lock));
159     if(!valid || disabled)
160         return VLC_TS_INVALID;
161     return fakeEsOut()->commandsQueue()->getPCR();
162 }
163 
getMinAheadTime() const164 mtime_t AbstractStream::getMinAheadTime() const
165 {
166     if(!segmentTracker)
167         return 0;
168     return segmentTracker->getMinAheadTime();
169 }
170 
getFirstDTS() const171 mtime_t AbstractStream::getFirstDTS() const
172 {
173     vlc_mutex_locker locker(const_cast<vlc_mutex_t *>(&lock));
174 
175     if(!valid || disabled)
176         return VLC_TS_INVALID;
177 
178     mtime_t dts = fakeEsOut()->commandsQueue()->getFirstDTS();
179     if(dts == VLC_TS_INVALID)
180         dts = fakeEsOut()->commandsQueue()->getPCR();
181     return dts;
182 }
183 
esCount() const184 int AbstractStream::esCount() const
185 {
186     return fakeEsOut()->esCount();
187 }
188 
seekAble() const189 bool AbstractStream::seekAble() const
190 {
191     bool restarting = fakeEsOut()->restarting();
192     bool draining = fakeEsOut()->commandsQueue()->isDraining();
193     bool eof = fakeEsOut()->commandsQueue()->isEOF();
194 
195     msg_Dbg(p_realdemux, "demuxer %p, fakeesout restarting %d, "
196             "discontinuity %d, commandsqueue draining %d, commandsqueue eof %d",
197             static_cast<void *>(demuxer), restarting, discontinuity, draining, eof);
198 
199     if(!valid || restarting || discontinuity || (!eof && draining))
200     {
201         msg_Warn(p_realdemux, "not seekable");
202         return false;
203     }
204     else
205     {
206         return true;
207     }
208 }
209 
isSelected() const210 bool AbstractStream::isSelected() const
211 {
212     return fakeEsOut()->hasSelectedEs();
213 }
214 
reactivate(mtime_t basetime)215 bool AbstractStream::reactivate(mtime_t basetime)
216 {
217     vlc_mutex_locker locker(&lock);
218     if(setPosition(basetime, false))
219     {
220         setDisabled(false);
221         return true;
222     }
223     else
224     {
225         eof = true; /* can't reactivate */
226         return false;
227     }
228 }
229 
startDemux()230 bool AbstractStream::startDemux()
231 {
232     if(demuxer)
233         return false;
234 
235     demuxersource->Reset();
236     demuxer = createDemux(format);
237     if(!demuxer && format != StreamFormat())
238         msg_Err(p_realdemux, "Failed to create demuxer %p %s", (void *)demuxer,
239                 format.str().c_str());
240     demuxfirstchunk = true;
241 
242     return !!demuxer;
243 }
244 
restartDemux()245 bool AbstractStream::restartDemux()
246 {
247     bool b_ret = true;
248     if(!demuxer)
249     {
250         fakeesout->recycleAll();
251         b_ret = startDemux();
252     }
253     else if(demuxer->needsRestartOnSeek())
254     {
255         inrestart = true;
256         /* Push all ES as recycling candidates */
257         fakeEsOut()->recycleAll();
258         /* Restart with ignoring es_Del pushes to queue when terminating demux */
259         fakeEsOut()->commandsQueue()->setDrop(true);
260         demuxer->destroy();
261         fakeEsOut()->commandsQueue()->setDrop(false);
262         b_ret = demuxer->create();
263         inrestart = false;
264     }
265     else
266     {
267         fakeEsOut()->commandsQueue()->Commit();
268     }
269     return b_ret;
270 }
271 
setDisabled(bool b)272 void AbstractStream::setDisabled(bool b)
273 {
274     if(disabled != b)
275         segmentTracker->notifyBufferingState(!b);
276     disabled = b;
277 }
278 
isValid() const279 bool AbstractStream::isValid() const
280 {
281     vlc_mutex_locker locker(const_cast<vlc_mutex_t *>(&lock));
282     return valid;
283 }
284 
isDisabled() const285 bool AbstractStream::isDisabled() const
286 {
287     vlc_mutex_locker locker(const_cast<vlc_mutex_t *>(&lock));
288     return disabled;
289 }
290 
decodersDrained()291 bool AbstractStream::decodersDrained()
292 {
293     return fakeEsOut()->decodersDrained();
294 }
295 
getLastBufferStatus() const296 AbstractStream::buffering_status AbstractStream::getLastBufferStatus() const
297 {
298     return last_buffer_status;
299 }
300 
getDemuxedAmount(mtime_t from) const301 mtime_t AbstractStream::getDemuxedAmount(mtime_t from) const
302 {
303     return fakeEsOut()->commandsQueue()->getDemuxedAmount(from);
304 }
305 
bufferize(mtime_t nz_deadline,unsigned i_min_buffering,unsigned i_extra_buffering)306 AbstractStream::buffering_status AbstractStream::bufferize(mtime_t nz_deadline,
307                                                            unsigned i_min_buffering, unsigned i_extra_buffering)
308 {
309     last_buffer_status = doBufferize(nz_deadline, i_min_buffering, i_extra_buffering);
310     return last_buffer_status;
311 }
312 
doBufferize(mtime_t nz_deadline,mtime_t i_min_buffering,mtime_t i_extra_buffering)313 AbstractStream::buffering_status AbstractStream::doBufferize(mtime_t nz_deadline,
314                                                              mtime_t i_min_buffering, mtime_t i_extra_buffering)
315 {
316     vlc_mutex_lock(&lock);
317 
318     /* Ensure it is configured */
319     if(!segmentTracker || !connManager || !valid)
320     {
321         vlc_mutex_unlock(&lock);
322         return AbstractStream::buffering_end;
323     }
324 
325     /* Disable streams that are not selected (alternate streams) */
326     if(esCount() && !isSelected() && !fakeEsOut()->restarting())
327     {
328         setDisabled(true);
329         segmentTracker->reset();
330         fakeEsOut()->commandsQueue()->Abort(false);
331         msg_Dbg(p_realdemux, "deactivating %s stream %s",
332                 format.str().c_str(), description.c_str());
333         vlc_mutex_unlock(&lock);
334         return AbstractStream::buffering_end;
335     }
336 
337     if(fakeEsOut()->commandsQueue()->isDraining())
338     {
339         vlc_mutex_unlock(&lock);
340         return AbstractStream::buffering_suspended;
341     }
342 
343     segmentTracker->setStartPosition();
344 
345     /* Reached end of live playlist */
346     if(!segmentTracker->bufferingAvailable())
347     {
348         vlc_mutex_unlock(&lock);
349         return AbstractStream::buffering_suspended;
350     }
351 
352     if(!demuxer)
353     {
354         format = segmentTracker->getCurrentFormat();
355         if(!startDemux())
356         {
357             /* If demux fails because of probing failure / wrong format*/
358             if(discontinuity)
359             {
360                 msg_Dbg( p_realdemux, "Draining on format change" );
361                 prepareRestart();
362                 discontinuity = false;
363                 fakeEsOut()->commandsQueue()->setDraining();
364                 vlc_mutex_unlock(&lock);
365                 return AbstractStream::buffering_ongoing;
366             }
367             valid = false; /* Prevent further retries */
368             fakeEsOut()->commandsQueue()->setEOF(true);
369             vlc_mutex_unlock(&lock);
370             return AbstractStream::buffering_end;
371         }
372     }
373 
374     const int64_t i_total_buffering = i_min_buffering + i_extra_buffering;
375 
376     mtime_t i_demuxed = fakeEsOut()->commandsQueue()->getDemuxedAmount(nz_deadline);
377     segmentTracker->notifyBufferingLevel(i_min_buffering, i_demuxed, i_total_buffering);
378     if(i_demuxed < i_total_buffering) /* not already demuxed */
379     {
380         mtime_t nz_extdeadline = fakeEsOut()->commandsQueue()->getBufferingLevel() +
381                                     (i_total_buffering - i_demuxed) / 4;
382         nz_deadline = std::max(nz_deadline, nz_extdeadline);
383 
384         /* need to read, demuxer still buffering, ... */
385         vlc_mutex_unlock(&lock);
386         Demuxer::Status demuxStatus = demuxer->demux(nz_deadline);
387         vlc_mutex_lock(&lock);
388         if(demuxStatus != Demuxer::Status::STATUS_SUCCESS)
389         {
390             if(discontinuity || needrestart)
391             {
392                 msg_Dbg(p_realdemux, "Restarting demuxer");
393                 prepareRestart(discontinuity);
394                 if(discontinuity)
395                 {
396                     msg_Dbg(p_realdemux, "Draining on discontinuity");
397                     fakeEsOut()->commandsQueue()->setDraining();
398                     discontinuity = false;
399                 }
400                 needrestart = false;
401                 vlc_mutex_unlock(&lock);
402                 return AbstractStream::buffering_ongoing;
403             }
404             fakeEsOut()->commandsQueue()->setEOF(true);
405             vlc_mutex_unlock(&lock);
406             return AbstractStream::buffering_end;
407         }
408         i_demuxed = fakeEsOut()->commandsQueue()->getDemuxedAmount(nz_deadline);
409         segmentTracker->notifyBufferingLevel(i_min_buffering, i_demuxed, i_total_buffering);
410     }
411     vlc_mutex_unlock(&lock);
412 
413     if(i_demuxed < i_total_buffering) /* need to read more */
414     {
415         if(i_demuxed < i_min_buffering)
416             return AbstractStream::buffering_lessthanmin; /* high prio */
417         return AbstractStream::buffering_ongoing;
418     }
419     return AbstractStream::buffering_full;
420 }
421 
dequeue(mtime_t nz_deadline,mtime_t * pi_pcr)422 AbstractStream::status AbstractStream::dequeue(mtime_t nz_deadline, mtime_t *pi_pcr)
423 {
424     vlc_mutex_locker locker(&lock);
425 
426     *pi_pcr = nz_deadline;
427 
428     if(fakeEsOut()->commandsQueue()->isDraining())
429     {
430         AdvDebug(mtime_t pcrvalue = fakeEsOut()->commandsQueue()->getPCR();
431                  mtime_t dtsvalue = fakeEsOut()->commandsQueue()->getFirstDTS();
432                  msg_Dbg(p_realdemux, "Stream %s pcr %" PRId64 " dts %" PRId64 " deadline %" PRId64 " [DRAINING]",
433                          description.c_str(), pcrvalue, dtsvalue, nz_deadline));
434 
435         *pi_pcr = fakeEsOut()->commandsQueue()->Process(p_realdemux->out, VLC_TS_0 + nz_deadline);
436         if(!fakeEsOut()->commandsQueue()->isEmpty())
437             return AbstractStream::status_demuxed;
438 
439         if(!fakeEsOut()->commandsQueue()->isEOF())
440         {
441             fakeEsOut()->commandsQueue()->Abort(true); /* reset buffering level and flags */
442             return AbstractStream::status_discontinuity;
443         }
444     }
445 
446     if(!valid || disabled || fakeEsOut()->commandsQueue()->isEOF())
447     {
448         *pi_pcr = nz_deadline;
449         return AbstractStream::status_eof;
450     }
451 
452     mtime_t bufferingLevel = fakeEsOut()->commandsQueue()->getBufferingLevel();
453 
454     AdvDebug(mtime_t pcrvalue = fakeEsOut()->commandsQueue()->getPCR();
455              mtime_t dtsvalue = fakeEsOut()->commandsQueue()->getFirstDTS();
456              msg_Dbg(p_realdemux, "Stream %s pcr %" PRId64 " dts %" PRId64 " deadline %" PRId64 " buflevel %" PRId64,
457                      description.c_str(), pcrvalue, dtsvalue, nz_deadline, bufferingLevel));
458 
459     if(nz_deadline + VLC_TS_0 <= bufferingLevel) /* demuxed */
460     {
461         *pi_pcr = fakeEsOut()->commandsQueue()->Process( p_realdemux->out, VLC_TS_0 + nz_deadline );
462         return AbstractStream::status_demuxed;
463     }
464 
465     return AbstractStream::status_buffering;
466 }
467 
getContentType()468 std::string AbstractStream::getContentType()
469 {
470     if (currentChunk == NULL && !eof)
471     {
472         const bool b_restarting = fakeEsOut()->restarting();
473         currentChunk = segmentTracker->getNextChunk(!b_restarting, connManager);
474     }
475     if(currentChunk)
476         return currentChunk->getContentType();
477     else
478         return std::string();
479 }
480 
readNextBlock()481 block_t * AbstractStream::readNextBlock()
482 {
483     if (currentChunk == NULL && !eof)
484     {
485         const bool b_restarting = fakeEsOut()->restarting();
486         currentChunk = segmentTracker->getNextChunk(!b_restarting, connManager);
487     }
488 
489     if(discontinuity && demuxfirstchunk)
490     {
491         /* clear up discontinuity on demux start (discontinuity on start segment bug) */
492         discontinuity = false;
493     }
494 
495     if(discontinuity || needrestart)
496     {
497         msg_Info(p_realdemux, "Encountered discontinuity");
498         /* Force stream/demuxer to end for this call */
499         return NULL;
500     }
501 
502     if(currentChunk == NULL)
503     {
504         eof = true;
505         return NULL;
506     }
507 
508     const bool b_segment_head_chunk = (currentChunk->getBytesRead() == 0);
509 
510     block_t *block = currentChunk->readBlock();
511     if(block == NULL)
512     {
513         if(currentChunk->getRequestStatus() == RequestStatus::NotFound &&
514            ++notfound_sequence < 3)
515         {
516             discontinuity = true;
517         }
518         delete currentChunk;
519         currentChunk = NULL;
520         return NULL;
521     }
522     else notfound_sequence = 0;
523 
524     demuxfirstchunk = false;
525 
526     if (currentChunk->isEmpty())
527     {
528         delete currentChunk;
529         currentChunk = NULL;
530     }
531 
532     block = checkBlock(block, b_segment_head_chunk);
533 
534     return block;
535 }
536 
setPosition(mtime_t time,bool tryonly)537 bool AbstractStream::setPosition(mtime_t time, bool tryonly)
538 {
539     if(!seekAble())
540         return false;
541 
542     bool b_needs_restart = demuxer ? demuxer->needsRestartOnSeek() : true;
543     bool ret = segmentTracker->setPositionByTime(time, b_needs_restart, tryonly);
544     if(!tryonly && ret)
545     {
546         // clear eof flag before restartDemux() to prevent readNextBlock() fail
547         eof = false;
548         demuxfirstchunk = true;
549         notfound_sequence = 0;
550         if(b_needs_restart)
551         {
552             if(currentChunk)
553                 delete currentChunk;
554             currentChunk = NULL;
555             needrestart = false;
556 
557             fakeEsOut()->resetTimestamps();
558 
559             mtime_t seekMediaTime = segmentTracker->getPlaybackTime(true);
560             fakeEsOut()->setExpectedTimestamp(seekMediaTime);
561             if( !restartDemux() )
562             {
563                 msg_Info(p_realdemux, "Restart demux failed");
564                 eof = true;
565                 valid = false;
566                 ret = false;
567             }
568             else
569             {
570                 fakeEsOut()->commandsQueue()->setEOF(false);
571             }
572         }
573         else fakeEsOut()->commandsQueue()->Abort( true );
574 
575 // in some cases, media time seek != sent dts
576 //        es_out_Control(p_realdemux->out, ES_OUT_SET_NEXT_DISPLAY_TIME,
577 //                       VLC_TS_0 + time);
578     }
579     return ret;
580 }
581 
getMediaPlaybackTimes(mtime_t * start,mtime_t * end,mtime_t * length,mtime_t * mediaStart,mtime_t * demuxStart) const582 bool AbstractStream::getMediaPlaybackTimes(mtime_t *start, mtime_t *end,
583                                            mtime_t *length,
584                                            mtime_t *mediaStart,
585                                            mtime_t *demuxStart) const
586 {
587     return (segmentTracker->getMediaPlaybackRange(start, end, length) &&
588             fakeEsOut()->getStartTimestamps(mediaStart, demuxStart));
589 }
590 
runUpdates()591 void AbstractStream::runUpdates()
592 {
593     if(valid && !disabled)
594         segmentTracker->updateSelected();
595 }
596 
fillExtraFMTInfo(es_format_t * p_fmt) const597 void AbstractStream::fillExtraFMTInfo( es_format_t *p_fmt ) const
598 {
599     if(!p_fmt->psz_language && !language.empty())
600         p_fmt->psz_language = strdup(language.c_str());
601     if(!p_fmt->psz_description && !description.empty())
602         p_fmt->psz_description = strdup(description.c_str());
603 }
604 
createDemux(const StreamFormat & format)605 AbstractDemuxer * AbstractStream::createDemux(const StreamFormat &format)
606 {
607     AbstractDemuxer *ret = newDemux( VLC_OBJECT(p_realdemux), format,
608                                      (es_out_t *)fakeEsOut(), demuxersource );
609     if(ret && !ret->create())
610     {
611         delete ret;
612         ret = NULL;
613     }
614     else fakeEsOut()->commandsQueue()->Commit();
615 
616     return ret;
617 }
618 
newDemux(vlc_object_t * p_obj,const StreamFormat & format,es_out_t * out,AbstractSourceStream * source) const619 AbstractDemuxer *AbstractStream::newDemux(vlc_object_t *p_obj, const StreamFormat &format,
620                                           es_out_t *out, AbstractSourceStream *source) const
621 {
622     AbstractDemuxer *ret = NULL;
623     switch((unsigned)format)
624     {
625         case StreamFormat::MP4:
626             ret = new Demuxer(p_obj, "mp4", out, source);
627             break;
628 
629         case StreamFormat::MPEG2TS:
630             ret = new Demuxer(p_obj, "ts", out, source);
631             break;
632 
633         default:
634         case StreamFormat::UNSUPPORTED:
635             break;
636     }
637     return ret;
638 }
639 
trackerEvent(const SegmentTrackerEvent & event)640 void AbstractStream::trackerEvent(const SegmentTrackerEvent &event)
641 {
642     switch(event.type)
643     {
644         case SegmentTrackerEvent::DISCONTINUITY:
645             discontinuity = true;
646             break;
647 
648         case SegmentTrackerEvent::FORMATCHANGE:
649             /* Check if our current demux is still valid */
650             if(*event.u.format.f != format || format == StreamFormat(StreamFormat::UNKNOWN))
651             {
652                 /* Format has changed between segments, we need to drain and change demux */
653                 msg_Info(p_realdemux, "Changing stream format %s -> %s",
654                          format.str().c_str(), event.u.format.f->str().c_str());
655                 format = *event.u.format.f;
656 
657                 /* This is an implict discontinuity */
658                 discontinuity = true;
659             }
660             break;
661 
662         case SegmentTrackerEvent::SWITCHING:
663             if(demuxer && !inrestart)
664             {
665                 if(!demuxer->bitstreamSwitchCompatible() ||
666                    (event.u.switching.next &&
667                    !event.u.switching.next->getAdaptationSet()->isBitSwitchable()))
668                     needrestart = true;
669             }
670             break;
671 
672         case SegmentTrackerEvent::SEGMENT_CHANGE:
673             if(demuxer && demuxer->needsRestartOnEachSegment() && !inrestart)
674             {
675                 needrestart = true;
676             }
677             break;
678 
679         default:
680             break;
681     }
682 }
683 
add_codec_string_from_fourcc(vlc_fourcc_t fourcc,std::list<std::string> & codecs)684 static void add_codec_string_from_fourcc(vlc_fourcc_t fourcc,
685                                          std::list<std::string> &codecs)
686 {
687     std::string codec;
688     codec.insert(0, reinterpret_cast<const char *>(&fourcc), 4);
689     codecs.push_back(codec);
690 }
691 
declaredCodecs()692 void AbstractStream::declaredCodecs()
693 {
694     const std::string & streamDesc = segmentTracker->getStreamDescription();
695     const std::string & streamLang = segmentTracker->getStreamLanguage();
696     std::list<std::string> codecs =  segmentTracker->getCurrentCodecs();
697 
698     if(codecs.empty())
699     {
700         const StreamFormat format = segmentTracker->getCurrentFormat();
701         switch(format)
702         {
703             case StreamFormat::TTML:
704                 add_codec_string_from_fourcc(VLC_CODEC_TTML, codecs);
705                 break;
706             case StreamFormat::WEBVTT:
707                 add_codec_string_from_fourcc(VLC_CODEC_WEBVTT, codecs);
708                 break;
709             default:
710                 break;
711         }
712     }
713 
714     for(std::list<std::string>::const_iterator it = codecs.begin();
715                                                it != codecs.end(); ++it)
716     {
717         FormatNamespace fnsp(*it);
718 
719         es_format_t fmt;
720         es_format_Init(&fmt, fnsp.getFmt()->i_cat, fnsp.getFmt()->i_codec);
721         es_format_Copy(&fmt, fnsp.getFmt());
722 
723         if(!streamLang.empty())
724             fmt.psz_language = strdup(streamLang.c_str());
725         if(!streamDesc.empty())
726             fmt.psz_description = strdup(streamDesc.c_str());
727 
728         fakeEsOut()->declareEs( &fmt );
729 
730         es_format_Clean(&fmt);
731     }
732 }
733 
fakeEsOut()734 FakeESOut::LockedFakeEsOut AbstractStream::fakeEsOut()
735 {
736     return fakeesout->WithLock();
737 }
738 
fakeEsOut() const739 FakeESOut::LockedFakeEsOut AbstractStream::fakeEsOut() const
740 {
741     return fakeesout->WithLock();
742 }
743