]> git.sesse.net Git - vlc/blob - modules/demux/dash/Streams.cpp
demux: dash: add basic seeking using next segment
[vlc] / modules / demux / dash / Streams.cpp
1 /*
2  * Streams.cpp
3  *****************************************************************************
4  * Copyright (C) 2014 - VideoLAN 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 #define __STDC_CONSTANT_MACROS
21 #include "Streams.hpp"
22 #include "adaptationlogic/AbstractAdaptationLogic.h"
23 #include "adaptationlogic/AdaptationLogicFactory.h"
24 #include "SegmentTracker.hpp"
25 #include <vlc_stream.h>
26 #include <vlc_demux.h>
27
28 using namespace dash::Streams;
29 using namespace dash::http;
30 using namespace dash::logic;
31
32 Stream::Stream(const std::string &mime)
33 {
34     init(mimeToType(mime), mimeToFormat(mime));
35 }
36
37 Stream::Stream(const Type type, const Format format)
38 {
39     init(type, format);
40 }
41
42 void Stream::init(const Type type_, const Format format_)
43 {
44     type = type_;
45     format = format_;
46     output = NULL;
47     adaptationLogic = NULL;
48     currentChunk = NULL;
49     eof = false;
50     segmentTracker = NULL;
51 }
52
53 Stream::~Stream()
54 {
55     delete currentChunk;
56     delete adaptationLogic;
57     delete output;
58     delete segmentTracker;
59 }
60
61 Type Stream::mimeToType(const std::string &mime)
62 {
63     Type mimetype;
64     if (!mime.compare(0, 6, "video/"))
65         mimetype = Streams::VIDEO;
66     else if (!mime.compare(0, 6, "audio/"))
67         mimetype = Streams::AUDIO;
68     else if (!mime.compare(0, 12, "application/"))
69         mimetype = Streams::APPLICATION;
70     else /* unknown of unsupported */
71         mimetype = Streams::UNKNOWN;
72     return mimetype;
73 }
74
75 Format Stream::mimeToFormat(const std::string &mime)
76 {
77     Format format = Streams::UNSUPPORTED;
78     std::string::size_type pos = mime.find("/");
79     if(pos != std::string::npos)
80     {
81         std::string tail = mime.substr(pos + 1);
82         if(tail == "mp4")
83             format = Streams::MP4;
84         else if (tail == "mp2t")
85             format = Streams::MPEG2TS;
86     }
87     return format;
88 }
89
90 void Stream::create(demux_t *demux, AbstractAdaptationLogic *logic, SegmentTracker *tracker)
91 {
92     switch(format)
93     {
94         case Streams::MP4:
95             output = new MP4StreamOutput(demux);
96             break;
97         case Streams::MPEG2TS:
98             output = new MPEG2TSStreamOutput(demux);
99             break;
100         default:
101             throw VLC_EBADVAR;
102             break;
103     }
104     adaptationLogic = logic;
105     segmentTracker = tracker;
106 }
107
108 bool Stream::isEOF() const
109 {
110     return false;
111 }
112
113 mtime_t Stream::getPCR() const
114 {
115     return output->getPCR();
116 }
117
118 int Stream::getGroup() const
119 {
120     return output->getGroup();
121 }
122
123 int Stream::esCount() const
124 {
125     return output->esCount();
126 }
127
128 bool Stream::operator ==(const Stream &stream) const
129 {
130     return stream.type == type;
131 }
132
133 Chunk * Stream::getChunk()
134 {
135     if (currentChunk == NULL)
136     {
137         currentChunk = segmentTracker->getNextChunk(type);
138         if (currentChunk == NULL)
139             eof = true;
140     }
141     return currentChunk;
142 }
143
144 bool Stream::seekAble() const
145 {
146     return (output && output->seekAble());
147 }
148
149 size_t Stream::read(HTTPConnectionManager *connManager)
150 {
151     Chunk *chunk = getChunk();
152     if(!chunk)
153         return 0;
154
155     if(!chunk->getConnection())
156     {
157        if(!connManager->connectChunk(chunk))
158         return 0;
159     }
160
161     size_t readsize = 0;
162
163     /* Because we don't know Chunk size at start, we need to get size
164        from content length */
165     if(chunk->getBytesRead() == 0)
166     {
167         if(chunk->getConnection()->query(chunk->getPath()) == false)
168             readsize = 32768; /* we don't handle retry here :/ */
169         else
170             readsize = chunk->getBytesToRead();
171     }
172     else
173     {
174         readsize = chunk->getBytesToRead();
175     }
176
177     if (readsize > 128000)
178         readsize = 32768;
179
180     block_t *block = block_Alloc(readsize);
181     if(!block)
182         return 0;
183
184     mtime_t time = mdate();
185     ssize_t ret = chunk->getConnection()->read(block->p_buffer, readsize);
186     time = mdate() - time;
187
188     if(ret <= 0)
189     {
190         block_Release(block);
191         chunk->getConnection()->releaseChunk();
192         currentChunk = NULL;
193         delete chunk;
194         return 0;
195     }
196     else
197     {
198         block->i_buffer = (size_t)ret;
199
200         adaptationLogic->updateDownloadRate(block->i_buffer, time);
201
202         if (chunk->getBytesToRead() == 0)
203         {
204             chunk->onDownload(block->p_buffer, block->i_buffer);
205             chunk->getConnection()->releaseChunk();
206             currentChunk = NULL;
207             delete chunk;
208         }
209     }
210
211     readsize = block->i_buffer;
212
213     output->pushBlock(block);
214
215     return readsize;
216 }
217
218 bool Stream::setPosition(mtime_t time, bool tryonly)
219 {
220     bool ret = segmentTracker->setPosition(time, tryonly);
221     if(!tryonly && ret)
222         output->setPosition(time);
223     return ret;
224 }
225
226 AbstractStreamOutput::AbstractStreamOutput(demux_t *demux)
227 {
228     realdemux = demux;
229     demuxstream = NULL;
230     pcr = VLC_TS_0;
231     group = -1;
232     escount = 0;
233     seekable = true;
234
235     fakeesout = new es_out_t;
236     if (!fakeesout)
237         throw VLC_ENOMEM;
238
239     fakeesout->pf_add = esOutAdd;
240     fakeesout->pf_control = esOutControl;
241     fakeesout->pf_del = esOutDel;
242     fakeesout->pf_destroy = esOutDestroy;
243     fakeesout->pf_send = esOutSend;
244     fakeesout->p_sys = (es_out_sys_t*) this;
245 }
246
247 AbstractStreamOutput::~AbstractStreamOutput()
248 {
249     if (demuxstream)
250         stream_Delete(demuxstream);
251     delete fakeesout;
252 }
253
254 mtime_t AbstractStreamOutput::getPCR() const
255 {
256     return pcr;
257 }
258
259 int AbstractStreamOutput::getGroup() const
260 {
261     return group;
262 }
263
264 int AbstractStreamOutput::esCount() const
265 {
266     return escount;
267 }
268
269 void AbstractStreamOutput::pushBlock(block_t *block)
270 {
271     stream_DemuxSend(demuxstream, block);
272 }
273
274 bool AbstractStreamOutput::seekAble() const
275 {
276     return (demuxstream && seekable);
277 }
278
279 void AbstractStreamOutput::setPosition(mtime_t nztime)
280 {
281     es_out_Control(realdemux->out, ES_OUT_SET_NEXT_DISPLAY_TIME,
282                    VLC_TS_0 + nztime);
283 }
284
285 /* Static callbacks */
286 es_out_id_t * AbstractStreamOutput::esOutAdd(es_out_t *fakees, const es_format_t *p_fmt)
287 {
288     AbstractStreamOutput *me = (AbstractStreamOutput *) fakees->p_sys;
289     me->escount++;
290     return me->realdemux->out->pf_add(me->realdemux->out, p_fmt);
291 }
292
293 int AbstractStreamOutput::esOutSend(es_out_t *fakees, es_out_id_t *p_es, block_t *p_block)
294 {
295     AbstractStreamOutput *me = (AbstractStreamOutput *) fakees->p_sys;
296     return me->realdemux->out->pf_send(me->realdemux->out, p_es, p_block);
297 }
298
299 void AbstractStreamOutput::esOutDel(es_out_t *fakees, es_out_id_t *p_es)
300 {
301     AbstractStreamOutput *me = (AbstractStreamOutput *) fakees->p_sys;
302     me->escount--;
303     me->realdemux->out->pf_del(me->realdemux->out, p_es);
304 }
305
306 int AbstractStreamOutput::esOutControl(es_out_t *fakees, int i_query, va_list args)
307 {
308     AbstractStreamOutput *me = (AbstractStreamOutput *) fakees->p_sys;
309     if (i_query == ES_OUT_SET_PCR )
310     {
311         me->pcr = (int64_t)va_arg( args, int64_t );
312         return VLC_SUCCESS;
313     }
314     else if( i_query == ES_OUT_SET_GROUP_PCR )
315     {
316         me->group = (int) va_arg( args, int );
317         me->pcr = (int64_t)va_arg( args, int64_t );
318         return VLC_SUCCESS;
319     }
320
321     return me->realdemux->out->pf_control(me->realdemux->out, i_query, args);
322 }
323
324 void AbstractStreamOutput::esOutDestroy(es_out_t *fakees)
325 {
326     AbstractStreamOutput *me = (AbstractStreamOutput *) fakees->p_sys;
327     me->realdemux->out->pf_destroy(me->realdemux->out);
328 }
329 /* !Static callbacks */
330
331 MP4StreamOutput::MP4StreamOutput(demux_t *demux) :
332     AbstractStreamOutput(demux)
333 {
334     demuxstream = stream_DemuxNew(demux, "mp4", fakeesout);
335     if(!demuxstream)
336         throw VLC_EGENERIC;
337 }
338
339 MPEG2TSStreamOutput::MPEG2TSStreamOutput(demux_t *demux) :
340     AbstractStreamOutput(demux)
341 {
342     demuxstream = stream_DemuxNew(demux, "ts", fakeesout);
343     if(!demuxstream)
344         throw VLC_EGENERIC;
345 }