1
// stream.cpp: Network streaming server for Cygnal, for Gnash.
3
// Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
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.
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.
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
22
#include "gnashconfig.h"
25
#include <sys/types.h>
32
#if !defined(_WIN32) && !defined(__amigaos4__)
34
#elif defined(__amigaos4__)
35
#include <proto/exec.h>
36
#include <cstdlib> //for malloc/free
41
#include "GnashSystemIOHeaders.h"
47
#include "diskstream.h"
49
#include "getclocktime.hpp"
51
// This is Linux specific, but offers better I/O for sending
52
// files out a network connection.
54
# include <sys/sendfile.h>
57
#include <boost/thread/mutex.hpp>
58
static boost::mutex io_mutex;
59
static boost::mutex mem_mutex;
64
/// This is the main namespace for Gnash and it's libraries.
67
static Cache& cache = Cache::getDefaultInstance();
70
/// This isn't set on all systems, but is used to get the page
71
/// size used for memory allocations.
73
#define _SC_PAGESIZE 8
77
/// This is the maximum number of pages that we load into memory from a file.
78
const size_t MAX_PAGES = 2560;
84
DiskStream::DiskStream()
85
: _state(DiskStream::NO_STATE),
94
// GNASH_REPORT_FUNCTION;
95
/// \brief get the pagesize and cache the value
97
_pagesize = sysconf(_SC_PAGESIZE);
98
_max_memload = _pagesize * MAX_PAGES;
101
// The default page size for Win32 is 4k
104
_pagesize = si.dwPageSize;
109
IExec->GetCPUInfoTags(
110
GCIT_ExecPageSize, &PageSize,
112
_pagesize = PageSize;
114
#error "Need to define the memory page size without sysconf()!"
118
#ifdef USE_STATS_CACHE
119
clock_gettime (CLOCK_REALTIME, &_last_access);
124
DiskStream::DiskStream(const string &str)
125
: _state(DiskStream::NO_STATE),
134
// GNASH_REPORT_FUNCTION;
135
/// \brief get the pagesize and cache the value
137
_pagesize = sysconf(_SC_PAGESIZE);
138
_max_memload = _pagesize * MAX_PAGES;
143
_pagesize = si.dwPageSize;
148
IExec->GetCPUInfoTags(
149
GCIT_ExecPageSize, &PageSize,
151
_pagesize = PageSize;
153
#error "Need to define the memory page size without sysconf()!"
159
#ifdef USE_STATS_CACHE
160
clock_gettime (CLOCK_REALTIME, &_last_access);
165
DiskStream::DiskStream(const string &str, boost::uint8_t *data, size_t size)
166
: _state(DiskStream::NO_STATE),
174
// GNASH_REPORT_FUNCTION;
176
/// \brief get the pagesize and cache the value
178
_pagesize = sysconf(_SC_PAGESIZE);
179
_max_memload = _pagesize * MAX_PAGES;
184
_pagesize = si.dwPageSize;
189
IExec->GetCPUInfoTags(
190
GCIT_ExecPageSize, &PageSize,
192
_pagesize = PageSize;
194
#error "Need to define the memory page size without sysconf()!"
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);
207
#ifdef USE_STATS_CACHE
208
clock_gettime (CLOCK_REALTIME, &_last_access);
213
DiskStream::DiskStream(const string &str, cygnal::Buffer &buf)
214
: _state(DiskStream::NO_STATE),
222
// GNASH_REPORT_FUNCTION;
224
/// \brief get the pagesize and cache the value
226
_pagesize = sysconf(_SC_PAGESIZE);
227
_max_memload = _pagesize * MAX_PAGES;
232
_pagesize = si.dwPageSize;
237
IExec->GetCPUInfoTags(
238
GCIT_ExecPageSize, &PageSize,
240
_pagesize = PageSize;
242
#error "Need to define the memory page size without sysconf()!"
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);
253
_filesize = buf.size();
255
#ifdef USE_STATS_CACHE
256
clock_gettime (CLOCK_REALTIME, &_last_access);
261
DiskStream::DiskStream(const string &str, int netfd)
262
: _state(DiskStream::NO_STATE),
271
// GNASH_REPORT_FUNCTION;
272
/// \brief get the pagesize and cache the value
274
_pagesize = sysconf(_SC_PAGESIZE);
275
_max_memload = _pagesize * MAX_PAGES;
280
_pagesize = si.dwPageSize;
285
IExec->GetCPUInfoTags(
286
GCIT_ExecPageSize, &PageSize,
288
_pagesize = PageSize;
290
#error "Need to define the memory page size without sysconf()!"
297
#ifdef USE_STATS_CACHE
298
clock_gettime (CLOCK_REALTIME, &_last_access);
303
DiskStream::~DiskStream()
305
GNASH_REPORT_FUNCTION;
306
log_debug("Deleting %s on fd #%d", _filespec, _filefd);
316
/// \brief copy another DiskStream into ourselves, so they share data
319
DiskStream::operator=(DiskStream *stream)
321
GNASH_REPORT_FUNCTION;
323
_filespec = stream->getFilespec();
324
_filetype = stream->getFileType();
325
_filefd = stream->getFileFd();
326
_netfd = stream->getNetFd();
327
_dataptr = stream->get();
328
_state = stream->getState();
334
DiskStream::fullyPopulated()
336
// GNASH_REPORT_FUNCTION;
338
if ((_filesize < _max_memload) && (_dataptr != 0)) {
344
/// \brief Close the open disk file, but stay resident in memory.
348
// GNASH_REPORT_FUNCTION;
350
log_debug("Closing %s on fd #%d", _filespec, _filefd);
356
// reset everything in case we get reopened.
360
_seekptr = _dataptr + _pagesize;
363
#if 0 // FIXME: don't unmap the memory for debugging
365
UnmapViewOfFile(_dataptr);
366
#elif defined(__amigaos4__)
367
if (_dataptr) free(_dataptr);
369
if ((_dataptr != MAP_FAILED) && (_dataptr != 0)) {
370
munmap(_dataptr, _pagesize);
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.
383
/// @param size The amount of bytes to read, often the filesize
384
/// for smaller files below CACHE_LIMIT.
386
/// @param offset The location in bytes in the file of the desired data.
388
/// @return A real pointer to the location of the data at the
389
/// location pointed to by the offset.
391
DiskStream::loadToMem(off_t offset)
393
// GNASH_REPORT_FUNCTION;
395
return loadToMem(_filesize, offset);
399
DiskStream::loadToMem(size_t filesize, off_t offset)
401
GNASH_REPORT_FUNCTION;
403
log_debug("%s: offset is: %d", __FUNCTION__, offset);
405
// store the offset we came in with so next time we know where to start
408
/// We only map memory in pages of pagesize, so if the offset is smaller
409
/// than that, start at page 0.
411
if (static_cast<size_t>(offset) < _pagesize) {
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.",
420
log_debug("Offset is page aligned already");
424
// Figure out the maximum number of bytes we can load into memory.
426
if (filesize < _max_memload) {
427
log_debug("Loading entire file of %d bytes into memory segment", filesize);
430
log_debug("Loading partial file of %d bytes into memory segment", filesize, _max_memload);
431
loadsize = _max_memload;
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.
438
log_debug("Using existing Buffer for file");
439
return _dataptr + offset;
442
boost::uint8_t *dataptr = 0;
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.
450
UnmapViewOfFile(_dataptr);
451
#elif defined(__amigaos4__)
452
if (_dataptr) free(_dataptr);
454
munmap(_dataptr, _pagesize);
458
// See if the page has already been mapped in;
459
unsigned char vec[_pagesize];
460
mincore(offset, _pagesize, vec);
462
// cached page is in memory
465
// if (size <= _pagesize) {
469
// lock in case two threads try to load the same file at the
471
boost::mutex::scoped_lock lock(mem_mutex);
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));
481
#elif defined(__amigaos4__)
482
dataptr = static_cast<boost::uint8_t *>(malloc(loadsize));
484
dataptr = static_cast<boost::uint8_t *>(mmap(0, loadsize,
485
PROT_READ, MAP_SHARED,
489
log_error (_("Couldn't load file %s"), _filespec);
493
if (dataptr == MAP_FAILED) {
494
log_error (_("Couldn't map file %s into memory: %s"),
495
_filespec, strerror(errno));
498
log_debug (_("File %s a offset %d mapped to: %p"), _filespec, offset, (void *)dataptr);
499
clock_gettime (CLOCK_REALTIME, &_last_access);
501
// map the seekptr to the end of data
502
_seekptr = _dataptr + _pagesize;
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);
525
if (filesize < _max_memload) {
529
// The data pointer points to the real data past all the header bytes.
535
/// \brief Write the existing data to the Network.
537
/// @return true is the write suceeded, false if it failed.
539
DiskStream::writeToNet(int /* start */, int /* bytes */)
541
GNASH_REPORT_FUNCTION;
546
/// \brief Write the data in memory to disk
548
/// @param filespec The relative path to the file to write, which goes in
549
/// a safebox for storage.
551
/// @return true if the operation suceeded, false if it failed.
553
DiskStream::writeToDisk()
555
// GNASH_REPORT_FUNCTION;
556
return writeToDisk(_filespec, _dataptr, _filesize);
560
DiskStream::writeToDisk(const std::string &filespec)
562
// GNASH_REPORT_FUNCTION;
563
return writeToDisk(filespec, _dataptr, _filesize);
567
DiskStream::writeToDisk(const std::string &filespec, cygnal::Buffer &data)
569
// GNASH_REPORT_FUNCTION;
570
return writeToDisk(filespec, data.reference(), data.allocated());
574
DiskStream::writeToDisk(const std::string &filespec, boost::uint8_t *data, size_t size)
576
// GNASH_REPORT_FUNCTION;
578
int fd = ::open(filespec.c_str() ,O_WRONLY|O_CREAT, S_IRWXU);
580
log_error(strerror(errno));
582
log_debug("Writing data (%d bytes) to disk: \"%s\"", size, filespec);
583
::write(fd, data, size);
589
/// \brief Open a file to be streamed.
591
/// @param filespec The full path and file name for the data to be
594
/// @return True if the file was opened sucessfully, false if not.
596
DiskStream::open(const string &filespec)
598
// GNASH_REPORT_FUNCTION;
600
return open(filespec, _netfd);
603
/// \brief Open a file to be streamed.
605
/// @param filespec The full path and file name for the data to be
608
/// @param netfd An optional file descriptor to read data from
610
/// @return True if the file was opened sucessfully, false if not.
612
DiskStream::open(const string &filespec, int /*netfd*/)
614
// GNASH_REPORT_FUNCTION;
617
// TODO: should we use the 'netfd' passed as parameter instead ?
618
return open(filespec, _netfd, _statistics);
621
/// \brief Open a file to be streamed.
623
/// @param filespec The full path and file name for the data to be
626
/// @param netfd An optional file descriptor to read data from
628
/// @param statistics The optional data structure to use for
629
/// collecting statistics on this stream.
631
/// @return True if the file was opened sucessfully, false if not.
633
DiskStream::open(const string &filespec, int netfd, Statistics &statistics)
635
GNASH_REPORT_FUNCTION;
637
// the file is already open
638
if (_state == OPEN) {
639
#ifdef USE_STATS_CACHE
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)) {
653
_statistics = statistics;
654
_filespec = filespec;
656
log_debug("Trying to open %s", filespec);
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."),
663
(long long int) _filesize);
665
_filetype = determineFileType(filespec);
666
loadToMem(0); // load the first page into memory
668
log_error (_("File %s doesn't exist"), _filespec);
673
#ifdef USE_STATS_CACHE
674
clock_gettime (CLOCK_REALTIME, &_first_access);
680
/// \brief Stream the file that has been loaded,
682
/// @return True if the data was streamed sucessfully, false if not.
686
// GNASH_REPORT_FUNCTION;
688
return play(_netfd, true);
692
DiskStream::play(bool flag)
694
// GNASH_REPORT_FUNCTION;
696
return play(_netfd, flag);
699
/// \brief Stream the file that has been loaded,
701
/// @param netfd The file descriptor to use for operations
703
/// @param flag True to only send the first packet, False plays entire file.
705
/// @return True if the data was streamed sucessfully, false if not.
707
DiskStream::play(int netfd, bool flag)
709
GNASH_REPORT_FUNCTION;
717
// If flag is false, only play the one page of the file.
723
log_network("No Diskstream open %s for net fd #%d", _filespec, netfd);
728
log_network("Diskstream %s is closed on net fd #%d.", _filespec, netfd);
741
if ((_filesize - _offset) < _pagesize) {
742
#ifdef HAVE_SENDFILE_XX
743
ret = sendfile(netfd, _filefd, &_offset, _filesize - _offset);
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));
752
log_network("Done playing file %s, size was: %d", _filespec, _filesize);
755
// reset to the beginning of the file
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);
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));
769
_offset += _pagesize;
776
log_network("ERROR: %s", strerror(errno));
796
log_debug("Restarting Disk Stream from the beginning");
800
_seekptr = _dataptr + _pagesize;
808
//int blocksize = 8192;
810
// while ((_seekptr - _dataptr) >= 0) {
811
//// nbytes = net.writeNet(_netfd, (char *)_seekptr, _filesize);
812
// if (nbytes <= 0) {
815
#ifdef USE_STATS_FILE
816
_statistics->addBytes(nbytes);
818
// _seekptr += nbytes;
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.
829
/// @param filespec The full path and file name for the data to be
832
/// @param quantity The number of frames to stream..
834
/// @return True if the thumbnails were streamed sucessfully, false if not.
836
DiskStream::preview(const string & /*filespec*/, int /*frames*/)
838
// GNASH_REPORT_FUNCTION;
841
log_unimpl("%s", __PRETTY_FUNCTION__);
842
return true; // Default to true
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.
850
/// @param filespec The full path and file name for the data to be
853
/// @param quantity The number of thumbnails to stream..
855
/// @return True if the thumbnails were streamed sucessfully, false if not.
857
DiskStream::thumbnail(const string & /*filespec*/, int /*quantity*/)
859
// GNASH_REPORT_FUNCTION;
862
log_unimpl("%s", __PRETTY_FUNCTION__);
863
return true; // Default to true
866
/// \brief Pause the stream currently being played.
868
/// @return True if the stream was paused sucessfully, false if not.
872
// GNASH_REPORT_FUNCTION;
875
log_unimpl("%s", __PRETTY_FUNCTION__);
876
return true; // Default to true
879
/// \brief Seek within the stream.
881
/// @param the offset in bytes to the location within the file to
884
/// @return A real pointer to the location of the data seeked to.
886
DiskStream::seek(off_t offset)
888
// GNASH_REPORT_FUNCTION;
891
return loadToMem(offset);
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.
900
/// @param filespec The file name for the data to be written.
902
/// @return True if the file was uploaded sucessfully, false if not.
904
DiskStream::upload(const string & /*filespec*/)
906
// GNASH_REPORT_FUNCTION;
909
log_unimpl("%s", __PRETTY_FUNCTION__);
910
return true; // Default to true
913
// Stream a single "real-time" source.
915
DiskStream::multicast(const string & /*filespec*/)
917
// GNASH_REPORT_FUNCTION;
920
log_unimpl("%s", __PRETTY_FUNCTION__);
921
return true; // Default to true
924
DiskStream::filetype_e
925
DiskStream::determineFileType()
927
// GNASH_REPORT_FUNCTION;
929
return(determineFileType(_filespec));
932
// Get the file type, so we know how to set the
933
// Content-type in the header.
935
DiskStream::getFileStats(const std::string &filespec)
937
// GNASH_REPORT_FUNCTION;
938
string actual_filespec = filespec;
940
bool try_again = false;
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 += '/';
954
actual_filespec += "index.html";
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;
965
_filetype = FILETYPE_NONE;
969
_filesize = st.st_size;
975
DiskStream::filetype_e
976
DiskStream::determineFileType(const string &filespec)
978
// GNASH_REPORT_FUNCTION;
980
if (filespec.empty()) {
981
return FILETYPE_NONE;
984
string::size_type pos;
986
string name = filespec;
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);
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;
1037
DiskStream::filetype_e
1038
DiskStream::determineFileType( boost::uint8_t *data)
1040
// GNASH_REPORT_FUNCTION;
1043
return FILETYPE_NONE;
1046
// JPEG, offset 6 bytes, read the string JFIF
1047
if (memcpy(data + 6, "JFIF", 4) == 0) {
1048
return FILETYPE_NONE;
1050
// SWF, offset 0, read the string FWS
1051
if (memcpy(data, "SWF", 3) == 0) {
1052
return FILETYPE_SWF;
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;
1060
// Ogg, offset 0, read the string OggS
1061
if (memcpy(data, "OggS", 4) == 0) {
1062
return FILETYPE_OGG;
1064
// Theora, offset 28, read string theora
1065
if (memcpy(data + 28, "theora", 6) == 0) {
1066
return FILETYPE_THEORA;
1068
// FLAC, offset 28, read string FLAC
1069
if (memcpy(data + 28, "FLAC", 4) == 0) {
1070
return FILETYPE_FLAC;
1072
// Vorbis, offset 28, read string vorbis
1073
if (memcpy(data + 28, "vorbis", 6) == 0) {
1074
return FILETYPE_VORBIS;
1076
// MP3, offset 0, read string ID3
1077
if (memcpy(data, "ID3", 3) == 0) {
1078
return FILETYPE_MP3;
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;
1090
// XML, offset 0, read string "\<?xml"
1091
if (memcpy(data, "<?xml", 5) == 0) {
1092
return FILETYPE_XML;
1095
return FILETYPE_NONE;
1098
/// \brief Dump the internal data of this class in a human readable form.
1099
/// @remarks This should only be used for debugging purposes.
1103
using namespace std;
1104
// GNASH_REPORT_FUNCTION;
1105
const char *state_str[] = {
1120
const char *type_str[] = {
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;
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));
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;
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."
1170
} // end of cygnal namespace
1174
// indent-tabs-mode: t