572 lines
18 KiB
C++
572 lines
18 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@
|
||
|
*
|
||
|
*/
|
||
|
/*
|
||
|
File: RTPFileSession.cpp
|
||
|
|
||
|
Contains:
|
||
|
|
||
|
|
||
|
|
||
|
*/
|
||
|
|
||
|
#define RTPFILESESSIONDEBUG 0
|
||
|
|
||
|
#include "RTPFileSession.h"
|
||
|
#include "OSMemory.h"
|
||
|
|
||
|
static OSRefTable sOpenFileMap;
|
||
|
|
||
|
|
||
|
RTPFileSession::RTPFileSession()
|
||
|
: fFileSource(NULL),
|
||
|
fFileLength(0),
|
||
|
fCurrentPosition(0),
|
||
|
fFile(NULL),
|
||
|
fTrackInfo(NULL),
|
||
|
fNumTracksEnabled(0),
|
||
|
fReadBuffer(NULL),
|
||
|
fReadBufferOffset(0),
|
||
|
fDataBuffer(NULL),
|
||
|
fDataBufferSize(0),
|
||
|
fDataBufferLen(0),
|
||
|
fCurrentPacket(NULL),
|
||
|
fAddedTracksRTPBytes(0)
|
||
|
{}
|
||
|
|
||
|
RTPFileSession::~RTPFileSession()
|
||
|
{
|
||
|
// Check to see if we should destroy this file
|
||
|
OSMutexLocker locker (sOpenFileMap.GetMutex());
|
||
|
|
||
|
#if RTPFILESESSIONDEBUG
|
||
|
qtss_printf("Dropping refcount on file\n");
|
||
|
#endif
|
||
|
if (fFile == NULL)
|
||
|
return;
|
||
|
|
||
|
sOpenFileMap.Release(fFile->GetRef());
|
||
|
if (fFile->GetRef()->GetRefCount() == 0)
|
||
|
{
|
||
|
#if RTPFILESESSIONDEBUG
|
||
|
qtss_printf("Refcount dropped to 0. Deleting file\n");
|
||
|
#endif
|
||
|
sOpenFileMap.UnRegister(fFile->GetRef());
|
||
|
delete fFile;
|
||
|
}
|
||
|
|
||
|
// Delete our data buffer
|
||
|
delete [] fDataBuffer;
|
||
|
delete [] fTrackInfo;
|
||
|
}
|
||
|
|
||
|
RTPFileSession::ErrorCode RTPFileSession::Initialize(StrPtrLen& inFilePath, Float32 inBufferSeconds)
|
||
|
{
|
||
|
Assert(fFile == NULL);
|
||
|
|
||
|
// Check to see if this file is already open
|
||
|
OSMutexLocker locker(sOpenFileMap.GetMutex());
|
||
|
OSRef* theFileRef = sOpenFileMap.Resolve((StrPtrLen*)&inFilePath);
|
||
|
|
||
|
if (theFileRef == NULL)
|
||
|
{
|
||
|
//qtss_printf("Didn't find file in map. Creating new one\n");
|
||
|
fFile = NEW RTPFile();
|
||
|
ErrorCode theErr = fFile->Initialize(inFilePath);
|
||
|
if (theErr != errNoError)
|
||
|
{
|
||
|
delete fFile;
|
||
|
fFile = NULL;
|
||
|
return theErr;
|
||
|
}
|
||
|
|
||
|
OS_Error osErr = sOpenFileMap.Register(fFile->GetRef());
|
||
|
Assert(osErr == OS_NoErr);
|
||
|
|
||
|
//unless we do this, the refcount won't increment (and we'll delete the session prematurely
|
||
|
OSRef* debug = sOpenFileMap.Resolve((StrPtrLen*)&inFilePath);
|
||
|
Assert(debug == fFile->GetRef());
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//qtss_printf("Found file. Refcounting.\n");
|
||
|
fFile = (RTPFile*)theFileRef->GetObject();
|
||
|
}
|
||
|
|
||
|
//Open the file no matter what
|
||
|
//fFileSource.Set(inFilePath.Ptr);
|
||
|
//Assert(fFileSource.GetLength() > 0);
|
||
|
QTSS_Error theErr = QTSS_OpenFileObject(inFilePath.Ptr, 0, &fFileSource);
|
||
|
Assert(theErr == QTSS_NoErr);
|
||
|
|
||
|
//
|
||
|
// Get the file length
|
||
|
UInt32 theLen = sizeof(fFileLength);
|
||
|
theErr = QTSS_GetValue(fFileSource, qtssFlObjLength, 0, &fFileLength, &theLen);
|
||
|
Assert(theErr == QTSS_NoErr);
|
||
|
Assert(theLen == sizeof(fFileLength));
|
||
|
|
||
|
// Allocate our data buffer
|
||
|
fDataBufferSize = this->PowerOf2Floor((UInt32)(inBufferSeconds * fFile->GetBytesPerSecond()));
|
||
|
|
||
|
// Check to see if the size is out of range. If so, adjust it
|
||
|
if (fDataBufferSize > kMaxDataBufferSize)
|
||
|
fDataBufferSize = kMaxDataBufferSize;
|
||
|
if (fDataBufferSize < kBlockSize)
|
||
|
fDataBufferSize = kBlockSize;
|
||
|
|
||
|
fReadBuffer = fDataBuffer = NEW UInt8[fDataBufferSize];
|
||
|
|
||
|
// Allocate a buffer of TrackInfos
|
||
|
fTrackInfo = NEW RTPFileSessionTrackInfo[fFile->GetMaxTrackNumber() + 1];
|
||
|
::memset(fTrackInfo, 0, fFile->GetMaxTrackNumber() * sizeof(RTPFileSessionTrackInfo));
|
||
|
return errNoError;
|
||
|
}
|
||
|
|
||
|
RTPFileSession::ErrorCode RTPFileSession::AddTrack(UInt32 trackID)
|
||
|
{
|
||
|
if (fFile->TrackExists(trackID))
|
||
|
{
|
||
|
if (!fTrackInfo[trackID].fEnabled)
|
||
|
{
|
||
|
fTrackInfo[trackID].fEnabled = true;
|
||
|
fAddedTracksRTPBytes += fFile->GetTrackBytes(trackID);
|
||
|
fNumTracksEnabled++;
|
||
|
}
|
||
|
else
|
||
|
return errTrackAlreadyAdded;
|
||
|
}
|
||
|
else
|
||
|
return errTrackDoesntExist;
|
||
|
return errNoError;
|
||
|
}
|
||
|
|
||
|
|
||
|
RTPFileSession::ErrorCode RTPFileSession::Seek(Float64 inTime)
|
||
|
{
|
||
|
if ((inTime < 0) || (inTime > fFile->GetMovieDuration()))
|
||
|
return errSeekToNonexistentTime;
|
||
|
|
||
|
UInt64 theBlockLocation = fFile->GetBlockLocation(inTime);
|
||
|
Assert(theBlockLocation >= fFile->fHeader.fDataStartPos);
|
||
|
Assert(theBlockLocation < fFileLength);
|
||
|
|
||
|
// Seek to the right file location.
|
||
|
//fFileSource.Seek(theBlockLocation);
|
||
|
QTSS_Error theErr = QTSS_Seek(fFileSource, theBlockLocation);
|
||
|
Assert(theErr == QTSS_NoErr);
|
||
|
fCurrentPosition = theBlockLocation;
|
||
|
|
||
|
// Read the file data
|
||
|
this->ReadAndAdvise();
|
||
|
|
||
|
for (UInt32 x = 0; x <= fFile->GetMaxTrackNumber(); x++)
|
||
|
fTrackInfo[x].fMarked = false;
|
||
|
|
||
|
//
|
||
|
// We need to find out what the first packet is for each enabled track.
|
||
|
// So scan ahead until we find the very first packet we need to send.
|
||
|
// At that point, "freeze" the current block in memory, and that position,
|
||
|
// because that's the position we'll be starting from when GetNextPacket gets
|
||
|
// called. In order to "freeze" we store lots of info about the current position
|
||
|
// on the stack with the variables defined below.
|
||
|
//
|
||
|
// Keep on going until we find the first packets for all the enabled tracks,
|
||
|
// and if that involves traversing multiple blocks, keep those blocks in a temporary
|
||
|
// buffer, allowing us to easily go back to the start point when done.
|
||
|
|
||
|
UInt8* theStartPos = NULL;
|
||
|
UInt32 origDataBufferLen = 0;
|
||
|
UInt64 currentFileOffset = 0;
|
||
|
UInt32 tracksFound = 0;
|
||
|
|
||
|
// Needed to call GetNextPacket
|
||
|
UInt8* thePacketP = NULL;
|
||
|
UInt32 thePacketLength = 0;
|
||
|
void* theCookie = NULL;
|
||
|
|
||
|
while (tracksFound < fNumTracksEnabled)
|
||
|
{
|
||
|
Float64 theTransmitTime = this->GetNextPacket(&thePacketP, &thePacketLength, &theCookie);
|
||
|
if (thePacketP == NULL)
|
||
|
{
|
||
|
Assert(tracksFound > 0);
|
||
|
break; // We're at the end of the file!
|
||
|
}
|
||
|
// Ignore < 0 timed packets
|
||
|
RTPFilePacket* thePacket = (RTPFilePacket*)thePacketP;
|
||
|
Assert((thePacket - 1)->fTransmitTime == theTransmitTime);
|
||
|
if (theTransmitTime < 0)
|
||
|
theTransmitTime = 0;
|
||
|
|
||
|
UInt32 theTrackID = (thePacket - 1)->fTrackID;
|
||
|
|
||
|
if ((theTransmitTime >= inTime) && (!fTrackInfo[theTrackID].fMarked) &&
|
||
|
(fTrackInfo[theTrackID].fEnabled))
|
||
|
{
|
||
|
// This is the first packet for this track after our fCurrentPtr mark.
|
||
|
// Record the first seq # and timestamp of the packet
|
||
|
UInt16* theSeqNumPtr = (UInt16*)thePacketP;
|
||
|
UInt32* theTimestampPtr = (UInt32*)thePacketP;
|
||
|
|
||
|
fTrackInfo[theTrackID].fSeekSeqNumber = theSeqNumPtr[1];
|
||
|
fTrackInfo[theTrackID].fSeekTimestamp = theTimestampPtr[1];
|
||
|
fTrackInfo[theTrackID].fMarked = true;
|
||
|
|
||
|
if (tracksFound == 0)
|
||
|
{
|
||
|
//
|
||
|
// If this is the first packet that we're going to send (for all
|
||
|
// streams), then mark the position, and make sure that if we
|
||
|
// need to dump this buffer to find first packets for other tracks,
|
||
|
// we'll be able to come back to this very place so we can start streaming.
|
||
|
fReadBuffer = NULL;
|
||
|
theStartPos = (UInt8*)(thePacket-1);
|
||
|
origDataBufferLen = fDataBufferLen;
|
||
|
currentFileOffset = fCurrentPosition;
|
||
|
}
|
||
|
|
||
|
tracksFound++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (fReadBuffer != NULL)
|
||
|
{
|
||
|
// We had to skip ahead in the file. Restore everything to
|
||
|
// the way it was when we found the first packet, so GetNextPacket
|
||
|
// will work fine.
|
||
|
delete [] fReadBuffer;
|
||
|
fReadBuffer = fDataBuffer;
|
||
|
Assert(origDataBufferLen > 0);
|
||
|
fDataBufferLen = origDataBufferLen;
|
||
|
Assert(currentFileOffset > 0);
|
||
|
//fFileSource.Seek(currentFileOffset);
|
||
|
theErr = QTSS_Seek(fFileSource, theBlockLocation);
|
||
|
Assert(theErr == QTSS_NoErr);
|
||
|
}
|
||
|
|
||
|
// Start at the first packet we need to send.
|
||
|
Assert(theStartPos != NULL);
|
||
|
fCurrentPacket = theStartPos;
|
||
|
|
||
|
return errNoError;
|
||
|
}
|
||
|
|
||
|
UInt16 RTPFileSession::GetNextTrackSequenceNumber(UInt32 inTrackID)
|
||
|
{
|
||
|
Assert(inTrackID <= fFile->GetMaxTrackNumber());
|
||
|
Assert(fTrackInfo[inTrackID].fMarked);
|
||
|
return fTrackInfo[inTrackID].fSeekSeqNumber;
|
||
|
}
|
||
|
|
||
|
UInt32 RTPFileSession::GetSeekTimestamp(UInt32 inTrackID)
|
||
|
{
|
||
|
Assert(inTrackID <= fFile->GetMaxTrackNumber());
|
||
|
Assert(fTrackInfo[inTrackID].fMarked);
|
||
|
return fTrackInfo[inTrackID].fSeekTimestamp;
|
||
|
}
|
||
|
|
||
|
|
||
|
Float64 RTPFileSession::GetNextPacket(UInt8** outPacket, UInt32* outPacketLength, void** outCookie)
|
||
|
{
|
||
|
Bool16 isValidPacket = false;
|
||
|
RTPFilePacket* thePacket = NULL;
|
||
|
|
||
|
// Loop until we find a legal packet
|
||
|
while (!isValidPacket)
|
||
|
{
|
||
|
// If we are between blocks, read the next block
|
||
|
if (fCurrentPacket == NULL)
|
||
|
{
|
||
|
if (fCurrentPosition == fFileLength)
|
||
|
{
|
||
|
|
||
|
#if RTPFILESESSIONDEBUG
|
||
|
qtss_printf("RTPFileSession::GetNextPacket fCurrentPosition == fFileLength quit\n");
|
||
|
#endif
|
||
|
*outPacket = NULL;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
this->ReadAndAdvise();
|
||
|
}
|
||
|
Assert(fCurrentPacket != NULL);
|
||
|
thePacket = (RTPFilePacket*)fCurrentPacket;
|
||
|
|
||
|
if (thePacket->fTrackID & kPaddingBit)
|
||
|
{
|
||
|
// We hit a padding packet, move the fCurrentPacket pointer to the next block
|
||
|
fReadBufferOffset += kBlockSize;
|
||
|
fReadBufferOffset &= kBlockMask; //Rounds down to the nearest block size
|
||
|
|
||
|
#if RTPFILESESSIONDEBUG
|
||
|
qtss_printf("Found a pad packet. Moving on\n");
|
||
|
#endif
|
||
|
// Check to make sure we aren't at the end of the buffer
|
||
|
if (fReadBufferOffset >= fDataBufferLen)
|
||
|
fCurrentPacket = NULL;
|
||
|
else
|
||
|
fCurrentPacket = fDataBuffer + fReadBufferOffset;
|
||
|
}
|
||
|
else if (!fTrackInfo[thePacket->fTrackID].fEnabled)
|
||
|
{
|
||
|
// This is a valid packet, but track not enabled, so skip it
|
||
|
Assert(thePacket->fTrackID <= fFile->GetMaxTrackNumber());
|
||
|
this->SkipToNextPacket(thePacket);
|
||
|
}
|
||
|
else
|
||
|
// This is a valid packet, and the track is enabled
|
||
|
isValidPacket = true;
|
||
|
}
|
||
|
|
||
|
// We must have a valid packet here
|
||
|
Assert(thePacket != NULL);
|
||
|
Assert(thePacket->fTrackID <= fFile->GetMaxTrackNumber());
|
||
|
|
||
|
// Set the return values
|
||
|
*outPacket = (UInt8*)(thePacket + 1);
|
||
|
*outPacketLength = thePacket->fPacketLength;
|
||
|
*outCookie = fTrackInfo[thePacket->fTrackID].fCookie;
|
||
|
|
||
|
// Set the packet's SSRC
|
||
|
UInt32* ssrcPtr = (UInt32*)*outPacket;
|
||
|
ssrcPtr[2] = fTrackInfo[thePacket->fTrackID].fSSRC;
|
||
|
|
||
|
Float64 transmitTime = thePacket->fTransmitTime;
|
||
|
|
||
|
this->SkipToNextPacket(thePacket);
|
||
|
return transmitTime;
|
||
|
}
|
||
|
|
||
|
void RTPFileSession::SkipToNextPacket(RTPFilePacket* inCurPacket)
|
||
|
{
|
||
|
// Skip over this packet
|
||
|
Assert(inCurPacket->fPacketLength < 1500);
|
||
|
fReadBufferOffset += inCurPacket->fPacketLength;
|
||
|
fCurrentPacket += (inCurPacket->fPacketLength + sizeof(RTPFilePacket));
|
||
|
|
||
|
// Check to see if we need to read more data
|
||
|
if (fReadBufferOffset >= fDataBufferLen)
|
||
|
{
|
||
|
#if RTPFILESESSIONDEBUG
|
||
|
qtss_printf("In SkipToNextPacket. Out of data\n");
|
||
|
#endif
|
||
|
fCurrentPacket = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void RTPFileSession::ReadAndAdvise()
|
||
|
{
|
||
|
if (fReadBuffer == NULL)
|
||
|
{
|
||
|
// In some situations, callers of this function may not want the fDataBuffer
|
||
|
// to be disturbed (see Seek()). If that's the case, the caller will set
|
||
|
// fReadBuffer to NULL, and we must allocate it here
|
||
|
fReadBuffer = NEW UInt8[fDataBufferSize];
|
||
|
}
|
||
|
|
||
|
// Read the next block. There should always be at least one packet
|
||
|
// here, as we have a valid block in the block table.
|
||
|
#if RTPFILESESSIONDEBUG
|
||
|
//qtss_printf("Moving onto next block. File loc: %qd\n",fFileSource.GetCurOffset());
|
||
|
#endif
|
||
|
fDataBufferLen = 0;
|
||
|
//(void)fFileSource.Read(fDataBuffer, fDataBufferSize, &fDataBufferLen);
|
||
|
QTSS_Error theErr = QTSS_Read(fFileSource, fDataBuffer, fDataBufferSize, &fDataBufferLen);
|
||
|
Assert(theErr == QTSS_NoErr);
|
||
|
Assert(fDataBufferLen > sizeof(RTPFilePacket));
|
||
|
fCurrentPosition += fDataBufferLen;
|
||
|
|
||
|
// Now do an advise for the next block, if this block isn't the last.
|
||
|
if (fCurrentPosition < fFileLength)
|
||
|
{
|
||
|
Assert(fDataBufferLen == fDataBufferSize);
|
||
|
//fFileSource.Advise(fFileSource.GetCurOffset(), fDataBufferSize);
|
||
|
theErr = QTSS_Advise(fFileSource, fCurrentPosition, fDataBufferSize);
|
||
|
}
|
||
|
fReadBufferOffset = 0;
|
||
|
fCurrentPacket = fDataBuffer;
|
||
|
}
|
||
|
|
||
|
UInt32 RTPFileSession::PowerOf2Floor(UInt32 inNumToFloor)
|
||
|
{
|
||
|
UInt32 retVal = 0x10000000;
|
||
|
while (retVal > 0)
|
||
|
{
|
||
|
if (retVal & inNumToFloor)
|
||
|
return retVal;
|
||
|
else
|
||
|
retVal >>= 1;
|
||
|
}
|
||
|
return retVal;
|
||
|
}
|
||
|
|
||
|
|
||
|
RTPFile::RTPFile()
|
||
|
: fTrackInfo(NULL),
|
||
|
fBlockMap(NULL),
|
||
|
fBytesPerSecond(0),
|
||
|
fMaxTrackNumber(0)
|
||
|
{}
|
||
|
|
||
|
RTPFile::~RTPFile()
|
||
|
{
|
||
|
delete [] fFilePath.Ptr;
|
||
|
delete [] fSDPData.Ptr;
|
||
|
delete [] fTrackInfo;
|
||
|
delete [] fBlockMap;
|
||
|
}
|
||
|
|
||
|
RTPFileSession::ErrorCode RTPFile::Initialize(const StrPtrLen& inFilePath)
|
||
|
{
|
||
|
//OSFileSource theFile(inFilePath.Ptr);
|
||
|
QTSS_Object theFile = NULL;
|
||
|
QTSS_Error theErr = QTSS_OpenFileObject(inFilePath.Ptr, 0, &theFile);
|
||
|
if (theErr != QTSS_NoErr)
|
||
|
return RTPFileSession::errFileNotFound;
|
||
|
|
||
|
// Copy the path.
|
||
|
fFilePath.Ptr = NEW char[inFilePath.Len + 1];
|
||
|
::memcpy(fFilePath.Ptr, inFilePath.Ptr, inFilePath.Len);
|
||
|
fFilePath.Len = inFilePath.Len;
|
||
|
|
||
|
// Setup our osref
|
||
|
fRef.Set(fFilePath, this);
|
||
|
|
||
|
// Read the header
|
||
|
//OS_Error theErr = theFile.Read(&fHeader, sizeof(fHeader));
|
||
|
UInt32 theLengthRead = 0;
|
||
|
theErr = QTSS_Read(theFile, &fHeader, sizeof(fHeader), &theLengthRead);
|
||
|
Assert(theErr == QTSS_NoErr);
|
||
|
Assert(theLengthRead == sizeof(fHeader));
|
||
|
|
||
|
// Read the SDP data
|
||
|
fSDPData.Len = fHeader.fSDPLen;
|
||
|
fSDPData.Ptr = NEW char[fSDPData.Len + 1];
|
||
|
//theErr = theFile.Read(fSDPData.Ptr, fSDPData.Len);
|
||
|
theErr = QTSS_Read(theFile, fSDPData.Ptr, fSDPData.Len, &theLengthRead);
|
||
|
Assert(theErr == QTSS_NoErr);
|
||
|
Assert(theLengthRead == fSDPData.Len);
|
||
|
|
||
|
// Parse the SDP Information
|
||
|
fSourceInfo.Parse(fSDPData.Ptr, fSDPData.Len);
|
||
|
|
||
|
// Read the track info
|
||
|
fTrackInfo = NEW RTPFileTrackInfo[fHeader.fNumTracks];
|
||
|
//theErr = theFile.Read(fTrackInfo, sizeof(RTPFileTrackInfo) * fHeader.fNumTracks);
|
||
|
theErr = QTSS_Read(theFile, fTrackInfo, sizeof(RTPFileTrackInfo) * fHeader.fNumTracks, &theLengthRead);
|
||
|
Assert(theErr == QTSS_NoErr);
|
||
|
Assert(theLengthRead == sizeof(RTPFileTrackInfo) * fHeader.fNumTracks);
|
||
|
|
||
|
// Create and read the block map
|
||
|
fBlockMap = NEW UInt8[fHeader.fBlockMapSize];
|
||
|
//theErr = theFile.Read(fBlockMap, fHeader.fBlockMapSize);
|
||
|
theErr = QTSS_Read(theFile, fBlockMap, fHeader.fBlockMapSize, &theLengthRead);
|
||
|
Assert(theErr == QTSS_NoErr);
|
||
|
Assert(theLengthRead == fHeader.fBlockMapSize);
|
||
|
|
||
|
// Calculate bit rate of all the tracks combined
|
||
|
Float64 totalBytes = 0;
|
||
|
for (UInt32 x = 0; x < fHeader.fNumTracks; x++)
|
||
|
totalBytes += (Float64)fTrackInfo[x].fBytesInTrack;
|
||
|
totalBytes /= fHeader.fMovieDuration;
|
||
|
fBytesPerSecond = (UInt32)totalBytes;
|
||
|
|
||
|
//Get the max track number
|
||
|
fMaxTrackNumber = 0;
|
||
|
for (UInt32 y = 0; y < fHeader.fNumTracks; y++)
|
||
|
if (fTrackInfo[y].fID > fMaxTrackNumber)
|
||
|
fMaxTrackNumber = fTrackInfo[y].fID;
|
||
|
|
||
|
(void)QTSS_CloseFileObject(theFile);
|
||
|
return RTPFileSession::errNoError;
|
||
|
}
|
||
|
|
||
|
SInt64 RTPFile::GetBlockLocation(Float64 inTimeInSecs)
|
||
|
{
|
||
|
if (inTimeInSecs == 0)
|
||
|
return fHeader.fDataStartPos;
|
||
|
|
||
|
UInt32 theTime = 0;
|
||
|
UInt32 x = 0;
|
||
|
for ( ; x < fHeader.fBlockMapSize ; x++)
|
||
|
{
|
||
|
theTime += fBlockMap[x];
|
||
|
if (theTime >= (UInt32)inTimeInSecs)
|
||
|
{
|
||
|
if ((theTime > (UInt32)inTimeInSecs) && (x > 0))
|
||
|
// If we've moved too far, go back to the previous block
|
||
|
x--;
|
||
|
return fHeader.fDataStartPos + (kBlockSize * x);
|
||
|
}
|
||
|
}
|
||
|
return fHeader.fDataStartPos + (kBlockSize * x);// The requested time must be in the last block (or nonexistent).
|
||
|
}
|
||
|
|
||
|
|
||
|
Float64 RTPFile::GetTrackDuration(UInt32 inTrackID)
|
||
|
{
|
||
|
for (UInt32 x = 0; x < fHeader.fNumTracks; x++)
|
||
|
{
|
||
|
if (fTrackInfo[x].fID == inTrackID)
|
||
|
return fTrackInfo[x].fDuration;
|
||
|
}
|
||
|
Assert(0);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
UInt32 RTPFile::GetTrackTimeScale(UInt32 inTrackID)
|
||
|
{
|
||
|
for (UInt32 x = 0; x < fHeader.fNumTracks; x++)
|
||
|
{
|
||
|
if (fTrackInfo[x].fID == inTrackID)
|
||
|
return fTrackInfo[x].fTimescale;
|
||
|
}
|
||
|
Assert(0);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
UInt64 RTPFile::GetTrackBytes(UInt32 inTrackID)
|
||
|
{
|
||
|
for (UInt32 x = 0; x < fHeader.fNumTracks; x++)
|
||
|
{
|
||
|
if (fTrackInfo[x].fID == inTrackID)
|
||
|
return fTrackInfo[x].fBytesInTrack;
|
||
|
}
|
||
|
Assert(0);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
Bool16 RTPFile::TrackExists(UInt32 inTrackID)
|
||
|
{
|
||
|
for (UInt32 x = 0; x < fHeader.fNumTracks; x++)
|
||
|
{
|
||
|
if (fTrackInfo[x].fID == inTrackID)
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|