~ubuntu-branches/ubuntu/saucy/gnash/saucy-proposed

« back to all changes in this revision

Viewing changes to .pc/fixtypos.patch/cygnal/libnet/diskstream.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Angel Abad, Angel Abad, Micah Gersten
  • Date: 2010-11-28 22:18:48 UTC
  • mfrom: (3.1.10 sid)
  • Revision ID: james.westby@ubuntu.com-20101128221848-apjipwy78m13612a
Tags: 0.8.8-6ubuntu1
[ Angel Abad <angelabad@ubuntu.com> ]
* Merge from debian unstable (LP: #682386). Remaining changes:
  - Add Ubuntu flash alternatives in postinst and prerm
    + update debian/browser-plugin-gnash.postinst
    + update debian/browser-plugin-gnash.prerm

[ Micah Gersten <micahg@ubuntu.com> ]
* Only install the flash alternative in /usr/lib/mozilla/plugins as the other
  locations are deprecated
  - update debian/browser-plugin-gnash.postinst

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// stream.cpp:  Network streaming server for Cygnal, for Gnash.
2
 
// 
3
 
//   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
4
 
//   Foundation, Inc
5
 
// 
6
 
// This program is free software; you can redistribute it and/or modify
7
 
// it under the terms of the GNU General Public License as published by
8
 
// the Free Software Foundation; either version 3 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 General Public License for more details.
15
 
//
16
 
// You should have received a copy of the GNU General Public License
17
 
// along with this program; if not, write to the Free Software
18
 
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19
 
//
20
 
 
21
 
#ifdef HAVE_CONFIG_H
22
 
#include "gnashconfig.h"
23
 
#endif
24
 
 
25
 
#include <sys/types.h>
26
 
#include <sys/stat.h>
27
 
#include <fcntl.h>
28
 
#include <iostream>
29
 
#include <string>
30
 
#include <cerrno>
31
 
#include <algorithm>
32
 
#if !defined(_WIN32) && !defined(__amigaos4__)
33
 
#include <sys/mman.h>
34
 
#elif defined(__amigaos4__)
35
 
#include <proto/exec.h>
36
 
#include <cstdlib> //for malloc/free
37
 
#else
38
 
#include <windows.h>
39
 
#endif
40
 
 
41
 
#include "GnashSystemIOHeaders.h"
42
 
#include "network.h"
43
 
#include "buffer.h"
44
 
#include "amf.h"
45
 
#include "log.h"
46
 
#include "cque.h"
47
 
#include "diskstream.h"
48
 
#include "cache.h"
49
 
#include "getclocktime.hpp"
50
 
 
51
 
// This is Linux specific, but offers better I/O for sending
52
 
// files out a network connection.
53
 
#ifdef HAVE_SENDFILE
54
 
# include <sys/sendfile.h>
55
 
#endif
56
 
 
57
 
#include <boost/thread/mutex.hpp>
58
 
static boost::mutex io_mutex;
59
 
static boost::mutex mem_mutex;
60
 
 
61
 
using std::string;
62
 
 
63
 
/// \namespace gnash
64
 
///     This is the main namespace for Gnash and it's libraries.
65
 
namespace gnash {
66
 
 
67
 
static Cache& cache = Cache::getDefaultInstance();
68
 
 
69
 
/// \def _SC_PAGESIZE
70
 
///     This isn't set on all systems, but is used to get the page
71
 
///     size used for memory allocations.
72
 
#ifndef _SC_PAGESIZE
73
 
#define _SC_PAGESIZE 8
74
 
#endif
75
 
 
76
 
/// \var MAX_PAGES
77
 
///     This is the maximum number of pages that we load into memory from a file.
78
 
const size_t MAX_PAGES = 2560;
79
 
 
80
 
#ifndef MAP_FAILED
81
 
#define MAP_FAILED 0
82
 
#endif
83
 
 
84
 
DiskStream::DiskStream()
85
 
    : _state(DiskStream::NO_STATE),
86
 
      _filefd(0),
87
 
      _netfd(0),
88
 
      _dataptr(0),
89
 
      _max_memload(0),
90
 
      _filesize(0),
91
 
      _pagesize(0),
92
 
      _offset(0)
93
 
{
94
 
//    GNASH_REPORT_FUNCTION;
95
 
    /// \brief get the pagesize and cache the value
96
 
#ifdef HAVE_SYSCONF
97
 
    _pagesize = sysconf(_SC_PAGESIZE);
98
 
    _max_memload = _pagesize * MAX_PAGES;    
99
 
#else
100
 
#if _WIN32
101
 
    // The default page size for Win32 is 4k
102
 
    SYSTEM_INFO si;
103
 
    GetSystemInfo(&si);
104
 
    _pagesize = si.dwPageSize;
105
 
#else
106
 
#ifdef __amigaos4__
107
 
        uint32 PageSize;
108
 
 
109
 
        IExec->GetCPUInfoTags(
110
 
               GCIT_ExecPageSize, &PageSize, 
111
 
               TAG_DONE);
112
 
    _pagesize = PageSize;
113
 
#else
114
 
#error "Need to define the memory page size without sysconf()!"
115
 
#endif
116
 
#endif
117
 
#endif
118
 
#ifdef USE_STATS_CACHE
119
 
    clock_gettime (CLOCK_REALTIME, &_last_access);
120
 
    _accesses = 1;
121
 
#endif
122
 
}
123
 
 
124
 
DiskStream::DiskStream(const string &str)
125
 
    : _state(DiskStream::NO_STATE),
126
 
      _filefd(0),
127
 
      _netfd(0),
128
 
      _dataptr(0),
129
 
      _max_memload(0),
130
 
      _filesize(0),
131
 
      _pagesize(0),
132
 
      _offset(0)
133
 
{
134
 
//    GNASH_REPORT_FUNCTION;
135
 
    /// \brief get the pagesize and cache the value
136
 
#ifdef HAVE_SYSCONF
137
 
    _pagesize = sysconf(_SC_PAGESIZE);
138
 
    _max_memload = _pagesize * MAX_PAGES;    
139
 
#else
140
 
#ifdef _WIN32
141
 
    SYSTEM_INFO si;
142
 
    GetSystemInfo(&si);
143
 
    _pagesize = si.dwPageSize;
144
 
#else
145
 
#ifdef __amigaos4__
146
 
        uint32 PageSize;
147
 
 
148
 
        IExec->GetCPUInfoTags(
149
 
               GCIT_ExecPageSize, &PageSize, 
150
 
               TAG_DONE);
151
 
    _pagesize = PageSize;
152
 
#else
153
 
#error "Need to define the memory page size without sysconf()!"
154
 
#endif
155
 
#endif
156
 
#endif
157
 
 
158
 
    _filespec = str;
159
 
#ifdef USE_STATS_CACHE
160
 
    clock_gettime (CLOCK_REALTIME, &_last_access);
161
 
    _accesses = 1;
162
 
#endif
163
 
}
164
 
 
165
 
DiskStream::DiskStream(const string &str, boost::uint8_t *data, size_t size)
166
 
    : _state(DiskStream::NO_STATE),
167
 
      _filefd(0),
168
 
      _netfd(0),
169
 
      _dataptr(0),
170
 
      _max_memload(0),
171
 
      _pagesize(0),
172
 
      _offset(0)
173
 
{
174
 
//    GNASH_REPORT_FUNCTION;
175
 
    
176
 
    /// \brief get the pagesize and cache the value
177
 
#ifdef HAVE_SYSCONF
178
 
    _pagesize = sysconf(_SC_PAGESIZE);
179
 
    _max_memload = _pagesize * MAX_PAGES;    
180
 
#else
181
 
#ifdef _WIN32
182
 
    SYSTEM_INFO si;
183
 
    GetSystemInfo(&si);
184
 
    _pagesize = si.dwPageSize;
185
 
#else
186
 
#ifdef __amigaos4__
187
 
        uint32 PageSize;
188
 
 
189
 
        IExec->GetCPUInfoTags(
190
 
               GCIT_ExecPageSize, &PageSize, 
191
 
               TAG_DONE);
192
 
    _pagesize = PageSize;
193
 
#else
194
 
#error "Need to define the memory page size without sysconf()!"
195
 
#endif
196
 
#endif
197
 
#endif
198
 
 
199
 
    _dataptr = new boost::uint8_t[size];
200
 
    // Note that this is a copy operation, which may effect performance. We do this for now
201
 
    // incase the top level pointer gets deleted. This should really be using
202
 
    // boost::scoped_array, but we don't want that complexity till this code stabalizes.
203
 
    std::copy(data, data + size, _dataptr);
204
 
    _filespec = str;
205
 
    _filesize = size;
206
 
    
207
 
#ifdef USE_STATS_CACHE
208
 
    clock_gettime (CLOCK_REALTIME, &_last_access);
209
 
    _accesses = 1;
210
 
#endif    
211
 
}
212
 
 
213
 
DiskStream::DiskStream(const string &str, cygnal::Buffer &buf)
214
 
    : _state(DiskStream::NO_STATE),
215
 
      _filefd(0),
216
 
      _netfd(0),
217
 
      _dataptr(0),
218
 
      _max_memload(0),
219
 
      _pagesize(0),
220
 
      _offset(0)
221
 
{
222
 
//    GNASH_REPORT_FUNCTION;
223
 
    
224
 
    /// \brief get the pagesize and cache the value
225
 
#ifdef HAVE_SYSCONF
226
 
    _pagesize = sysconf(_SC_PAGESIZE);
227
 
    _max_memload = _pagesize * MAX_PAGES;    
228
 
#else
229
 
#ifdef _WIN32
230
 
    SYSTEM_INFO si;
231
 
    GetSystemInfo(&si);
232
 
    _pagesize = si.dwPageSize;
233
 
#else
234
 
#ifdef __amigaos4__
235
 
    uint32 PageSize;
236
 
    
237
 
    IExec->GetCPUInfoTags(
238
 
        GCIT_ExecPageSize, &PageSize, 
239
 
        TAG_DONE);
240
 
    _pagesize = PageSize;
241
 
#else
242
 
#error "Need to define the memory page size without sysconf()!"
243
 
#endif
244
 
#endif
245
 
#endif
246
 
 
247
 
    _dataptr = new boost::uint8_t[buf.size()];
248
 
    // Note that this is a copy operation, which may effect performance. We do this for now
249
 
    // incase the top level pointer gets deleted. This should really be using
250
 
    // boost::scoped_array, but we don't want that complexity till this code stabalizes.
251
 
    std::copy(buf.begin(), buf.end(), _dataptr);
252
 
    _filespec = str;
253
 
    _filesize = buf.size();
254
 
    
255
 
#ifdef USE_STATS_CACHE
256
 
    clock_gettime (CLOCK_REALTIME, &_last_access);
257
 
    _accesses = 1;
258
 
#endif
259
 
}
260
 
 
261
 
DiskStream::DiskStream(const string &str, int netfd)
262
 
    : _state(DiskStream::NO_STATE),
263
 
      _filefd(0),
264
 
      _filespec(0),
265
 
      _dataptr(0),
266
 
      _max_memload(0),
267
 
      _filesize(0),
268
 
      _pagesize(0),
269
 
      _offset(0)
270
 
{
271
 
//    GNASH_REPORT_FUNCTION;
272
 
    /// \brief get the pagesize and cache the value
273
 
#ifdef HAVE_SYSCONF
274
 
    _pagesize = sysconf(_SC_PAGESIZE);
275
 
    _max_memload = _pagesize * MAX_PAGES;    
276
 
#else
277
 
#ifdef _WIN32
278
 
    SYSTEM_INFO si;
279
 
    GetSystemInfo(&si);
280
 
    _pagesize = si.dwPageSize;
281
 
#else
282
 
#ifdef __amigaos4__
283
 
        uint32 PageSize;
284
 
 
285
 
        IExec->GetCPUInfoTags(
286
 
               GCIT_ExecPageSize, &PageSize, 
287
 
               TAG_DONE);
288
 
    _pagesize = PageSize;
289
 
#else
290
 
#error "Need to define the memory page size without sysconf()!"
291
 
#endif
292
 
#endif
293
 
#endif
294
 
 
295
 
    _netfd = netfd;
296
 
    _filespec = str;
297
 
#ifdef USE_STATS_CACHE
298
 
    clock_gettime (CLOCK_REALTIME, &_last_access);
299
 
    _accesses = 1;
300
 
#endif
301
 
}
302
 
 
303
 
DiskStream::~DiskStream()
304
 
{
305
 
    GNASH_REPORT_FUNCTION;
306
 
    log_debug("Deleting %s on fd #%d", _filespec, _filefd);
307
 
 
308
 
    if (_filefd) {
309
 
        ::close(_filefd);
310
 
    }
311
 
    if (_netfd) {
312
 
        ::close(_netfd);
313
 
    }
314
 
}
315
 
 
316
 
/// \brief copy another DiskStream into ourselves, so they share data
317
 
///             in memory.
318
 
DiskStream &
319
 
DiskStream::operator=(DiskStream *stream)
320
 
{
321
 
    GNASH_REPORT_FUNCTION;
322
 
    
323
 
    _filespec = stream->getFilespec();
324
 
    _filetype = stream->getFileType();
325
 
    _filefd = stream->getFileFd();
326
 
    _netfd = stream->getNetFd();
327
 
    _dataptr = stream->get();
328
 
    _state = stream->getState();
329
 
 
330
 
    return *this;
331
 
}
332
 
 
333
 
bool
334
 
DiskStream::fullyPopulated()
335
 
{
336
 
    // GNASH_REPORT_FUNCTION;
337
 
    
338
 
    if ((_filesize < _max_memload) && (_dataptr != 0)) {
339
 
        return true;
340
 
    }
341
 
    return false;
342
 
};
343
 
 
344
 
/// \brief Close the open disk file, but stay resident in memory.
345
 
void
346
 
DiskStream::close()
347
 
{
348
 
    // GNASH_REPORT_FUNCTION;
349
 
 
350
 
    log_debug("Closing %s on fd #%d", _filespec, _filefd);
351
 
 
352
 
    if (_filefd) {
353
 
        ::close(_filefd);
354
 
    }
355
 
 
356
 
    // reset everything in case we get reopened.
357
 
    _filefd = 0;
358
 
    _netfd = 0;
359
 
    _offset = 0;
360
 
    _seekptr = _dataptr + _pagesize;
361
 
    _state = CLOSED;
362
 
 
363
 
#if 0                           // FIXME: don't unmap the memory for debugging
364
 
#ifdef _WIN32
365
 
    UnmapViewOfFile(_dataptr);
366
 
#elif defined(__amigaos4__)
367
 
        if (_dataptr) free(_dataptr);
368
 
#else
369
 
    if ((_dataptr != MAP_FAILED) && (_dataptr != 0)) {
370
 
        munmap(_dataptr, _pagesize);
371
 
    }
372
 
#endif
373
 
#endif
374
 
    
375
 
}
376
 
 
377
 
/// \brief Load a chunk (pagesize) of the file into memory.
378
 
///     This loads a pagesize of the disk file into memory. We read
379
 
///     the file this way as it is faster and takes less resources
380
 
///     than read(), which add buffering we don't need.
381
 
///     This offset must be a multipe of the pagesize.
382
 
///
383
 
/// @param size The amount of bytes to read, often the filesize
384
 
///             for smaller files below CACHE_LIMIT.
385
 
///
386
 
/// @param offset The location in bytes in the file of the desired data.
387
 
///
388
 
/// @return A real pointer to the location of the data at the
389
 
///     location pointed to by the offset.
390
 
boost::uint8_t *
391
 
DiskStream::loadToMem(off_t offset)
392
 
{
393
 
    // GNASH_REPORT_FUNCTION;
394
 
 
395
 
    return loadToMem(_filesize, offset);
396
 
}
397
 
 
398
 
boost::uint8_t *
399
 
DiskStream::loadToMem(size_t filesize, off_t offset)
400
 
{
401
 
    GNASH_REPORT_FUNCTION;
402
 
 
403
 
    log_debug("%s: offset is: %d", __FUNCTION__, offset);
404
 
 
405
 
    // store the offset we came in with so next time we know where to start
406
 
    _offset = offset;
407
 
        
408
 
    /// We only map memory in pages of pagesize, so if the offset is smaller
409
 
    /// than that, start at page 0.
410
 
    off_t page = 0;
411
 
    if (static_cast<size_t>(offset) < _pagesize) {
412
 
        page = 0;
413
 
    } else {
414
 
        if (offset % _pagesize) {
415
 
            // calculate the number of pages
416
 
            page = ((offset - (offset % _pagesize)) / _pagesize) * _pagesize;
417
 
            log_debug("Adjusting offset from %d to %d so it's page aligned.",
418
 
                      offset, page);
419
 
        } else {
420
 
            log_debug("Offset is page aligned already");
421
 
        }
422
 
    }
423
 
 
424
 
    // Figure out the maximum number of bytes we can load into memory.
425
 
    size_t loadsize = 0;
426
 
    if (filesize < _max_memload) {
427
 
        log_debug("Loading entire file of %d bytes into memory segment", filesize);
428
 
        loadsize = filesize;
429
 
    } else {
430
 
        log_debug("Loading partial file of %d bytes into memory segment", filesize, _max_memload);
431
 
        loadsize = _max_memload;
432
 
    }
433
 
    
434
 
    // If we were initialized from a memory Buffer, data is being uploaded into
435
 
    // this DiskStream, so sufficient memory will already be allocated for this data.
436
 
    // If the data came from a disk based file, then we allocate enough memory to hold it.
437
 
    if (_dataptr) {
438
 
        log_debug("Using existing Buffer for file");
439
 
        return _dataptr + offset;
440
 
    }
441
 
    
442
 
    boost::uint8_t *dataptr = 0;
443
 
    
444
 
    if (_filefd) {
445
 
        /// If the data pointer is legit, then we need to unmap that page
446
 
        /// to mmap() a new one. If we're still in the current mapped
447
 
        /// page, then just return the existing data pointer.
448
 
        if (dataptr != 0) {
449
 
#ifdef _WIN32
450
 
            UnmapViewOfFile(_dataptr);
451
 
#elif defined(__amigaos4__)
452
 
        if (_dataptr) free(_dataptr);
453
 
#else
454
 
            munmap(_dataptr, _pagesize);
455
 
#endif
456
 
        }
457
 
#if 0
458
 
        // See if the page has already been mapped in;
459
 
        unsigned char vec[_pagesize];
460
 
        mincore(offset, _pagesize, vec);
461
 
        if (vec[i] & 0x1) {
462
 
            // cached page is in memory
463
 
        }
464
 
#endif
465
 
//      if (size <= _pagesize) {
466
 
//          size = _filesize;
467
 
//      }
468
 
 
469
 
        // lock in case two threads try to load the same file at the
470
 
        // same time.
471
 
        boost::mutex::scoped_lock lock(mem_mutex);
472
 
        
473
 
#ifdef _WIN32
474
 
        HANDLE handle = CreateFileMapping((HANDLE)_get_osfhandle(_filefd), NULL,
475
 
                                          PAGE_WRITECOPY, 0, 0, NULL);
476
 
        if (handle != NULL) {
477
 
            dataptr = static_cast<boost::uint8_t *>(MapViewOfFile(handle, FILE_MAP_COPY, 0, offset, page));
478
 
            CloseHandle(handle);
479
 
 
480
 
        }
481
 
#elif defined(__amigaos4__)
482
 
        dataptr = static_cast<boost::uint8_t *>(malloc(loadsize));
483
 
#else
484
 
        dataptr = static_cast<boost::uint8_t *>(mmap(0, loadsize,
485
 
                                                     PROT_READ, MAP_SHARED,
486
 
                                                     _filefd, page));
487
 
#endif
488
 
    } else {
489
 
        log_error (_("Couldn't load file %s"), _filespec);
490
 
        return 0;
491
 
    }
492
 
    
493
 
    if (dataptr == MAP_FAILED) {
494
 
        log_error (_("Couldn't map file %s into memory: %s"),
495
 
                   _filespec, strerror(errno));
496
 
        return 0;
497
 
    } else {
498
 
        log_debug (_("File %s a offset %d mapped to: %p"), _filespec, offset, (void *)dataptr);
499
 
        clock_gettime (CLOCK_REALTIME, &_last_access);
500
 
        _dataptr = dataptr;
501
 
        // map the seekptr to the end of data
502
 
        _seekptr = _dataptr + _pagesize;
503
 
        _state = OPEN;
504
 
        _offset = 0;
505
 
    }
506
 
 
507
 
    boost::uint8_t *ptr = dataptr;
508
 
    if (_filetype == FILETYPE_FLV) {
509
 
        // FIXME: for now, assume all media files are in FLV format
510
 
        _flv.reset(new cygnal::Flv);
511
 
        boost::shared_ptr<cygnal::Flv::flv_header_t> head = _flv->decodeHeader(ptr);
512
 
        ptr += sizeof(cygnal::Flv::flv_header_t);
513
 
        ptr += sizeof(cygnal::Flv::previous_size_t);
514
 
        boost::shared_ptr<cygnal::Flv::flv_tag_t> tag  = _flv->decodeTagHeader(ptr);
515
 
        ptr += sizeof(cygnal::Flv::flv_tag_t);
516
 
        size_t bodysize = _flv->convert24(tag->bodysize);           
517
 
        if (tag->type == cygnal::Flv::TAG_METADATA) {
518
 
            boost::shared_ptr<cygnal::Element> metadata = _flv->decodeMetaData(ptr, bodysize);
519
 
            if (metadata) {
520
 
                metadata->dump();
521
 
            }
522
 
        }
523
 
    }
524
 
 
525
 
    if (filesize < _max_memload) {
526
 
        close();
527
 
    }
528
 
    
529
 
    // The data pointer points to the real data past all the header bytes.
530
 
//    _dataptr = ptr;
531
 
 
532
 
    return _seekptr;    
533
 
}
534
 
 
535
 
/// \brief Write the existing data to the Network.
536
 
///
537
 
/// @return true is the write suceeded, false if it failed.
538
 
bool
539
 
DiskStream::writeToNet(int /* start */, int /* bytes */)
540
 
{
541
 
    GNASH_REPORT_FUNCTION;
542
 
 
543
 
    return false;
544
 
}
545
 
 
546
 
/// \brief Write the data in memory to disk
547
 
///
548
 
/// @param filespec The relative path to the file to write, which goes in
549
 
///             a safebox for storage.
550
 
///
551
 
/// @return true if the operation suceeded, false if it failed.
552
 
bool
553
 
DiskStream::writeToDisk()
554
 
{
555
 
//    GNASH_REPORT_FUNCTION;
556
 
    return writeToDisk(_filespec, _dataptr, _filesize);
557
 
}
558
 
 
559
 
bool
560
 
DiskStream::writeToDisk(const std::string &filespec)
561
 
{
562
 
//    GNASH_REPORT_FUNCTION;
563
 
    return writeToDisk(filespec, _dataptr, _filesize);
564
 
}
565
 
 
566
 
bool
567
 
DiskStream::writeToDisk(const std::string &filespec, cygnal::Buffer &data)
568
 
{
569
 
//    GNASH_REPORT_FUNCTION;
570
 
    return writeToDisk(filespec, data.reference(), data.allocated());
571
 
}
572
 
 
573
 
bool
574
 
DiskStream::writeToDisk(const std::string &filespec, boost::uint8_t *data, size_t size)
575
 
{
576
 
//    GNASH_REPORT_FUNCTION;
577
 
 
578
 
    int fd = ::open(filespec.c_str() ,O_WRONLY|O_CREAT, S_IRWXU);
579
 
    if (fd < 0) {
580
 
        log_error(strerror(errno));
581
 
    }
582
 
    log_debug("Writing data (%d bytes) to disk: \"%s\"", size, filespec);
583
 
    ::write(fd, data, size);
584
 
    ::close(fd);
585
 
 
586
 
    return true;
587
 
}
588
 
 
589
 
/// \brief Open a file to be streamed.
590
 
///
591
 
/// @param filespec The full path and file name for the data to be
592
 
///     read.
593
 
///
594
 
/// @return True if the file was opened sucessfully, false if not.
595
 
bool
596
 
DiskStream::open(const string &filespec)
597
 
{
598
 
//    GNASH_REPORT_FUNCTION;
599
 
    
600
 
    return open(filespec, _netfd);
601
 
}
602
 
 
603
 
/// \brief Open a file to be streamed.
604
 
///
605
 
/// @param filespec The full path and file name for the data to be
606
 
///     read.
607
 
///
608
 
/// @param netfd An optional file descriptor to read data from
609
 
///
610
 
/// @return True if the file was opened sucessfully, false if not.
611
 
bool
612
 
DiskStream::open(const string &filespec, int /*netfd*/)
613
 
{
614
 
//    GNASH_REPORT_FUNCTION;
615
 
 
616
 
    //struct stat stats;
617
 
    // TODO: should we use the 'netfd' passed as parameter instead ?
618
 
    return open(filespec, _netfd, _statistics);
619
 
}
620
 
 
621
 
/// \brief Open a file to be streamed.
622
 
///
623
 
/// @param filespec The full path and file name for the data to be
624
 
///     read.
625
 
///
626
 
/// @param netfd An optional file descriptor to read data from
627
 
///
628
 
/// @param statistics The optional data structure to use for
629
 
///     collecting statistics on this stream.
630
 
///
631
 
/// @return True if the file was opened sucessfully, false if not.
632
 
bool
633
 
DiskStream::open(const string &filespec, int netfd, Statistics &statistics)
634
 
{
635
 
    GNASH_REPORT_FUNCTION;
636
 
 
637
 
    // the file is already open
638
 
    if (_state == OPEN) {
639
 
#ifdef USE_STATS_CACHE
640
 
        _accesses++;
641
 
#endif
642
 
        return true;
643
 
    }
644
 
 
645
 
    // If DONE, then we were previously open, but not closed, so we
646
 
    // just reopen the same stream.
647
 
    if ((_state == DONE) || (_state == CLOSED)) {
648
 
        _state = OPEN;
649
 
        return true;
650
 
    }
651
 
    
652
 
    _netfd = netfd;
653
 
    _statistics = statistics;
654
 
    _filespec = filespec;
655
 
 
656
 
    log_debug("Trying to open %s", filespec);
657
 
    
658
 
    if (getFileStats(filespec)) {
659
 
        boost::mutex::scoped_lock lock(io_mutex);
660
 
        _filefd = ::open(_filespec.c_str(), O_RDONLY);
661
 
        log_debug (_("Opening file %s (fd #%d), %lld bytes in size."),
662
 
                   _filespec, _filefd,
663
 
                 (long long int) _filesize);
664
 
        _state = OPEN;
665
 
        _filetype = determineFileType(filespec);
666
 
        loadToMem(0); // load the first page into memory
667
 
    } else {
668
 
        log_error (_("File %s doesn't exist"), _filespec);
669
 
        _state = DONE;
670
 
        return false;
671
 
    }
672
 
    
673
 
#ifdef USE_STATS_CACHE
674
 
    clock_gettime (CLOCK_REALTIME, &_first_access);
675
 
#endif
676
 
    
677
 
    return true;
678
 
}
679
 
 
680
 
/// \brief Stream the file that has been loaded,
681
 
///
682
 
/// @return True if the data was streamed sucessfully, false if not.
683
 
bool
684
 
DiskStream::play()
685
 
{
686
 
//    GNASH_REPORT_FUNCTION;
687
 
 
688
 
    return play(_netfd, true);
689
 
}
690
 
 
691
 
bool
692
 
DiskStream::play(bool flag)
693
 
{
694
 
//    GNASH_REPORT_FUNCTION;
695
 
 
696
 
    return play(_netfd, flag);
697
 
}
698
 
 
699
 
/// \brief Stream the file that has been loaded,
700
 
///
701
 
/// @param netfd The file descriptor to use for operations
702
 
///
703
 
/// @param flag True to only send the first packet, False plays entire file.
704
 
///
705
 
/// @return True if the data was streamed sucessfully, false if not.
706
 
bool
707
 
DiskStream::play(int netfd, bool flag)
708
 
{
709
 
    GNASH_REPORT_FUNCTION;
710
 
 
711
 
    bool done = false;
712
 
//     dump();
713
 
    
714
 
    _netfd = netfd;
715
 
 
716
 
    while (!done) {
717
 
        // If flag is false, only play the one page of the file.
718
 
        if (!flag) {
719
 
            done = true;
720
 
        }
721
 
        switch (_state) {
722
 
          case NO_STATE:
723
 
              log_network("No Diskstream open %s for net fd #%d", _filespec, netfd);
724
 
              break;
725
 
          case CREATED:
726
 
          case CLOSED:
727
 
              if (_dataptr) {
728
 
                  log_network("Diskstream %s is closed on net fd #%d.", _filespec, netfd);
729
 
              }
730
 
              done = true;
731
 
              continue;
732
 
          case OPEN:
733
 
              loadToMem(0);
734
 
              _offset = 0;
735
 
              _state = PLAY;
736
 
              // continue;
737
 
          case PLAY:
738
 
          {
739
 
              size_t ret;
740
 
              Network net;
741
 
              if ((_filesize - _offset) < _pagesize) {
742
 
#ifdef HAVE_SENDFILE_XX
743
 
                  ret = sendfile(netfd, _filefd, &_offset, _filesize - _offset);
744
 
#else
745
 
                  ret = net.writeNet(netfd, (_dataptr + _offset), (_filesize - _offset));
746
 
                  if (ret != (_filesize - _offset)) {
747
 
                      log_error("In %s(%d): couldn't write %d bytes to net fd #%d! %s",
748
 
                                __FUNCTION__, __LINE__, (_filesize - _offset),
749
 
                                netfd, strerror(errno));
750
 
                  }
751
 
#endif
752
 
                  log_network("Done playing file %s, size was: %d", _filespec, _filesize);
753
 
                  close();
754
 
                  done = true;
755
 
                  // reset to the beginning of the file
756
 
                  _offset = 0;
757
 
              } else {
758
 
                  //log_network("\tPlaying part of file %s, offset is: %d out of %d bytes.", _filespec, _offset, _filesize);
759
 
#ifdef HAVE_SENDFILE_XX
760
 
                  ret = sendfile(netfd, _filefd, &_offset, _pagesize);
761
 
#else
762
 
                  ret = net.writeNet(netfd, (_dataptr + _offset), _pagesize);
763
 
                  if (ret != _pagesize) {
764
 
                      log_error("In %s(%d): couldn't write %d of bytes of data to net fd #%d! Got %d, %s",
765
 
                                __FUNCTION__, __LINE__, _pagesize, netfd,
766
 
                                ret, strerror(errno));
767
 
                      return false;
768
 
                  }
769
 
                  _offset += _pagesize;
770
 
#endif
771
 
              }
772
 
              switch (errno) {
773
 
                case EINVAL:
774
 
                case ENOSYS:
775
 
                case EFAULT:
776
 
                    log_network("ERROR: %s", strerror(errno));
777
 
                    break;
778
 
                default:
779
 
                    break;
780
 
              }
781
 
              break;
782
 
          }
783
 
          case PREVIEW:
784
 
              break;
785
 
          case THUMBNAIL:
786
 
              break;
787
 
          case PAUSE:
788
 
              break;
789
 
          case SEEK:
790
 
              break;
791
 
          case UPLOAD:
792
 
              break;
793
 
          case MULTICAST:
794
 
              break;
795
 
          case DONE:
796
 
              log_debug("Restarting Disk Stream from the beginning");
797
 
              _offset = 0;
798
 
              _filefd = 0;
799
 
              _state = PLAY;
800
 
              _seekptr = _dataptr + _pagesize;
801
 
              _netfd = netfd;
802
 
              continue;
803
 
          default:
804
 
              break;
805
 
        }
806
 
    }
807
 
 
808
 
    //int blocksize = 8192;
809
 
//    Network net;
810
 
//    while ((_seekptr - _dataptr) >= 0) {
811
 
////    nbytes = net.writeNet(_netfd, (char *)_seekptr, _filesize);
812
 
//    if (nbytes <= 0) {
813
 
//        break;
814
 
//    }
815
 
#ifdef USE_STATS_FILE
816
 
    _statistics->addBytes(nbytes);
817
 
    _bytes += nbytes;
818
 
    // _seekptr += nbytes;
819
 
#endif
820
 
 
821
 
    return true;
822
 
}
823
 
 
824
 
/// \brief Stream a preview of the file.
825
 
///     A preview is a series of video frames from
826
 
///     the video file. Each video frame is taken by sampling
827
 
///     the file at a set interval.
828
 
///
829
 
/// @param filespec The full path and file name for the data to be
830
 
///     read.
831
 
///
832
 
/// @param quantity The number of frames to stream..
833
 
///
834
 
/// @return True if the thumbnails were streamed sucessfully, false if not.
835
 
bool
836
 
DiskStream::preview(const string & /*filespec*/, int /*frames*/)
837
 
{
838
 
//    GNASH_REPORT_FUNCTION;
839
 
 
840
 
    _state = PREVIEW;
841
 
    log_unimpl("%s", __PRETTY_FUNCTION__);
842
 
    return true; // Default to true    
843
 
}
844
 
 
845
 
/// \brief Stream a series of thumbnails.
846
 
///     A thumbnail is a series of jpg images of frames from
847
 
///     the video file instead of video frames. Each thumbnail
848
 
///     is taken by sampling the file at a set interval.
849
 
///
850
 
/// @param filespec The full path and file name for the data to be
851
 
///     read.
852
 
///
853
 
/// @param quantity The number of thumbnails to stream..
854
 
///
855
 
/// @return True if the thumbnails were streamed sucessfully, false if not.
856
 
bool
857
 
DiskStream::thumbnail(const string & /*filespec*/, int /*quantity*/)
858
 
{
859
 
//    GNASH_REPORT_FUNCTION;
860
 
    
861
 
    _state = THUMBNAIL;
862
 
    log_unimpl("%s", __PRETTY_FUNCTION__);
863
 
    return true; // Default to true
864
 
}
865
 
 
866
 
/// \brief Pause the stream currently being played.
867
 
///
868
 
/// @return True if the stream was paused sucessfully, false if not.
869
 
bool
870
 
DiskStream::pause()
871
 
{
872
 
//    GNASH_REPORT_FUNCTION;
873
 
    
874
 
    _state = PAUSE;
875
 
    log_unimpl("%s", __PRETTY_FUNCTION__);
876
 
    return true; // Default to true
877
 
}
878
 
 
879
 
/// \brief Seek within the stream.
880
 
///
881
 
/// @param the offset in bytes to the location within the file to
882
 
///     seek to.
883
 
///
884
 
/// @return A real pointer to the location of the data seeked to.
885
 
boost::uint8_t *
886
 
DiskStream::seek(off_t offset)
887
 
{
888
 
//    GNASH_REPORT_FUNCTION;
889
 
    
890
 
    _state = SEEK;
891
 
    return loadToMem(offset);    
892
 
}
893
 
 
894
 
/// \brief Upload a file into a sandbox.
895
 
///     The sandbox is an area where uploaded files can get
896
 
///     written to safely. For SWF content, the file name also
897
 
///     includes a few optional paths used to seperate
898
 
///     applications from each other.
899
 
///
900
 
/// @param filespec The file name for the data to be written.
901
 
///
902
 
/// @return True if the file was uploaded sucessfully, false if not.
903
 
bool
904
 
DiskStream::upload(const string & /*filespec*/)
905
 
{
906
 
//    GNASH_REPORT_FUNCTION;
907
 
    
908
 
    _state = UPLOAD;
909
 
    log_unimpl("%s", __PRETTY_FUNCTION__);
910
 
    return true; // Default to true
911
 
}
912
 
 
913
 
// Stream a single "real-time" source.
914
 
bool
915
 
DiskStream::multicast(const string & /*filespec*/)
916
 
{
917
 
//    GNASH_REPORT_FUNCTION;
918
 
    
919
 
    _state = MULTICAST;
920
 
    log_unimpl("%s", __PRETTY_FUNCTION__);
921
 
    return true; // Default to true    
922
 
}
923
 
 
924
 
DiskStream::filetype_e
925
 
DiskStream::determineFileType()
926
 
{
927
 
//    GNASH_REPORT_FUNCTION;
928
 
    
929
 
  return(determineFileType(_filespec));
930
 
}
931
 
 
932
 
// Get the file type, so we know how to set the
933
 
// Content-type in the header.
934
 
bool
935
 
DiskStream::getFileStats(const std::string &filespec)
936
 
{
937
 
  //    GNASH_REPORT_FUNCTION;    
938
 
    string actual_filespec = filespec;
939
 
    struct stat st;
940
 
    bool try_again = false;
941
 
 
942
 
    do {
943
 
      //        cerr << "Trying to open " << actual_filespec << "\r\n";
944
 
      if (stat(actual_filespec.c_str(), &st) == 0) {
945
 
        // If it's a directory, then we emulate what apache
946
 
        // does, which is to load the index.html file in that
947
 
        // directry if it exists.
948
 
        if (S_ISDIR(st.st_mode)) {
949
 
          log_debug("%s is a directory, appending index.html\n",
950
 
                    actual_filespec.c_str());
951
 
          if (actual_filespec[actual_filespec.size()-1] != '/') {
952
 
            actual_filespec += '/';
953
 
          }
954
 
          actual_filespec += "index.html";
955
 
          try_again = true;
956
 
          continue;
957
 
        } else {                // not a directory
958
 
          //      log_debug("%s is a normal file\n", actual_filespec.c_str());
959
 
          _filespec = actual_filespec;
960
 
          _filetype = determineFileType(_filespec);
961
 
          _filesize = st.st_size;
962
 
          try_again = false;
963
 
        }
964
 
      } else {
965
 
        _filetype = FILETYPE_NONE;
966
 
        return false;
967
 
      } // end of stat()
968
 
      
969
 
      _filesize = st.st_size;
970
 
    } while (try_again);
971
 
    
972
 
    return true;
973
 
}
974
 
 
975
 
DiskStream::filetype_e 
976
 
DiskStream::determineFileType(const string &filespec)
977
 
{
978
 
//    GNASH_REPORT_FUNCTION;
979
 
    
980
 
  if (filespec.empty()) {
981
 
    return FILETYPE_NONE;
982
 
  }
983
 
 
984
 
  string::size_type pos;
985
 
 
986
 
  string name = filespec;
987
 
 
988
 
  // transform to lower case so we match filenames which may
989
 
  // have the suffix in upper case, or this test fails.
990
 
  std::transform(name.begin(), name.end(), name.begin(), 
991
 
                 (int(*)(int)) tolower);
992
 
 
993
 
  pos = name.rfind(".");
994
 
  if (pos != string::npos) {
995
 
    string suffix = name.substr(pos+1, name.size());
996
 
    _filetype = FILETYPE_NONE;
997
 
    if (suffix == "htm") {
998
 
      _filetype = FILETYPE_HTML;
999
 
    } else if (suffix == "html") {
1000
 
      _filetype = FILETYPE_HTML;
1001
 
    } else if (suffix == "ogg") {
1002
 
      _filetype = FILETYPE_OGG;
1003
 
    } else if (suffix == "ogv") {
1004
 
      _filetype = FILETYPE_OGG;
1005
 
    } else if (suffix == "swf") {
1006
 
      _filetype = FILETYPE_SWF;
1007
 
    } else if (suffix == "php") {
1008
 
      _filetype = FILETYPE_PHP;
1009
 
    } else if (suffix == "flv") {
1010
 
      _filetype = FILETYPE_FLV;
1011
 
    }else if (suffix == "mp3") {
1012
 
      _filetype = FILETYPE_MP3;
1013
 
    } else if (suffix == "flac") {
1014
 
      _filetype = FILETYPE_FLAC;
1015
 
    } else if (suffix == "jpg") {
1016
 
      _filetype = FILETYPE_JPEG;
1017
 
    } else if (suffix == "jpeg") {
1018
 
      _filetype = FILETYPE_JPEG;
1019
 
    } else if (suffix == "txt") {
1020
 
      _filetype = FILETYPE_TEXT;
1021
 
    } else if (suffix == "xml") {
1022
 
      _filetype = FILETYPE_XML;
1023
 
    } else if (suffix == "mp4") {
1024
 
      _filetype = FILETYPE_MP4;
1025
 
    } else if (suffix == "mpeg") {
1026
 
      _filetype = FILETYPE_MP4;
1027
 
    } else if (suffix == "png") {
1028
 
      _filetype = FILETYPE_PNG;
1029
 
    } else if (suffix == "gif") {
1030
 
      _filetype = FILETYPE_GIF;
1031
 
    }
1032
 
  }
1033
 
 
1034
 
  return _filetype;
1035
 
}
1036
 
 
1037
 
DiskStream::filetype_e 
1038
 
DiskStream::determineFileType( boost::uint8_t *data)
1039
 
{
1040
 
//    GNASH_REPORT_FUNCTION;
1041
 
 
1042
 
  if (data == 0) {
1043
 
    return FILETYPE_NONE;
1044
 
  }
1045
 
 
1046
 
  // JPEG, offset 6 bytes, read the string JFIF
1047
 
  if (memcpy(data + 6, "JFIF", 4) == 0) {
1048
 
    return FILETYPE_NONE;
1049
 
  }
1050
 
  // SWF, offset 0, read the string FWS
1051
 
  if (memcpy(data, "SWF", 3) == 0) {
1052
 
    return FILETYPE_SWF;
1053
 
  }
1054
 
  // compressed SWF, offset 0, read the string CWS
1055
 
  // FLV, offset 0, read the string FLV
1056
 
  // PNG, offset 0, read the string PNG
1057
 
  if (memcpy(data, "PNG", 3) == 0) {
1058
 
    return FILETYPE_PNG;
1059
 
  }
1060
 
  // Ogg, offset 0, read the string OggS
1061
 
  if (memcpy(data, "OggS", 4) == 0) {
1062
 
    return FILETYPE_OGG;
1063
 
  }
1064
 
  // Theora, offset 28, read string theora
1065
 
  if (memcpy(data + 28, "theora", 6) == 0) {
1066
 
    return FILETYPE_THEORA;
1067
 
  }
1068
 
  // FLAC, offset 28, read string FLAC
1069
 
  if (memcpy(data + 28, "FLAC", 4) == 0) {
1070
 
    return FILETYPE_FLAC;
1071
 
  }
1072
 
  // Vorbis, offset 28, read string vorbis
1073
 
  if (memcpy(data + 28, "vorbis", 6) == 0) {
1074
 
    return FILETYPE_VORBIS;
1075
 
  }
1076
 
  // MP3, offset 0, read string ID3
1077
 
  if (memcpy(data, "ID3", 3) == 0) {
1078
 
    return FILETYPE_MP3;
1079
 
  }
1080
 
 
1081
 
  // HTML
1082
 
  //   offset 0, read string "\<!doctype\ html"
1083
 
  //   offset 0, read string "\<head"
1084
 
  //   offset 0, read string "\<title"
1085
 
  //   offset 0, read string "\<html"
1086
 
  if (memcpy(data, "ID3", 3) == 0) {
1087
 
    return FILETYPE_HTML;
1088
 
  }
1089
 
 
1090
 
  // XML, offset 0, read string "\<?xml"
1091
 
  if (memcpy(data, "<?xml", 5) == 0) {
1092
 
    return FILETYPE_XML;
1093
 
  }
1094
 
  
1095
 
  return FILETYPE_NONE;
1096
 
}
1097
 
 
1098
 
///  \brief Dump the internal data of this class in a human readable form.
1099
 
/// @remarks This should only be used for debugging purposes.
1100
 
void
1101
 
DiskStream::dump()
1102
 
{
1103
 
    using namespace std;
1104
 
//    GNASH_REPORT_FUNCTION;
1105
 
    const char *state_str[] = {
1106
 
        "NO_STATE",
1107
 
        "CREATED",
1108
 
        "CLOSED",
1109
 
        "OPEN", 
1110
 
        "PLAY",
1111
 
        "PREVIEW",
1112
 
        "THUMBNAIL",
1113
 
        "PAUSE",
1114
 
        "SEEK",
1115
 
        "UPLOAD",
1116
 
        "MULTICAST",
1117
 
        "DONE"
1118
 
    };
1119
 
 
1120
 
    const char *type_str[] = {
1121
 
        "NONE",
1122
 
        "AMF",
1123
 
        "SWF",
1124
 
        "HTML",
1125
 
        "PNG",
1126
 
        "JPEG",
1127
 
        "GIF",
1128
 
        "MP3",
1129
 
        "MP4",
1130
 
        "OGG",
1131
 
        "VORBIS",
1132
 
        "THEORA",
1133
 
        "DIRAC",
1134
 
        "TEXT",
1135
 
        "FLV",
1136
 
        "VP6",
1137
 
        "XML",
1138
 
        "FLAC",
1139
 
        "ENCODED"
1140
 
    };  
1141
 
    
1142
 
    cerr << "State is \"" << state_str[_state] << "\"" << endl;
1143
 
    cerr << "File type is \"" << type_str[_filetype] << "\"" << endl;
1144
 
    cerr << "Filespec is \"" << _filespec << "\"" << endl;
1145
 
    cerr << "Disk file descriptor is fd #" << _filefd << endl;
1146
 
    cerr << "Network file descriptor is fd #" << _netfd << endl;
1147
 
    cerr << "File size is " <<  _filesize << endl;
1148
 
    cerr << "Memory Page size is " << _pagesize << endl;
1149
 
    cerr << "Memory Offset is " << _offset << endl;
1150
 
    cerr << "Base Memory Address is " << (void *)_dataptr << endl;
1151
 
    cerr << "Seek Pointer Memory Address is " << (void *)_seekptr << endl;
1152
 
    
1153
 
    // dump timing related data
1154
 
    struct timespec now;
1155
 
    clock_gettime (CLOCK_REALTIME, &now);    
1156
 
//    double time = ((now.tv_sec - _last_access.tv_sec) + ((now.tv_nsec - _last_access.tv_nsec)/1e9));
1157
 
    
1158
 
    cerr << "Time since last access:  " << fixed << ((now.tv_sec - _last_access.tv_sec) + ((now.tv_nsec - _last_access.tv_nsec)/1e9)) << " seconds ago." << endl;
1159
 
    
1160
 
#ifdef USE_STATS_CACHE
1161
 
    cerr << "Time since first access: " << fixed <<
1162
 
        ((_last_access.tv_sec - _first_access.tv_sec) + ((_last_access.tv_nsec - _first_access.tv_nsec)/1e9))
1163
 
         << " seconds lifespan."
1164
 
         << endl;
1165
 
#endif
1166
 
    
1167
 
}
1168
 
 
1169
 
 
1170
 
} // end of cygnal namespace
1171
 
 
1172
 
// local Variables:
1173
 
// mode: C++
1174
 
// indent-tabs-mode: t
1175
 
// End: