322 lines
11 KiB
C++
322 lines
11 KiB
C++
/*
|
|
*
|
|
* @APPLE_LICENSE_HEADER_START@
|
|
*
|
|
* Copyright (c) 1999-2008 Apple Inc. All Rights Reserved.
|
|
*
|
|
* This file contains Original Code and/or Modifications of Original Code
|
|
* as defined in and that are subject to the Apple Public Source License
|
|
* Version 2.0 (the 'License'). You may not use this file except in
|
|
* compliance with the License. Please obtain a copy of the License at
|
|
* http://www.opensource.apple.com/apsl/ and read it before using this
|
|
* file.
|
|
*
|
|
* The Original Code and all software distributed under the License are
|
|
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
|
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
|
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
|
* Please see the License for the specific language governing rights and
|
|
* limitations under the License.
|
|
*
|
|
* @APPLE_LICENSE_HEADER_END@
|
|
*
|
|
*/
|
|
|
|
|
|
#include <stdlib.h>
|
|
#include "SafeStdLib.h"
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
#ifndef __MacOSX__
|
|
#include "getopt.h"
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
|
|
#include "RTPFileDefs.h"
|
|
#include "QTRTPFile.h"
|
|
#include "QTHintTrack.h"
|
|
#include "OSHeaders.h"
|
|
|
|
|
|
class QTRTPGenFile : public QTRTPFile
|
|
{
|
|
public:
|
|
|
|
QTRTPGenFile() : QTRTPFile() {}
|
|
virtual ~QTRTPGenFile() {}
|
|
|
|
// Accessors
|
|
UInt32 GetNumHintTracks() { return fNumHintTracks; }
|
|
RTPTrackListEntry* GetFirstTrack() { return fFirstTrack; }
|
|
};
|
|
|
|
|
|
UInt8* WriteTempFile(QTRTPFile* inQTRTPFile, int inTempFile, UInt32* outNumBlocks);
|
|
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
|
|
if (argc <= 1)
|
|
{
|
|
qtss_printf("Usage: rtpfilegen *hintedqtfilename*\n");
|
|
exit(0);
|
|
}
|
|
|
|
char* theFileName = argv[1];
|
|
|
|
QTRTPFile::Initialize();
|
|
|
|
// Init our qt file
|
|
QTRTPGenFile theFile;
|
|
QTRTPFile::ErrorCode initErr = theFile.Initialize(theFileName);
|
|
switch (initErr)
|
|
{
|
|
case QTRTPFile::errFileNotFound:
|
|
qtss_printf("Movie file not found\n"); exit(0);
|
|
case QTRTPFile::errInvalidQuickTimeFile:
|
|
qtss_printf("File is not a hinted quicktime file\n"); exit(0);
|
|
case QTRTPFile::errNoHintTracks:
|
|
qtss_printf("File has no hint tracks\n"); exit(0);
|
|
case QTRTPFile::errInternalError:
|
|
qtss_printf("Internal error\n"); exit(0);
|
|
|
|
case QTRTPFile::errNoError:
|
|
case QTRTPFile::errTrackIDNotFound:
|
|
case QTRTPFile::errCallAgain:
|
|
//noops
|
|
break;
|
|
}
|
|
|
|
// Open our output file
|
|
char* outputFileName = new char[::strlen(theFileName) + 5];
|
|
::strcpy(outputFileName, theFileName);
|
|
::strcat(outputFileName, ".rtp");
|
|
int theOutFile = open(outputFileName, O_WRONLY | O_CREAT | O_TRUNC);
|
|
if (theOutFile == -1)
|
|
{ qtss_printf("Failed to open output file at %s.\n", outputFileName); exit(0); }
|
|
|
|
// Open a temp file
|
|
char* tempFileName = new char[::strlen(theFileName) + 6];
|
|
::strcpy(tempFileName, theFileName);
|
|
::strcat(tempFileName, ".temp");
|
|
int theTempFile = open(tempFileName, O_WRONLY | O_CREAT | O_TRUNC);
|
|
if (theTempFile == -1)
|
|
{ qtss_printf("Failed to open temp file at %s.\n", tempFileName); exit(0); }
|
|
|
|
// Setup all our tracks
|
|
UInt32 theMaxTrackID = 0; // These variables are needed to write the file header
|
|
UInt32 theNumTracks = 0;
|
|
QTRTPFile::RTPTrackListEntry* curTrack = theFile.GetFirstTrack();
|
|
while (curTrack != NULL)
|
|
{
|
|
if (theMaxTrackID < curTrack->TrackID)
|
|
theMaxTrackID = curTrack->TrackID;
|
|
|
|
theNumTracks++;
|
|
(void)theFile.AddTrack(curTrack->TrackID);
|
|
// Make the cookie be the track ID, so we know what the track ID is when
|
|
// we are retreiving packets
|
|
theFile.SetTrackCookies(curTrack->TrackID, (void*) NULL, curTrack->TrackID);
|
|
|
|
curTrack = curTrack->NextTrack;
|
|
}
|
|
|
|
// Seek to time 0
|
|
(void)theFile.Seek(0);
|
|
|
|
// Write status
|
|
qtss_printf("Generating RTP packets to temp file at %s...\n", tempFileName);
|
|
|
|
// Write out all the packets to the temp file
|
|
UInt32 theNumBlocks = 0;
|
|
UInt8* theBlockArray = WriteTempFile(&theFile, theTempFile, &theNumBlocks);
|
|
::close(theTempFile);
|
|
|
|
// Write status
|
|
qtss_printf("Generating RTP file at %s. Copying packets...\n", outputFileName);
|
|
|
|
// Get SDP
|
|
int theSDPLen = 0;
|
|
char* theSDPFile = theFile.GetSDPFile(&theSDPLen);
|
|
|
|
// Write file header
|
|
RTPFileHeader theHeader;
|
|
theHeader.fSDPLen = theSDPLen;
|
|
theHeader.fNumTracks = theFile.GetNumHintTracks();
|
|
theHeader.fMaxTrackID = theMaxTrackID;
|
|
theHeader.fBlockMapSize = theNumBlocks;
|
|
theHeader.fDataStartPos = theNumBlocks + theSDPLen + sizeof(RTPFileHeader) +
|
|
(sizeof(RTPFileTrackInfo) * theNumTracks);
|
|
theHeader.fMovieDuration = theFile.GetMovieDuration();
|
|
|
|
int theErr = ::write(theOutFile, &theHeader, sizeof(theHeader));
|
|
if (theErr != sizeof(theHeader))
|
|
{ qtss_printf("Write operation failed on output file\n"); exit(0); }
|
|
|
|
// Write the SDP data
|
|
theErr = ::write(theOutFile, theSDPFile, theSDPLen);
|
|
if (theErr != theSDPLen)
|
|
{ qtss_printf("Write operation failed on output file\n"); exit(0); }
|
|
|
|
// Write track info
|
|
curTrack = theFile.GetFirstTrack();
|
|
while (curTrack != NULL)
|
|
{
|
|
RTPFileTrackInfo theTrackInfo;
|
|
theTrackInfo.fID = curTrack->TrackID;
|
|
theTrackInfo.fTimescale = curTrack->HintTrack->GetRTPTimescale();
|
|
theTrackInfo.fBytesInTrack = curTrack->HintTrack->GetTotalRTPBytes();
|
|
theTrackInfo.fDuration = curTrack->HintTrack->GetDurationInSeconds();
|
|
|
|
theErr = ::write(theOutFile, &theTrackInfo, sizeof(theTrackInfo));
|
|
if (theErr != sizeof(theTrackInfo))
|
|
{ qtss_printf("Write operation failed on output file\n"); exit(0); }
|
|
|
|
curTrack = curTrack->NextTrack;
|
|
}
|
|
|
|
// Write the block map
|
|
UInt32 numWritten = ::write(theOutFile, theBlockArray, theNumBlocks);
|
|
if (numWritten != theNumBlocks)
|
|
{ qtss_printf("Write operation failed on output file\n"); exit(0); }
|
|
|
|
//Copy the temp file into the out file
|
|
theTempFile = open(tempFileName, O_RDONLY);
|
|
if (theTempFile == -1)
|
|
{ qtss_printf("Failed to open temp file for reading at %s.\n", tempFileName); exit(0); }
|
|
|
|
while (true)
|
|
{
|
|
char copyBuffer[kBlockSize];
|
|
|
|
// Read a block out of the temp file
|
|
theErr = ::read(theTempFile, ©Buffer[0], kBlockSize);
|
|
if (theErr <= 0)
|
|
break;
|
|
|
|
int lenWritten = ::write(theOutFile, ©Buffer[0], theErr);
|
|
if (theErr != lenWritten)
|
|
{ qtss_printf("Write operation failed on output file\n"); exit(0); }
|
|
}
|
|
|
|
qtss_printf("RTP file generation complete\n");
|
|
|
|
// Delete the temp file
|
|
(void)::unlink(tempFileName);
|
|
|
|
// close the output file
|
|
::close(theOutFile);
|
|
|
|
// We're done!
|
|
}
|
|
|
|
|
|
// Returns the complete block map
|
|
UInt8* WriteTempFile(QTRTPFile* inQTRTPFile, int inTempFile, UInt32* outNumBlocks)
|
|
{
|
|
int theErr = -1;
|
|
UInt32 theNumPackets = 0;
|
|
|
|
// Write all packets to the temp file. Pad out blocks appropriately
|
|
UInt32 offsetInBlock = 0;
|
|
|
|
// As we write out packets, build our block map
|
|
UInt8* theBlockMap = new UInt8[1024];
|
|
UInt32 curBlockMapSize = 1024;
|
|
UInt32 curBlockMapIndex = 0;
|
|
Float64 theLastBlockTime = 0;
|
|
|
|
while (true)
|
|
{
|
|
RTPFilePacket thePacketHeader;
|
|
char* thePacketData = NULL;
|
|
int thePacketLength = 0;
|
|
|
|
thePacketHeader.fTransmitTime = inQTRTPFile->GetNextPacket(&thePacketData, &thePacketLength);
|
|
if (thePacketData == NULL)
|
|
// We're done with all tracks
|
|
break;
|
|
|
|
thePacketHeader.fPacketLength = thePacketLength;
|
|
thePacketHeader.fTrackID = inQTRTPFile->GetLastPacketTrack()->Cookie2;
|
|
|
|
// Make sure there is enough room in the current block for this
|
|
// packet plus a header for the next packet. If not, move onto the next block
|
|
if ((offsetInBlock + thePacketHeader.fPacketLength + (2 * sizeof(RTPFilePacket))) > kBlockSize)
|
|
{
|
|
// Write out a pad packet
|
|
RTPFilePacket pad;
|
|
pad.fTrackID |= kPaddingBit;
|
|
|
|
theErr = ::write(inTempFile, &pad, sizeof(pad));
|
|
if (theErr < (int) sizeof(pad))
|
|
{ qtss_printf("Write operation failed on temp file\n"); exit(0); }
|
|
|
|
offsetInBlock += sizeof(pad);
|
|
SInt32 spaceRemaining = kBlockSize - offsetInBlock;
|
|
Assert(spaceRemaining >= 0);
|
|
|
|
// Fill out the rest of this block with crap
|
|
char* dumpBuf = new char[spaceRemaining];
|
|
::memset(dumpBuf, 0xFF, spaceRemaining);//Fill with FFs so we can debug easier
|
|
theErr = ::write(inTempFile, dumpBuf, spaceRemaining);
|
|
if (theErr < spaceRemaining)
|
|
{ qtss_printf("Write operation failed on temp file\n"); exit(0); }
|
|
|
|
delete [] dumpBuf;
|
|
|
|
offsetInBlock = 0;
|
|
}
|
|
|
|
// If this is the first packet in a block, we should mark the time in the
|
|
// Write the packet header and the packet data
|
|
if (offsetInBlock == 0)
|
|
{
|
|
// What is the offset between the first packet in the last block and this block?
|
|
Float64 theOffset = thePacketHeader.fTransmitTime - theLastBlockTime;
|
|
if (theOffset < 0)
|
|
theOffset = 0; //This may happen with the stupid negative times
|
|
theLastBlockTime = thePacketHeader.fTransmitTime;
|
|
if (theLastBlockTime < 0)
|
|
theLastBlockTime = 0; //This may happen with the stupid negative times
|
|
|
|
Assert(theOffset < 255);
|
|
theBlockMap[curBlockMapIndex] = (UInt8)theOffset;
|
|
|
|
// We may need to reallocate the block array
|
|
curBlockMapIndex++;
|
|
if (curBlockMapIndex == curBlockMapSize)
|
|
{
|
|
UInt8* newBlockArray = new UInt8[curBlockMapSize * 2];
|
|
::memcpy(newBlockArray, theBlockMap, curBlockMapSize);
|
|
curBlockMapSize *= 2;
|
|
|
|
delete [] theBlockMap;
|
|
theBlockMap = newBlockArray;
|
|
}
|
|
}
|
|
|
|
theNumPackets++;
|
|
theErr = ::write(inTempFile, &thePacketHeader, sizeof(thePacketHeader));
|
|
if (theErr < (int) sizeof(thePacketHeader))
|
|
{ qtss_printf("Write operation failed on temp file\n"); exit(0); }
|
|
|
|
theErr = ::write(inTempFile, thePacketData, thePacketLength);
|
|
if (theErr < thePacketLength)
|
|
{ qtss_printf("Write operation failed on temp file\n"); exit(0); }
|
|
|
|
// update our block offset
|
|
offsetInBlock += thePacketLength + sizeof(thePacketHeader);
|
|
}
|
|
|
|
qtss_printf("Finished writing packets. Wrote %"_U32BITARG_" packets to temp file.\n", theNumPackets);
|
|
*outNumBlocks = curBlockMapIndex-1;
|
|
return theBlockMap;
|
|
}
|