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