liveMedia/ByteStreamFileSource.cpp

Go to the documentation of this file.
00001 /**********
00002 This library is free software; you can redistribute it and/or modify it under
00003 the terms of the GNU Lesser General Public License as published by the
00004 Free Software Foundation; either version 2.1 of the License, or (at your
00005 option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
00006 
00007 This library is distributed in the hope that it will be useful, but WITHOUT
00008 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00009 FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
00010 more details.
00011 
00012 You should have received a copy of the GNU Lesser General Public License
00013 along with this library; if not, write to the Free Software Foundation, Inc.,
00014 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
00015 **********/
00016 // "liveMedia"
00017 // Copyright (c) 1996-2012 Live Networks, Inc.  All rights reserved.
00018 // A file source that is a plain byte stream (rather than frames)
00019 // Implementation
00020 
00021 #if (defined(__WIN32__) || defined(_WIN32)) && !defined(_WIN32_WCE)
00022 #include <io.h>
00023 #include <fcntl.h>
00024 #define READ_FROM_FILES_SYNCHRONOUSLY 1
00025     // Because Windows is a silly toy operating system that doesn't (reliably) treat
00026     // open files as being readable sockets (which can be handled within the default
00027     // "BasicTaskScheduler" event loop, using "select()"), we implement file reading
00028     // in Windows using synchronous, rather than asynchronous, I/O.  This can severely
00029     // limit the scalability of servers using this code that run on Windows.
00030     // If this is a problem for you, then either use a better operating system,
00031     // or else write your own Windows-specific event loop ("TaskScheduler" subclass)
00032     // that can handle readable data in Windows open files as an event.
00033 #endif
00034 
00035 #include "ByteStreamFileSource.hh"
00036 #include "InputFile.hh"
00037 #include "GroupsockHelper.hh"
00038 
00040 
00041 ByteStreamFileSource*
00042 ByteStreamFileSource::createNew(UsageEnvironment& env, char const* fileName,
00043                                 unsigned preferredFrameSize,
00044                                 unsigned playTimePerFrame) {
00045   FILE* fid = OpenInputFile(env, fileName);
00046   if (fid == NULL) return NULL;
00047 
00048   ByteStreamFileSource* newSource
00049     = new ByteStreamFileSource(env, fid, preferredFrameSize, playTimePerFrame);
00050   newSource->fFileSize = GetFileSize(fileName, fid);
00051 
00052   return newSource;
00053 }
00054 
00055 ByteStreamFileSource*
00056 ByteStreamFileSource::createNew(UsageEnvironment& env, FILE* fid,
00057                                 unsigned preferredFrameSize,
00058                                 unsigned playTimePerFrame) {
00059   if (fid == NULL) return NULL;
00060 
00061   ByteStreamFileSource* newSource = new ByteStreamFileSource(env, fid, preferredFrameSize, playTimePerFrame);
00062   newSource->fFileSize = GetFileSize(NULL, fid);
00063 
00064   return newSource;
00065 }
00066 
00067 void ByteStreamFileSource::seekToByteAbsolute(u_int64_t byteNumber, u_int64_t numBytesToStream) {
00068   SeekFile64(fFid, (int64_t)byteNumber, SEEK_SET);
00069 
00070   fNumBytesToStream = numBytesToStream;
00071   fLimitNumBytesToStream = fNumBytesToStream > 0;
00072 }
00073 
00074 void ByteStreamFileSource::seekToByteRelative(int64_t offset) {
00075   SeekFile64(fFid, offset, SEEK_CUR);
00076 }
00077 
00078 void ByteStreamFileSource::seekToEnd() {
00079   SeekFile64(fFid, 0, SEEK_END);
00080 }
00081 
00082 ByteStreamFileSource::ByteStreamFileSource(UsageEnvironment& env, FILE* fid,
00083                                            unsigned preferredFrameSize,
00084                                            unsigned playTimePerFrame)
00085   : FramedFileSource(env, fid), fFileSize(0), fPreferredFrameSize(preferredFrameSize),
00086     fPlayTimePerFrame(playTimePerFrame), fLastPlayTime(0),
00087     fHaveStartedReading(False), fLimitNumBytesToStream(False), fNumBytesToStream(0) {
00088 #ifndef READ_FROM_FILES_SYNCHRONOUSLY
00089   makeSocketNonBlocking(fileno(fFid));
00090 #endif
00091 
00092   // Test whether the file is seekable
00093   if (SeekFile64(fFid, 1, SEEK_CUR) >= 0) {
00094     fFidIsSeekable = True;
00095     SeekFile64(fFid, -1, SEEK_CUR);
00096   } else {
00097     fFidIsSeekable = False;
00098   }
00099 }
00100 
00101 ByteStreamFileSource::~ByteStreamFileSource() {
00102   if (fFid == NULL) return;
00103 
00104 #ifndef READ_FROM_FILES_SYNCHRONOUSLY
00105   envir().taskScheduler().turnOffBackgroundReadHandling(fileno(fFid));
00106 #endif
00107 
00108   CloseInputFile(fFid);
00109 }
00110 
00111 void ByteStreamFileSource::doGetNextFrame() {
00112   if (feof(fFid) || ferror(fFid) || (fLimitNumBytesToStream && fNumBytesToStream == 0)) {
00113     handleClosure(this);
00114     return;
00115   }
00116 
00117 #ifdef READ_FROM_FILES_SYNCHRONOUSLY
00118   doReadFromFile();
00119 #else
00120   if (!fHaveStartedReading) {
00121     // Await readable data from the file:
00122     envir().taskScheduler().turnOnBackgroundReadHandling(fileno(fFid),
00123                (TaskScheduler::BackgroundHandlerProc*)&fileReadableHandler, this);
00124     fHaveStartedReading = True;
00125   }
00126 #endif
00127 }
00128 
00129 void ByteStreamFileSource::doStopGettingFrames() {
00130 #ifndef READ_FROM_FILES_SYNCHRONOUSLY
00131   envir().taskScheduler().turnOffBackgroundReadHandling(fileno(fFid));
00132   fHaveStartedReading = False;
00133 #endif
00134 }
00135 
00136 void ByteStreamFileSource::fileReadableHandler(ByteStreamFileSource* source, int /*mask*/) {
00137   if (!source->isCurrentlyAwaitingData()) {
00138     source->doStopGettingFrames(); // we're not ready for the data yet
00139     return;
00140   }
00141   source->doReadFromFile();
00142 }
00143 
00144 void ByteStreamFileSource::doReadFromFile() {
00145   // Try to read as many bytes as will fit in the buffer provided (or "fPreferredFrameSize" if less)
00146   if (fLimitNumBytesToStream && fNumBytesToStream < (u_int64_t)fMaxSize) {
00147     fMaxSize = (unsigned)fNumBytesToStream;
00148   }
00149   if (fPreferredFrameSize > 0 && fPreferredFrameSize < fMaxSize) {
00150     fMaxSize = fPreferredFrameSize;
00151   }
00152 #ifdef READ_FROM_FILES_SYNCHRONOUSLY
00153   fFrameSize = fread(fTo, 1, fMaxSize, fFid);
00154 #else
00155   if (fFidIsSeekable) {
00156     fFrameSize = fread(fTo, 1, fMaxSize, fFid);
00157   } else {
00158     // For non-seekable files (e.g., pipes), call "read()" rather than "fread()", to ensure that the read doesn't block:
00159     fFrameSize = read(fileno(fFid), fTo, fMaxSize);
00160   }
00161 #endif
00162   if (fFrameSize == 0) {
00163     handleClosure(this);
00164     return;
00165   }
00166   fNumBytesToStream -= fFrameSize;
00167 
00168   // Set the 'presentation time':
00169   if (fPlayTimePerFrame > 0 && fPreferredFrameSize > 0) {
00170     if (fPresentationTime.tv_sec == 0 && fPresentationTime.tv_usec == 0) {
00171       // This is the first frame, so use the current time:
00172       gettimeofday(&fPresentationTime, NULL);
00173     } else {
00174       // Increment by the play time of the previous data:
00175       unsigned uSeconds = fPresentationTime.tv_usec + fLastPlayTime;
00176       fPresentationTime.tv_sec += uSeconds/1000000;
00177       fPresentationTime.tv_usec = uSeconds%1000000;
00178     }
00179 
00180     // Remember the play time of this data:
00181     fLastPlayTime = (fPlayTimePerFrame*fFrameSize)/fPreferredFrameSize;
00182     fDurationInMicroseconds = fLastPlayTime;
00183   } else {
00184     // We don't know a specific play time duration for this data,
00185     // so just record the current time as being the 'presentation time':
00186     gettimeofday(&fPresentationTime, NULL);
00187   }
00188 
00189   // Inform the reader that he has data:
00190 #ifdef READ_FROM_FILES_SYNCHRONOUSLY
00191   // To avoid possible infinite recursion, we need to return to the event loop to do this:
00192   nextTask() = envir().taskScheduler().scheduleDelayedTask(0,
00193                                 (TaskFunc*)FramedSource::afterGetting, this);
00194 #else
00195   // Because the file read was done from the event loop, we can call the
00196   // 'after getting' function directly, without risk of infinite recursion:
00197   FramedSource::afterGetting(this);
00198 #endif
00199 }

Generated on Thu Feb 2 23:51:29 2012 for live by  doxygen 1.5.2