152 lines
5.3 KiB
C++
Executable File
152 lines
5.3 KiB
C++
Executable File
/**********
|
|
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 <http://www.gnu.org/copyleft/lesser.html>.)
|
|
|
|
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 <io.h>
|
|
#include <fcntl.h>
|
|
#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();
|
|
}
|