1/**********
2This library is free software; you can redistribute it and/or modify it under
3the terms of the GNU Lesser General Public License as published by the
4Free Software Foundation; either version 3 of the License, or (at your
5option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
6
7This library is distributed in the hope that it will be useful, but WITHOUT
8ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
10more details.
11
12You should have received a copy of the GNU Lesser General Public License
13along with this library; if not, write to the Free Software Foundation, Inc.,
1451 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15**********/
16// "liveMedia"
17// Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved.
18// A file source that is a plain byte stream (rather than frames)
19// Implementation
20
21#include "ByteStreamFileSource.hh"
22#include "InputFile.hh"
23#include "GroupsockHelper.hh"
24
25////////// ByteStreamFileSource //////////
26
27ByteStreamFileSource*
28ByteStreamFileSource::createNew(UsageEnvironment& env, char const* fileName,
29 unsigned preferredFrameSize,
30 unsigned playTimePerFrame) {
31 FILE* fid = OpenInputFile(env, fileName);
32 if (fid == NULL) return NULL;
33
34 ByteStreamFileSource* newSource
35 = new ByteStreamFileSource(env, fid, preferredFrameSize, playTimePerFrame);
36 newSource->fFileSize = GetFileSize(fileName, fid);
37
38 return newSource;
39}
40
41ByteStreamFileSource*
42ByteStreamFileSource::createNew(UsageEnvironment& env, FILE* fid,
43 unsigned preferredFrameSize,
44 unsigned playTimePerFrame) {
45 if (fid == NULL) return NULL;
46
47 ByteStreamFileSource* newSource = new ByteStreamFileSource(env, fid, preferredFrameSize, playTimePerFrame);
48 newSource->fFileSize = GetFileSize(NULL, fid);
49
50 return newSource;
51}
52
53void ByteStreamFileSource::seekToByteAbsolute(u_int64_t byteNumber, u_int64_t numBytesToStream) {
54 SeekFile64(fFid, (int64_t)byteNumber, SEEK_SET);
55
56 fNumBytesToStream = numBytesToStream;
57 fLimitNumBytesToStream = fNumBytesToStream > 0;
58}
59
60void ByteStreamFileSource::seekToByteRelative(int64_t offset, u_int64_t numBytesToStream) {
61 SeekFile64(fFid, offset, SEEK_CUR);
62
63 fNumBytesToStream = numBytesToStream;
64 fLimitNumBytesToStream = fNumBytesToStream > 0;
65}
66
67void ByteStreamFileSource::seekToEnd() {
68 SeekFile64(fFid, 0, SEEK_END);
69}
70
71ByteStreamFileSource::ByteStreamFileSource(UsageEnvironment& env, FILE* fid,
72 unsigned preferredFrameSize,
73 unsigned playTimePerFrame)
74 : FramedFileSource(env, fid), fFileSize(0), fPreferredFrameSize(preferredFrameSize),
75 fPlayTimePerFrame(playTimePerFrame), fLastPlayTime(0),
76 fHaveStartedReading(False), fLimitNumBytesToStream(False), fNumBytesToStream(0) {
77#ifndef READ_FROM_FILES_SYNCHRONOUSLY
78 makeSocketNonBlocking(fileno(fFid));
79#endif
80
81 // Test whether the file is seekable
82 fFidIsSeekable = FileIsSeekable(fFid);
83}
84
85ByteStreamFileSource::~ByteStreamFileSource() {
86 if (fFid == NULL) return;
87
88#ifndef READ_FROM_FILES_SYNCHRONOUSLY
89 envir().taskScheduler().turnOffBackgroundReadHandling(fileno(fFid));
90#endif
91
92 CloseInputFile(fFid);
93}
94
95void ByteStreamFileSource::doGetNextFrame() {
96 if (feof(fFid) || ferror(fFid) || (fLimitNumBytesToStream && fNumBytesToStream == 0)) {
97 handleClosure();
98 return;
99 }
100
101#ifdef READ_FROM_FILES_SYNCHRONOUSLY
102 doReadFromFile();
103#else
104 if (!fHaveStartedReading) {
105 // Await readable data from the file:
106 envir().taskScheduler().turnOnBackgroundReadHandling(fileno(fFid),
107 (TaskScheduler::BackgroundHandlerProc*)&fileReadableHandler, this);
108 fHaveStartedReading = True;
109 }
110#endif
111}
112
113void ByteStreamFileSource::doStopGettingFrames() {
114 envir().taskScheduler().unscheduleDelayedTask(nextTask());
115#ifndef READ_FROM_FILES_SYNCHRONOUSLY
116 envir().taskScheduler().turnOffBackgroundReadHandling(fileno(fFid));
117 fHaveStartedReading = False;
118#endif
119}
120
121void ByteStreamFileSource::fileReadableHandler(ByteStreamFileSource* source, int /*mask*/) {
122 if (!source->isCurrentlyAwaitingData()) {
123 source->doStopGettingFrames(); // we're not ready for the data yet
124 return;
125 }
126 source->doReadFromFile();
127}
128
129void ByteStreamFileSource::doReadFromFile() {
130 // Try to read as many bytes as will fit in the buffer provided (or "fPreferredFrameSize" if less)
131 if (fLimitNumBytesToStream && fNumBytesToStream < (u_int64_t)fMaxSize) {
132 fMaxSize = (unsigned)fNumBytesToStream;
133 }
134 if (fPreferredFrameSize > 0 && fPreferredFrameSize < fMaxSize) {
135 fMaxSize = fPreferredFrameSize;
136 }
137#ifdef READ_FROM_FILES_SYNCHRONOUSLY
138 fFrameSize = fread(fTo, 1, fMaxSize, fFid);
139#else
140 if (fFidIsSeekable) {
141 fFrameSize = fread(fTo, 1, fMaxSize, fFid);
142 } else {
143 // For non-seekable files (e.g., pipes), call "read()" rather than "fread()", to ensure that the read doesn't block:
144 fFrameSize = read(fileno(fFid), fTo, fMaxSize);
145 }
146#endif
147 if (fFrameSize == 0) {
148 handleClosure();
149 return;
150 }
151 fNumBytesToStream -= fFrameSize;
152
153 // Set the 'presentation time':
154 if (fPlayTimePerFrame > 0 && fPreferredFrameSize > 0) {
155 if (fPresentationTime.tv_sec == 0 && fPresentationTime.tv_usec == 0) {
156 // This is the first frame, so use the current time:
157 gettimeofday(&fPresentationTime, NULL);
158 } else {
159 // Increment by the play time of the previous data:
160 unsigned uSeconds = fPresentationTime.tv_usec + fLastPlayTime;
161 fPresentationTime.tv_sec += uSeconds/1000000;
162 fPresentationTime.tv_usec = uSeconds%1000000;
163 }
164
165 // Remember the play time of this data:
166 fLastPlayTime = (fPlayTimePerFrame*fFrameSize)/fPreferredFrameSize;
167 fDurationInMicroseconds = fLastPlayTime;
168 } else {
169 // We don't know a specific play time duration for this data,
170 // so just record the current time as being the 'presentation time':
171 gettimeofday(&fPresentationTime, NULL);
172 }
173
174 // Inform the reader that he has data:
175#ifdef READ_FROM_FILES_SYNCHRONOUSLY
176 // To avoid possible infinite recursion, we need to return to the event loop to do this:
177 nextTask() = envir().taskScheduler().scheduleDelayedTask(0,
178 (TaskFunc*)FramedSource::afterGetting, this);
179#else
180 // Because the file read was done from the event loop, we can call the
181 // 'after getting' function directly, without risk of infinite recursion:
182 FramedSource::afterGetting(this);
183#endif
184}
185