demux: adaptive: clear BLOCK_FLAG_HEADER flag properly
[vlc.git] / modules / demux / adaptive / http / Chunk.cpp
blobc063e81268252d7d9df1eb6d7d915c966898e639
1 /*
2 * Chunk.cpp
3 *****************************************************************************
4 * Copyright (C) 2010 - 2011 Klagenfurt University
6 * Created on: Aug 10, 2010
7 * Authors: Christopher Mueller <christopher.mueller@itec.uni-klu.ac.at>
8 * Christian Timmerer <christian.timmerer@itec.uni-klu.ac.at>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published
12 * by the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include "Chunk.h"
29 #include "HTTPConnection.hpp"
30 #include "HTTPConnectionManager.h"
31 #include "Downloader.hpp"
33 #include <vlc_common.h>
34 #include <vlc_block.h>
36 #include <algorithm>
38 using namespace adaptive::http;
40 AbstractChunkSource::AbstractChunkSource()
42 contentLength = 0;
45 AbstractChunkSource::~AbstractChunkSource()
50 void AbstractChunkSource::setBytesRange(const BytesRange &range)
52 bytesRange = range;
53 if(bytesRange.isValid() && bytesRange.getEndByte())
54 contentLength = bytesRange.getEndByte() - bytesRange.getStartByte();
57 const BytesRange & AbstractChunkSource::getBytesRange() const
59 return bytesRange;
62 AbstractChunk::AbstractChunk(AbstractChunkSource *source_)
64 bytesRead = 0;
65 source = source_;
68 AbstractChunk::~AbstractChunk()
70 delete source;
73 size_t AbstractChunk::getBytesRead() const
75 return this->bytesRead;
78 uint64_t AbstractChunk::getStartByteInFile() const
80 if(!source || !source->getBytesRange().isValid())
81 return 0;
83 return source->getBytesRange().getStartByte();
86 block_t * AbstractChunk::doRead(size_t size, bool b_block)
88 if(!source)
89 return NULL;
91 block_t *block = (b_block) ? source->readBlock() : source->read(size);
92 if(block)
94 if(bytesRead == 0)
95 block->i_flags |= BLOCK_FLAG_HEADER;
96 bytesRead += block->i_buffer;
97 onDownload(&block);
98 block->i_flags &= ~BLOCK_FLAG_HEADER;
101 return block;
104 bool AbstractChunk::isEmpty() const
106 return !source->hasMoreData();
109 block_t * AbstractChunk::readBlock()
111 return doRead(0, true);
114 block_t * AbstractChunk::read(size_t size)
116 return doRead(size, false);
119 HTTPChunkSource::HTTPChunkSource(const std::string& url, AbstractConnectionManager *manager,
120 const adaptive::ID &id) :
121 AbstractChunkSource(),
122 connection (NULL),
123 connManager (manager),
124 consumed (0)
126 prepared = false;
127 eof = false;
128 sourceid = id;
129 if(!init(url))
130 eof = true;
133 HTTPChunkSource::~HTTPChunkSource()
135 if(connection)
136 connection->setUsed(false);
139 bool HTTPChunkSource::init(const std::string &url)
141 params = ConnectionParams(url);
143 if(params.getScheme() != "http" && params.getScheme() != "https")
144 return false;
146 if(params.getPath().empty() || params.getHostname().empty())
147 return false;
149 return true;
152 bool HTTPChunkSource::hasMoreData() const
154 if(eof)
155 return false;
156 else if(contentLength)
157 return consumed < contentLength;
158 else return true;
161 block_t * HTTPChunkSource::read(size_t readsize)
163 if(!prepare())
165 eof = true;
166 return NULL;
169 if(consumed == contentLength && consumed > 0)
171 eof = true;
172 return NULL;
175 if(contentLength && readsize > contentLength - consumed)
176 readsize = contentLength - consumed;
178 block_t *p_block = block_Alloc(readsize);
179 if(!p_block)
181 eof = true;
182 return NULL;
185 mtime_t time = mdate();
186 ssize_t ret = connection->read(p_block->p_buffer, readsize);
187 time = mdate() - time;
188 if(ret < 0)
190 block_Release(p_block);
191 p_block = NULL;
192 eof = true;
194 else
196 p_block->i_buffer = (size_t) ret;
197 consumed += p_block->i_buffer;
198 if((size_t)ret < readsize)
199 eof = true;
200 connManager->updateDownloadRate(sourceid, p_block->i_buffer, time);
203 return p_block;
206 bool HTTPChunkSource::prepare(int i_redir)
208 if(prepared)
209 return true;
211 if(!connManager)
212 return false;
214 if(!connection)
216 connection = connManager->getConnection(params);
217 if(!connection)
218 return false;
221 int i_ret = connection->request(params.getPath(), bytesRange);
222 if(i_ret != VLC_SUCCESS)
224 if(i_ret == VLC_ETIMEOUT && i_redir < 3)
225 return HTTPChunkSource::prepare(i_redir + 1);
226 return false;
228 /* Because we don't know Chunk size at start, we need to get size
229 from content length */
230 contentLength = connection->getContentLength();
231 prepared = true;
233 return true;
236 block_t * HTTPChunkSource::readBlock()
238 return read(HTTPChunkSource::CHUNK_SIZE);
241 HTTPChunkBufferedSource::HTTPChunkBufferedSource(const std::string& url, AbstractConnectionManager *manager,
242 const adaptive::ID &sourceid) :
243 HTTPChunkSource(url, manager, sourceid),
244 p_head (NULL),
245 pp_tail (&p_head),
246 buffered (0)
248 vlc_mutex_init(&lock);
249 vlc_cond_init(&avail);
250 done = false;
251 eof = false;
252 downloadstart = 0;
255 HTTPChunkBufferedSource::~HTTPChunkBufferedSource()
257 vlc_mutex_lock(&lock);
258 if(p_head)
260 block_ChainRelease(p_head);
261 p_head = NULL;
262 pp_tail = &p_head;
264 done = true;
265 buffered = 0;
266 vlc_mutex_unlock(&lock);
268 connManager->cancel(this);
270 vlc_cond_destroy(&avail);
271 vlc_mutex_destroy(&lock);
274 bool HTTPChunkBufferedSource::isDone() const
276 bool b_done;
277 vlc_mutex_lock(const_cast<vlc_mutex_t *>(&lock));
278 b_done = done;
279 vlc_mutex_unlock(const_cast<vlc_mutex_t *>(&lock));
280 return b_done;
283 void HTTPChunkBufferedSource::bufferize(size_t readsize)
285 vlc_mutex_lock(&lock);
286 if(!prepare())
288 done = true;
289 eof = true;
290 vlc_cond_signal(&avail);
291 vlc_mutex_unlock(&lock);
292 return;
295 if(readsize < HTTPChunkSource::CHUNK_SIZE)
296 readsize = HTTPChunkSource::CHUNK_SIZE;
298 if(contentLength && readsize > contentLength - buffered)
299 readsize = contentLength - buffered;
301 vlc_mutex_unlock(&lock);
303 block_t *p_block = block_Alloc(readsize);
304 if(!p_block)
306 eof = true;
307 return;
310 struct
312 size_t size;
313 mtime_t time;
314 } rate = {0,0};
316 ssize_t ret = connection->read(p_block->p_buffer, readsize);
317 if(ret <= 0)
319 block_Release(p_block);
320 vlc_mutex_lock(&lock);
321 done = true;
322 rate.size = buffered + consumed;
323 rate.time = mdate() - downloadstart;
324 downloadstart = 0;
325 vlc_mutex_unlock(&lock);
327 else
329 p_block->i_buffer = (size_t) ret;
330 vlc_mutex_lock(&lock);
331 buffered += p_block->i_buffer;
332 block_ChainLastAppend(&pp_tail, p_block);
333 if((size_t) ret < readsize)
335 done = true;
336 rate.size = buffered + consumed;
337 rate.time = mdate() - downloadstart;
338 downloadstart = 0;
340 vlc_mutex_unlock(&lock);
343 if(rate.size)
345 connManager->updateDownloadRate(sourceid, rate.size, rate.time);
348 vlc_cond_signal(&avail);
351 bool HTTPChunkBufferedSource::prepare()
353 if(!prepared)
355 downloadstart = mdate();
356 return HTTPChunkSource::prepare();
358 return true;
361 bool HTTPChunkBufferedSource::hasMoreData() const
363 bool b_hasdata;
364 vlc_mutex_lock(const_cast<vlc_mutex_t *>(&lock));
365 b_hasdata = !eof;
366 vlc_mutex_unlock(const_cast<vlc_mutex_t *>(&lock));
367 return b_hasdata;
370 block_t * HTTPChunkBufferedSource::readBlock()
372 block_t *p_block = NULL;
374 vlc_mutex_lock(&lock);
376 while(!p_head && !done)
377 vlc_cond_wait(&avail, &lock);
379 if(!p_head && done)
381 if(!eof)
382 p_block = block_Alloc(0);
383 eof = true;
384 vlc_mutex_unlock(&lock);
385 return p_block;
388 /* dequeue */
389 p_block = p_head;
390 p_head = p_head->p_next;
391 if(p_head == NULL)
393 pp_tail = &p_head;
394 if(done)
395 eof = true;
397 p_block->p_next = NULL;
399 consumed += p_block->i_buffer;
400 buffered -= p_block->i_buffer;
402 vlc_mutex_unlock(&lock);
404 return p_block;
407 block_t * HTTPChunkBufferedSource::read(size_t readsize)
409 vlc_mutex_lock(&lock);
411 while(readsize > buffered && !done)
412 vlc_cond_wait(&avail, &lock);
414 block_t *p_block = NULL;
415 if(!readsize || !buffered || !(p_block = block_Alloc(readsize)) )
417 eof = true;
418 vlc_mutex_unlock(&lock);
419 return NULL;
422 size_t copied = 0;
423 while(buffered && readsize)
425 const size_t toconsume = std::min(p_head->i_buffer, readsize);
426 memcpy(&p_block->p_buffer[copied], p_head->p_buffer, toconsume);
427 copied += toconsume;
428 readsize -= toconsume;
429 buffered -= toconsume;
430 p_head->i_buffer -= toconsume;
431 p_head->p_buffer += toconsume;
432 if(p_head->i_buffer == 0)
434 block_t *next = p_head->p_next;
435 p_head->p_next = NULL;
436 block_Release(p_head);
437 p_head = next;
438 if(next == NULL)
439 pp_tail = &p_head;
443 consumed += copied;
444 p_block->i_buffer = copied;
446 if(copied < readsize)
447 eof = true;
449 vlc_mutex_unlock(&lock);
451 return p_block;
454 HTTPChunk::HTTPChunk(const std::string &url, AbstractConnectionManager *manager,
455 const adaptive::ID &id):
456 AbstractChunk(new HTTPChunkSource(url, manager, id))
461 HTTPChunk::~HTTPChunk()