/********** This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. (See .) This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA **********/ // "liveMedia" // Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. // File sinks // Implementation #if (defined(__WIN32__) || defined(_WIN32)) && !defined(_WIN32_WCE) #include #include #endif #include "FileSink.hh" #include "GroupsockHelper.hh" #include "OutputFile.hh" ////////// FileSink ////////// FileSink::FileSink(UsageEnvironment& env, FILE* fid, unsigned bufferSize, char const* perFrameFileNamePrefix) : MediaSink(env), fOutFid(fid), fBufferSize(bufferSize), fSamePresentationTimeCounter(0) { fBuffer = new unsigned char[bufferSize]; if (perFrameFileNamePrefix != NULL) { fPerFrameFileNamePrefix = strDup(perFrameFileNamePrefix); fPerFrameFileNameBuffer = new char[strlen(perFrameFileNamePrefix) + 100]; } else { fPerFrameFileNamePrefix = NULL; fPerFrameFileNameBuffer = NULL; } fPrevPresentationTime.tv_sec = ~0; fPrevPresentationTime.tv_usec = 0; } FileSink::~FileSink() { delete[] fPerFrameFileNameBuffer; delete[] fPerFrameFileNamePrefix; delete[] fBuffer; if (fOutFid != NULL) fclose(fOutFid); } FileSink* FileSink::createNew(UsageEnvironment& env, char const* fileName, unsigned bufferSize, Boolean oneFilePerFrame) { do { FILE* fid; char const* perFrameFileNamePrefix; if (oneFilePerFrame) { // Create the fid for each frame fid = NULL; perFrameFileNamePrefix = fileName; } else { // Normal case: create the fid once fid = OpenOutputFile(env, fileName); if (fid == NULL) break; perFrameFileNamePrefix = NULL; } return new FileSink(env, fid, bufferSize, perFrameFileNamePrefix); } while (0); return NULL; } Boolean FileSink::continuePlaying() { if (fSource == NULL) return False; fSource->getNextFrame(fBuffer, fBufferSize, afterGettingFrame, this, onSourceClosure, this); return True; } void FileSink::afterGettingFrame(void* clientData, unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime, unsigned /*durationInMicroseconds*/) { FileSink* sink = (FileSink*)clientData; sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime); } void FileSink::addData(unsigned char const* data, unsigned dataSize, struct timeval presentationTime) { if (fPerFrameFileNameBuffer != NULL && fOutFid == NULL) { // Special case: Open a new file on-the-fly for this frame if (presentationTime.tv_usec == fPrevPresentationTime.tv_usec && presentationTime.tv_sec == fPrevPresentationTime.tv_sec) { // The presentation time is unchanged from the previous frame, so we add a 'counter' // suffix to the file name, to distinguish them: sprintf(fPerFrameFileNameBuffer, "%s-%lu.%06lu-%u", fPerFrameFileNamePrefix, presentationTime.tv_sec, presentationTime.tv_usec, ++fSamePresentationTimeCounter); } else { sprintf(fPerFrameFileNameBuffer, "%s-%lu.%06lu", fPerFrameFileNamePrefix, presentationTime.tv_sec, presentationTime.tv_usec); fPrevPresentationTime = presentationTime; // for next time fSamePresentationTimeCounter = 0; // for next time } fOutFid = OpenOutputFile(envir(), fPerFrameFileNameBuffer); } // Write to our file: #ifdef TEST_LOSS static unsigned const framesPerPacket = 10; static unsigned const frameCount = 0; static Boolean const packetIsLost; if ((frameCount++)%framesPerPacket == 0) { packetIsLost = (our_random()%10 == 0); // simulate 10% packet loss ##### } if (!packetIsLost) #endif if (fOutFid != NULL && data != NULL) { fwrite(data, 1, dataSize, fOutFid); } } void FileSink::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime) { if (numTruncatedBytes > 0) { envir() << "FileSink::afterGettingFrame(): The input frame data was too large for our buffer size (" << fBufferSize << "). " << numTruncatedBytes << " bytes of trailing data was dropped! Correct this by increasing the \"bufferSize\" parameter in the \"createNew()\" call to at least " << fBufferSize + numTruncatedBytes << "\n"; } addData(fBuffer, frameSize, presentationTime); if (fOutFid == NULL || fflush(fOutFid) == EOF) { // The output file has closed. Handle this the same way as if the input source had closed: if (fSource != NULL) fSource->stopGettingFrames(); onSourceClosure(); return; } if (fPerFrameFileNameBuffer != NULL) { if (fOutFid != NULL) { fclose(fOutFid); fOutFid = NULL; } } // Then try getting the next frame: continuePlaying(); }