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 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00015 **********/
00016 // "liveMedia"
00017 // Copyright (c) 1996-2008 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   Boolean deleteFidOnClose = fid == stdin ? False : True;
00049   ByteStreamFileSource* newSource
00050     = new ByteStreamFileSource(env, fid, deleteFidOnClose,
00051                                preferredFrameSize, playTimePerFrame);
00052   newSource->fFileSize = GetFileSize(fileName, fid);
00053 
00054   return newSource;
00055 }
00056 
00057 ByteStreamFileSource*
00058 ByteStreamFileSource::createNew(UsageEnvironment& env, FILE* fid,
00059                                 Boolean deleteFidOnClose,
00060                                 unsigned preferredFrameSize,
00061                                 unsigned playTimePerFrame) {
00062   if (fid == NULL) return NULL;
00063 
00064   ByteStreamFileSource* newSource
00065     = new ByteStreamFileSource(env, fid, deleteFidOnClose,
00066                                preferredFrameSize, playTimePerFrame);
00067   newSource->fFileSize = GetFileSize(NULL, fid);
00068 
00069   return newSource;
00070 }
00071 
00072 void ByteStreamFileSource::seekToByteAbsolute(u_int64_t byteNumber) {
00073   SeekFile64(fFid, (int64_t)byteNumber, SEEK_SET);
00074 }
00075 
00076 void ByteStreamFileSource::seekToByteRelative(int64_t offset) {
00077   SeekFile64(fFid, offset, SEEK_CUR);
00078 }
00079 
00080 ByteStreamFileSource::ByteStreamFileSource(UsageEnvironment& env, FILE* fid,
00081                                            Boolean deleteFidOnClose,
00082                                            unsigned preferredFrameSize,
00083                                            unsigned playTimePerFrame)
00084   : FramedFileSource(env, fid), fPreferredFrameSize(preferredFrameSize),
00085     fPlayTimePerFrame(playTimePerFrame), fLastPlayTime(0), fFileSize(0),
00086     fDeleteFidOnClose(deleteFidOnClose), fHaveStartedReading(False) {
00087 }
00088 
00089 ByteStreamFileSource::~ByteStreamFileSource() {
00090   if (fFid == NULL) return;
00091 
00092 #ifndef READ_FROM_FILES_SYNCHRONOUSLY
00093   envir().taskScheduler().turnOffBackgroundReadHandling(fileno(fFid));
00094 #endif
00095 
00096   if (fDeleteFidOnClose) fclose(fFid);
00097 }
00098 
00099 void ByteStreamFileSource::doGetNextFrame() {
00100   if (feof(fFid) || ferror(fFid)) {
00101     handleClosure(this);
00102     return;
00103   }
00104 
00105 #ifdef READ_FROM_FILES_SYNCHRONOUSLY
00106   doReadFromFile();
00107 #else
00108   if (!fHaveStartedReading) {
00109     // Await readable data from the file:
00110     envir().taskScheduler().turnOnBackgroundReadHandling(fileno(fFid),
00111                (TaskScheduler::BackgroundHandlerProc*)&fileReadableHandler, this);
00112     fHaveStartedReading = True;
00113   }
00114 #endif
00115 }
00116 
00117 void ByteStreamFileSource::doStopGettingFrames() {
00118 #ifndef READ_FROM_FILES_SYNCHRONOUSLY
00119   envir().taskScheduler().turnOffBackgroundReadHandling(fileno(fFid));
00120   fHaveStartedReading = False;
00121 #endif
00122 }
00123 
00124 void ByteStreamFileSource::fileReadableHandler(ByteStreamFileSource* source, int /*mask*/) {
00125   if (!source->isCurrentlyAwaitingData()) {
00126     source->doStopGettingFrames(); // we're not ready for the data yet
00127     return;
00128   }
00129   source->doReadFromFile();
00130 }
00131 
00132 void ByteStreamFileSource::doReadFromFile() {
00133   // Try to read as many bytes as will fit in the buffer provided
00134   // (or "fPreferredFrameSize" if less)
00135   if (fPreferredFrameSize > 0 && fPreferredFrameSize < fMaxSize) {
00136     fMaxSize = fPreferredFrameSize;
00137   }
00138   fFrameSize = fread(fTo, 1, fMaxSize, fFid);
00139   if (fFrameSize == 0) {
00140     handleClosure(this);
00141     return;
00142   }
00143 
00144   // Set the 'presentation time':
00145   if (fPlayTimePerFrame > 0 && fPreferredFrameSize > 0) {
00146     if (fPresentationTime.tv_sec == 0 && fPresentationTime.tv_usec == 0) {
00147       // This is the first frame, so use the current time:
00148       gettimeofday(&fPresentationTime, NULL);
00149     } else {
00150       // Increment by the play time of the previous data:
00151       unsigned uSeconds = fPresentationTime.tv_usec + fLastPlayTime;
00152       fPresentationTime.tv_sec += uSeconds/1000000;
00153       fPresentationTime.tv_usec = uSeconds%1000000;
00154     }
00155 
00156     // Remember the play time of this data:
00157     fLastPlayTime = (fPlayTimePerFrame*fFrameSize)/fPreferredFrameSize;
00158     fDurationInMicroseconds = fLastPlayTime;
00159   } else {
00160     // We don't know a specific play time duration for this data,
00161     // so just record the current time as being the 'presentation time':
00162     gettimeofday(&fPresentationTime, NULL);
00163   }
00164 
00165   // Inform the reader that he has data:
00166 #ifdef READ_FROM_FILES_SYNCHRONOUSLY
00167   // To avoid possible infinite recursion, we need to return to the event loop to do this:
00168   nextTask() = envir().taskScheduler().scheduleDelayedTask(0,
00169                                 (TaskFunc*)FramedSource::afterGetting, this);
00170 #else
00171   // Because the file read was done from the event loop, we can call the
00172   // 'after getting' function directly, without risk of infinite recursion:
00173   FramedSource::afterGetting(this);
00174 #endif
00175 }

Generated on Tue Jul 22 06:39:05 2008 for live by  doxygen 1.5.2