00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "QuickTimeFileSink.hh"
00022 #include "QuickTimeGenericRTPSource.hh"
00023 #include "GroupsockHelper.hh"
00024 #include "OutputFile.hh"
00025 #include "H263plusVideoRTPSource.hh"
00026 #include "MPEG4GenericRTPSource.hh"
00027 #include "MPEG4LATMAudioRTPSource.hh"
00028
00029 #include <ctype.h>
00030
00031 #define fourChar(x,y,z,w) ( ((x)<<24)|((y)<<16)|((z)<<8)|(w) )
00032
00034
00035
00036 class ChunkDescriptor {
00037 public:
00038 ChunkDescriptor(unsigned offsetInFile, unsigned size,
00039 unsigned frameSize, unsigned frameDuration,
00040 struct timeval presentationTime);
00041 virtual ~ChunkDescriptor();
00042
00043 ChunkDescriptor* extendChunk(unsigned newOffsetInFile, unsigned newSize,
00044 unsigned newFrameSize,
00045 unsigned newFrameDuration,
00046 struct timeval newPresentationTime);
00047
00048 public:
00049 ChunkDescriptor* fNextChunk;
00050 unsigned fOffsetInFile;
00051 unsigned fNumFrames;
00052 unsigned fFrameSize;
00053 unsigned fFrameDuration;
00054 struct timeval fPresentationTime;
00055 };
00056
00057 class SubsessionBuffer {
00058 public:
00059 SubsessionBuffer(unsigned bufferSize)
00060 : fBufferSize(bufferSize) {
00061 reset();
00062 fData = new unsigned char[bufferSize];
00063 }
00064 virtual ~SubsessionBuffer() { delete fData; }
00065 void reset() { fBytesInUse = 0; }
00066 void addBytes(unsigned numBytes) { fBytesInUse += numBytes; }
00067
00068 unsigned char* dataStart() { return &fData[0]; }
00069 unsigned char* dataEnd() { return &fData[fBytesInUse]; }
00070 unsigned bytesInUse() const { return fBytesInUse; }
00071 unsigned bytesAvailable() const { return fBufferSize - fBytesInUse; }
00072
00073 void setPresentationTime(struct timeval const& presentationTime) {
00074 fPresentationTime = presentationTime;
00075 }
00076 struct timeval const& presentationTime() const {return fPresentationTime;}
00077
00078 private:
00079 unsigned fBufferSize;
00080 struct timeval fPresentationTime;
00081 unsigned char* fData;
00082 unsigned fBytesInUse;
00083 };
00084
00085
00086
00087 class Count64 {
00088 public:
00089 Count64() { hi = lo = 0; }
00090
00091 void operator+=(unsigned arg);
00092
00093 unsigned hi, lo;
00094 };
00095
00096 class SubsessionIOState {
00097 public:
00098 SubsessionIOState(QuickTimeFileSink& sink, MediaSubsession& subsession);
00099 virtual ~SubsessionIOState();
00100
00101 Boolean setQTstate();
00102 void setFinalQTstate();
00103
00104 void afterGettingFrame(unsigned packetDataSize,
00105 struct timeval presentationTime);
00106 void onSourceClosure();
00107
00108 Boolean syncOK(struct timeval presentationTime);
00109
00110
00111 static void setHintTrack(SubsessionIOState* hintedTrack,
00112 SubsessionIOState* hintTrack);
00113 Boolean isHintTrack() const { return fTrackHintedByUs != NULL; }
00114 Boolean hasHintTrack() const { return fHintTrackForUs != NULL; }
00115
00116 UsageEnvironment& envir() const { return fOurSink.envir(); }
00117
00118 public:
00119 static unsigned fCurrentTrackNumber;
00120 unsigned fTrackID;
00121 SubsessionIOState* fHintTrackForUs; SubsessionIOState* fTrackHintedByUs;
00122
00123 SubsessionBuffer *fBuffer, *fPrevBuffer;
00124 QuickTimeFileSink& fOurSink;
00125 MediaSubsession& fOurSubsession;
00126
00127 unsigned short fLastPacketRTPSeqNum;
00128 Boolean fOurSourceIsActive;
00129
00130 Boolean fHaveBeenSynced;
00131 struct timeval fSyncTime;
00132
00133 Boolean fQTEnableTrack;
00134 unsigned fQTcomponentSubtype;
00135 char const* fQTcomponentName;
00136 typedef unsigned (QuickTimeFileSink::*atomCreationFunc)();
00137 atomCreationFunc fQTMediaInformationAtomCreator;
00138 atomCreationFunc fQTMediaDataAtomCreator;
00139 char const* fQTAudioDataType;
00140 unsigned short fQTSoundSampleVersion;
00141 unsigned fQTTimeScale;
00142 unsigned fQTTimeUnitsPerSample;
00143 unsigned fQTBytesPerFrame;
00144 unsigned fQTSamplesPerFrame;
00145
00146
00147 unsigned fQTTotNumSamples;
00148 unsigned fQTDurationM;
00149 unsigned fQTDurationT;
00150 unsigned fTKHD_durationPosn;
00151
00152 unsigned fQTInitialOffsetDuration;
00153
00154
00155 ChunkDescriptor *fHeadChunk, *fTailChunk;
00156 unsigned fNumChunks;
00157
00158
00159 struct hinf {
00160 Count64 trpy;
00161 Count64 nump;
00162 Count64 tpyl;
00163
00164 Count64 dmed;
00165 Count64 dimm;
00166
00167
00168 unsigned pmax;
00169 unsigned dmax;
00170 } fHINF;
00171
00172 private:
00173 void useFrame(SubsessionBuffer& buffer);
00174 void useFrameForHinting(unsigned frameSize,
00175 struct timeval presentationTime,
00176 unsigned startSampleNumber);
00177
00178
00179 unsigned useFrame1(unsigned sourceDataSize,
00180 struct timeval presentationTime,
00181 unsigned frameDuration, unsigned destFileOffset);
00182
00183
00184 private:
00185
00186 struct {
00187 unsigned frameSize;
00188 struct timeval presentationTime;
00189 unsigned destFileOffset;
00190
00191
00192 unsigned startSampleNumber;
00193 unsigned short seqNum;
00194 unsigned rtpHeader;
00195 unsigned char numSpecialHeaders;
00196 unsigned specialHeaderBytesLength;
00197 unsigned char specialHeaderBytes[SPECIAL_HEADER_BUFFER_SIZE];
00198 unsigned packetSizes[256];
00199 } fPrevFrameState;
00200 };
00201
00202
00204
00205 QuickTimeFileSink::QuickTimeFileSink(UsageEnvironment& env,
00206 MediaSession& inputSession,
00207 FILE* outFid,
00208 unsigned bufferSize,
00209 unsigned short movieWidth,
00210 unsigned short movieHeight,
00211 unsigned movieFPS,
00212 Boolean packetLossCompensate,
00213 Boolean syncStreams,
00214 Boolean generateHintTracks,
00215 Boolean generateMP4Format)
00216 : Medium(env), fInputSession(inputSession), fOutFid(outFid),
00217 fBufferSize(bufferSize), fPacketLossCompensate(packetLossCompensate),
00218 fSyncStreams(syncStreams), fGenerateMP4Format(generateMP4Format),
00219 fAreCurrentlyBeingPlayed(False),
00220 fLargestRTPtimestampFrequency(0),
00221 fNumSubsessions(0), fNumSyncedSubsessions(0),
00222 fHaveCompletedOutputFile(False),
00223 fMovieWidth(movieWidth), fMovieHeight(movieHeight),
00224 fMovieFPS(movieFPS), fMaxTrackDurationM(0) {
00225 fNewestSyncTime.tv_sec = fNewestSyncTime.tv_usec = 0;
00226 fFirstDataTime.tv_sec = fFirstDataTime.tv_usec = (unsigned)(~0);
00227
00228
00229 MediaSubsessionIterator iter(fInputSession);
00230 MediaSubsession* subsession;
00231 while ((subsession = iter.next()) != NULL) {
00232
00233 FramedSource* subsessionSource = subsession->readSource();
00234 if (subsessionSource == NULL) continue;
00235
00236
00237
00238
00239 if (subsession->videoWidth() != 0) {
00240 fMovieWidth = subsession->videoWidth();
00241 }
00242 if (subsession->videoHeight() != 0) {
00243 fMovieHeight = subsession->videoHeight();
00244 }
00245 if (subsession->videoFPS() != 0) {
00246 fMovieFPS = subsession->videoFPS();
00247 }
00248
00249 SubsessionIOState* ioState
00250 = new SubsessionIOState(*this, *subsession);
00251 if (ioState == NULL || !ioState->setQTstate()) {
00252
00253 delete ioState; ioState = NULL;
00254 continue;
00255 }
00256 subsession->miscPtr = (void*)ioState;
00257
00258 if (generateHintTracks) {
00259
00260 SubsessionIOState* hintTrack
00261 = new SubsessionIOState(*this, *subsession);
00262 SubsessionIOState::setHintTrack(ioState, hintTrack);
00263 if (!hintTrack->setQTstate()) {
00264 delete hintTrack;
00265 SubsessionIOState::setHintTrack(ioState, NULL);
00266 }
00267 }
00268
00269
00270 if (subsession->rtcpInstance() != NULL) {
00271 subsession->rtcpInstance()->setByeHandler(onRTCPBye, ioState);
00272 }
00273
00274 unsigned rtpTimestampFrequency = subsession->rtpTimestampFrequency();
00275 if (rtpTimestampFrequency > fLargestRTPtimestampFrequency) {
00276 fLargestRTPtimestampFrequency = rtpTimestampFrequency;
00277 }
00278
00279 ++fNumSubsessions;
00280 }
00281
00282
00283
00284
00285 gettimeofday(&fStartTime, NULL);
00286 fAppleCreationTime = fStartTime.tv_sec - 0x83dac000;
00287
00288
00289
00290
00291 fMDATposition = ftell(fOutFid);
00292 addAtomHeader("mdat");
00293 }
00294
00295 QuickTimeFileSink::~QuickTimeFileSink() {
00296 completeOutputFile();
00297
00298
00299 MediaSubsessionIterator iter(fInputSession);
00300 MediaSubsession* subsession;
00301 while ((subsession = iter.next()) != NULL) {
00302 SubsessionIOState* ioState
00303 = (SubsessionIOState*)(subsession->miscPtr);
00304 if (ioState == NULL) continue;
00305
00306 delete ioState->fHintTrackForUs;
00307 delete ioState;
00308 }
00309 }
00310
00311 QuickTimeFileSink*
00312 QuickTimeFileSink::createNew(UsageEnvironment& env,
00313 MediaSession& inputSession,
00314 char const* outputFileName,
00315 unsigned bufferSize,
00316 unsigned short movieWidth,
00317 unsigned short movieHeight,
00318 unsigned movieFPS,
00319 Boolean packetLossCompensate,
00320 Boolean syncStreams,
00321 Boolean generateHintTracks,
00322 Boolean generateMP4Format) {
00323 do {
00324 FILE* fid = OpenOutputFile(env, outputFileName);
00325 if (fid == NULL) break;
00326
00327 return new QuickTimeFileSink(env, inputSession, fid, bufferSize,
00328 movieWidth, movieHeight, movieFPS,
00329 packetLossCompensate, syncStreams,
00330 generateHintTracks, generateMP4Format);
00331 } while (0);
00332
00333 return NULL;
00334 }
00335
00336 Boolean QuickTimeFileSink::startPlaying(afterPlayingFunc* afterFunc,
00337 void* afterClientData) {
00338
00339 if (fAreCurrentlyBeingPlayed) {
00340 envir().setResultMsg("This sink has already been played");
00341 return False;
00342 }
00343
00344 fAreCurrentlyBeingPlayed = True;
00345 fAfterFunc = afterFunc;
00346 fAfterClientData = afterClientData;
00347
00348 return continuePlaying();
00349 }
00350
00351 Boolean QuickTimeFileSink::continuePlaying() {
00352
00353
00354 Boolean haveActiveSubsessions = False;
00355 MediaSubsessionIterator iter(fInputSession);
00356 MediaSubsession* subsession;
00357 while ((subsession = iter.next()) != NULL) {
00358 FramedSource* subsessionSource = subsession->readSource();
00359 if (subsessionSource == NULL) continue;
00360
00361 if (subsessionSource->isCurrentlyAwaitingData()) continue;
00362
00363 SubsessionIOState* ioState
00364 = (SubsessionIOState*)(subsession->miscPtr);
00365 if (ioState == NULL) continue;
00366
00367 haveActiveSubsessions = True;
00368 unsigned char* toPtr = ioState->fBuffer->dataEnd();
00369 unsigned toSize = ioState->fBuffer->bytesAvailable();
00370 subsessionSource->getNextFrame(toPtr, toSize,
00371 afterGettingFrame, ioState,
00372 onSourceClosure, ioState);
00373 }
00374 if (!haveActiveSubsessions) {
00375 envir().setResultMsg("No subsessions are currently active");
00376 return False;
00377 }
00378
00379 return True;
00380 }
00381
00382 void QuickTimeFileSink
00383 ::afterGettingFrame(void* clientData, unsigned packetDataSize,
00384 unsigned ,
00385 struct timeval presentationTime,
00386 unsigned ) {
00387 SubsessionIOState* ioState = (SubsessionIOState*)clientData;
00388 if (!ioState->syncOK(presentationTime)) {
00389
00390 ioState->fOurSink.continuePlaying();
00391 return;
00392 }
00393 ioState->afterGettingFrame(packetDataSize, presentationTime);
00394 }
00395
00396 void QuickTimeFileSink::onSourceClosure(void* clientData) {
00397 SubsessionIOState* ioState = (SubsessionIOState*)clientData;
00398 ioState->onSourceClosure();
00399 }
00400
00401 void QuickTimeFileSink::onSourceClosure1() {
00402
00403
00404 MediaSubsessionIterator iter(fInputSession);
00405 MediaSubsession* subsession;
00406 while ((subsession = iter.next()) != NULL) {
00407 SubsessionIOState* ioState
00408 = (SubsessionIOState*)(subsession->miscPtr);
00409 if (ioState == NULL) continue;
00410
00411 if (ioState->fOurSourceIsActive) return;
00412 }
00413
00414 completeOutputFile();
00415
00416
00417 if (fAfterFunc != NULL) {
00418 (*fAfterFunc)(fAfterClientData);
00419 }
00420 }
00421
00422 void QuickTimeFileSink::onRTCPBye(void* clientData) {
00423 SubsessionIOState* ioState = (SubsessionIOState*)clientData;
00424
00425 struct timeval timeNow;
00426 gettimeofday(&timeNow, NULL);
00427 unsigned secsDiff
00428 = timeNow.tv_sec - ioState->fOurSink.fStartTime.tv_sec;
00429
00430 MediaSubsession& subsession = ioState->fOurSubsession;
00431 ioState->envir() << "Received RTCP \"BYE\" on \""
00432 << subsession.mediumName()
00433 << "/" << subsession.codecName()
00434 << "\" subsession (after "
00435 << secsDiff << " seconds)\n";
00436
00437
00438 ioState->onSourceClosure();
00439 }
00440
00441 static Boolean timevalGE(struct timeval const& tv1,
00442 struct timeval const& tv2) {
00443 return (unsigned)tv1.tv_sec > (unsigned)tv2.tv_sec
00444 || (tv1.tv_sec == tv2.tv_sec
00445 && (unsigned)tv1.tv_usec >= (unsigned)tv2.tv_usec);
00446 }
00447
00448 void QuickTimeFileSink::completeOutputFile() {
00449 if (fHaveCompletedOutputFile || fOutFid == NULL) return;
00450
00451
00452
00453 unsigned curFileSize = ftell(fOutFid);
00454 setWord(fMDATposition, curFileSize);
00455
00456
00457 MediaSubsessionIterator iter(fInputSession);
00458 MediaSubsession* subsession;
00459 while ((subsession = iter.next()) != NULL) {
00460 SubsessionIOState* ioState
00461 = (SubsessionIOState*)(subsession->miscPtr);
00462 if (ioState == NULL) continue;
00463
00464 ChunkDescriptor* const headChunk = ioState->fHeadChunk;
00465 if (headChunk != NULL
00466 && timevalGE(fFirstDataTime, headChunk->fPresentationTime)) {
00467 fFirstDataTime = headChunk->fPresentationTime;
00468 }
00469 }
00470
00471
00472 iter.reset();
00473 while ((subsession = iter.next()) != NULL) {
00474 SubsessionIOState* ioState
00475 = (SubsessionIOState*)(subsession->miscPtr);
00476 if (ioState == NULL) continue;
00477
00478 ioState->setFinalQTstate();
00479
00480 if (ioState->hasHintTrack()) {
00481 ioState->fHintTrackForUs->setFinalQTstate();
00482 }
00483 }
00484
00485 if (fGenerateMP4Format) {
00486
00487 addAtom_ftyp();
00488 }
00489
00490
00491 addAtom_moov();
00492
00493
00494 fHaveCompletedOutputFile = True;
00495 }
00496
00497
00499
00500 unsigned SubsessionIOState::fCurrentTrackNumber = 0;
00501
00502 SubsessionIOState::SubsessionIOState(QuickTimeFileSink& sink,
00503 MediaSubsession& subsession)
00504 : fHintTrackForUs(NULL), fTrackHintedByUs(NULL),
00505 fOurSink(sink), fOurSubsession(subsession),
00506 fLastPacketRTPSeqNum(0), fHaveBeenSynced(False), fQTTotNumSamples(0),
00507 fHeadChunk(NULL), fTailChunk(NULL), fNumChunks(0) {
00508 fTrackID = ++fCurrentTrackNumber;
00509
00510 fBuffer = new SubsessionBuffer(fOurSink.fBufferSize);
00511 fPrevBuffer = sink.fPacketLossCompensate
00512 ? new SubsessionBuffer(fOurSink.fBufferSize) : NULL;
00513
00514 FramedSource* subsessionSource = subsession.readSource();
00515 fOurSourceIsActive = subsessionSource != NULL;
00516
00517 fPrevFrameState.presentationTime.tv_sec = 0;
00518 fPrevFrameState.presentationTime.tv_usec = 0;
00519 fPrevFrameState.seqNum = 0;
00520 }
00521
00522 SubsessionIOState::~SubsessionIOState() {
00523 delete fBuffer; delete fPrevBuffer;
00524 delete fHeadChunk;
00525 }
00526
00527 Boolean SubsessionIOState::setQTstate() {
00528 char const* noCodecWarning1 = "Warning: We don't implement a QuickTime ";
00529 char const* noCodecWarning2 = " Media Data Type for the \"";
00530 char const* noCodecWarning3 = "\" track, so we'll insert a dummy \"????\" Media Data Atom instead. A separate, codec-specific editing pass will be needed before this track can be played.\n";
00531 Boolean supportPartiallyOnly = False;
00532
00533 do {
00534 fQTEnableTrack = True;
00535 fQTTimeScale = fOurSubsession.rtpTimestampFrequency();
00536 fQTTimeUnitsPerSample = 1;
00537 fQTBytesPerFrame = 0;
00538
00539 fQTSamplesPerFrame = 1;
00540
00541
00542
00543 if (isHintTrack()) {
00544
00545 fQTEnableTrack = False;
00546 fQTcomponentSubtype = fourChar('h','i','n','t');
00547 fQTcomponentName = "hint media handler";
00548 fQTMediaInformationAtomCreator = &QuickTimeFileSink::addAtom_gmhd;
00549 fQTMediaDataAtomCreator = &QuickTimeFileSink::addAtom_rtp;
00550 } else if (strcmp(fOurSubsession.mediumName(), "audio") == 0) {
00551 fQTcomponentSubtype = fourChar('s','o','u','n');
00552 fQTcomponentName = "Apple Sound Media Handler";
00553 fQTMediaInformationAtomCreator = &QuickTimeFileSink::addAtom_smhd;
00554 fQTMediaDataAtomCreator
00555 = &QuickTimeFileSink::addAtom_soundMediaGeneral;
00556 fQTSoundSampleVersion = 0;
00557
00558
00559 if (strcmp(fOurSubsession.codecName(), "X-QT") == 0 ||
00560 strcmp(fOurSubsession.codecName(), "X-QUICKTIME") == 0) {
00561 fQTMediaDataAtomCreator = &QuickTimeFileSink::addAtom_genericMedia;
00562 } else if (strcmp(fOurSubsession.codecName(), "PCMU") == 0) {
00563 fQTAudioDataType = "ulaw";
00564 fQTBytesPerFrame = 1;
00565 } else if (strcmp(fOurSubsession.codecName(), "GSM") == 0) {
00566 fQTAudioDataType = "agsm";
00567 fQTBytesPerFrame = 33;
00568 fQTSamplesPerFrame = 160;
00569 } else if (strcmp(fOurSubsession.codecName(), "PCMA") == 0) {
00570 fQTAudioDataType = "alaw";
00571 fQTBytesPerFrame = 1;
00572 } else if (strcmp(fOurSubsession.codecName(), "QCELP") == 0) {
00573 fQTMediaDataAtomCreator = &QuickTimeFileSink::addAtom_Qclp;
00574 fQTSamplesPerFrame = 160;
00575 } else if (strcmp(fOurSubsession.codecName(), "MPEG4-GENERIC") == 0 ||
00576 strcmp(fOurSubsession.codecName(), "MP4A-LATM") == 0) {
00577 fQTMediaDataAtomCreator = &QuickTimeFileSink::addAtom_mp4a;
00578 fQTTimeUnitsPerSample = 1024;
00579
00580
00581 unsigned frequencyFromConfig
00582 = samplingFrequencyFromAudioSpecificConfig(fOurSubsession.fmtp_config());
00583 if (frequencyFromConfig != 0) fQTTimeScale = frequencyFromConfig;
00584 } else {
00585 envir() << noCodecWarning1 << "Audio" << noCodecWarning2
00586 << fOurSubsession.codecName() << noCodecWarning3;
00587 fQTMediaDataAtomCreator = &QuickTimeFileSink::addAtom_dummy;
00588 fQTEnableTrack = False;
00589 }
00590 } else if (strcmp(fOurSubsession.mediumName(), "video") == 0) {
00591 fQTcomponentSubtype = fourChar('v','i','d','e');
00592 fQTcomponentName = "Apple Video Media Handler";
00593 fQTMediaInformationAtomCreator = &QuickTimeFileSink::addAtom_vmhd;
00594
00595
00596 if (strcmp(fOurSubsession.codecName(), "X-QT") == 0 ||
00597 strcmp(fOurSubsession.codecName(), "X-QUICKTIME") == 0) {
00598 fQTMediaDataAtomCreator = &QuickTimeFileSink::addAtom_genericMedia;
00599 } else if (strcmp(fOurSubsession.codecName(), "H263-1998") == 0 ||
00600 strcmp(fOurSubsession.codecName(), "H263-2000") == 0) {
00601 fQTMediaDataAtomCreator = &QuickTimeFileSink::addAtom_h263;
00602 fQTTimeScale = 600;
00603 fQTTimeUnitsPerSample = fQTTimeScale/fOurSink.fMovieFPS;
00604 } else if (strcmp(fOurSubsession.codecName(), "MP4V-ES") == 0) {
00605 fQTMediaDataAtomCreator = &QuickTimeFileSink::addAtom_mp4v;
00606 fQTTimeScale = 600;
00607 fQTTimeUnitsPerSample = fQTTimeScale/fOurSink.fMovieFPS;
00608 } else {
00609 envir() << noCodecWarning1 << "Video" << noCodecWarning2
00610 << fOurSubsession.codecName() << noCodecWarning3;
00611 fQTMediaDataAtomCreator = &QuickTimeFileSink::addAtom_dummy;
00612 fQTEnableTrack = False;
00613 }
00614 } else {
00615 envir() << "Warning: We don't implement a QuickTime Media Handler for media type \""
00616 << fOurSubsession.mediumName() << "\"";
00617 break;
00618 }
00619
00620 if (supportPartiallyOnly) {
00621 envir() << "Warning: We don't have sufficient codec-specific information (e.g., sample sizes) to fully generate the \""
00622 << fOurSubsession.mediumName() << "/"
00623 << fOurSubsession.codecName()
00624 << "\" track, so we'll disable this track in the movie. A separate, codec-specific editing pass will be needed before this track can be played\n";
00625 fQTEnableTrack = False;
00626 }
00627
00628 return True;
00629 } while (0);
00630
00631 envir() << ", so a track for the \"" << fOurSubsession.mediumName()
00632 << "/" << fOurSubsession.codecName()
00633 << "\" subsession will not be included in the output QuickTime file\n";
00634 return False;
00635 }
00636
00637 void SubsessionIOState::setFinalQTstate() {
00638
00639 fQTDurationT = 0;
00640
00641 ChunkDescriptor* chunk = fHeadChunk;
00642 while (chunk != NULL) {
00643 unsigned const numFrames = chunk->fNumFrames;
00644 unsigned const dur = numFrames*chunk->fFrameDuration;
00645 fQTDurationT += dur;
00646
00647 chunk = chunk->fNextChunk;
00648 }
00649
00650
00651 double scaleFactor = fOurSink.movieTimeScale()/(double)fQTTimeScale;
00652 fQTDurationM = (unsigned)(fQTDurationT*scaleFactor);
00653
00654 if (fQTDurationM > fOurSink.fMaxTrackDurationM) {
00655 fOurSink.fMaxTrackDurationM = fQTDurationM;
00656 }
00657 }
00658
00659 void SubsessionIOState::afterGettingFrame(unsigned packetDataSize,
00660 struct timeval presentationTime) {
00661
00662
00663 unsigned short rtpSeqNum
00664 = fOurSubsession.rtpSource()->curPacketRTPSeqNum();
00665 if (fOurSink.fPacketLossCompensate && fPrevBuffer->bytesInUse() > 0) {
00666 short seqNumGap = rtpSeqNum - fLastPacketRTPSeqNum;
00667 for (short i = 1; i < seqNumGap; ++i) {
00668
00669 useFrame(*fPrevBuffer);
00670 }
00671 }
00672 fLastPacketRTPSeqNum = rtpSeqNum;
00673
00674
00675 if (fBuffer->bytesInUse() == 0) {
00676 fBuffer->setPresentationTime(presentationTime);
00677 }
00678 fBuffer->addBytes(packetDataSize);
00679
00680
00681
00682 if (fQTMediaDataAtomCreator == &QuickTimeFileSink::addAtom_genericMedia){
00683 QuickTimeGenericRTPSource* rtpSource
00684 = (QuickTimeGenericRTPSource*)fOurSubsession.rtpSource();
00685 QuickTimeGenericRTPSource::QTState& qtState = rtpSource->qtState;
00686 fQTTimeScale = qtState.timescale;
00687 if (qtState.width != 0) {
00688 fOurSink.fMovieWidth = qtState.width;
00689 }
00690 if (qtState.height != 0) {
00691 fOurSink.fMovieHeight = qtState.height;
00692 }
00693
00694
00695
00696 if (qtState.sdAtomSize >= 8) {
00697 char const* atom = qtState.sdAtom;
00698 unsigned mediaType = fourChar(atom[4],atom[5],atom[6],atom[7]);
00699 switch (mediaType) {
00700 case fourChar('a','g','s','m'): {
00701 fQTBytesPerFrame = 33;
00702 fQTSamplesPerFrame = 160;
00703 break;
00704 }
00705 case fourChar('Q','c','l','p'): {
00706 fQTBytesPerFrame = 35;
00707 fQTSamplesPerFrame = 160;
00708 break;
00709 }
00710 case fourChar('H','c','l','p'): {
00711 fQTBytesPerFrame = 17;
00712 fQTSamplesPerFrame = 160;
00713 break;
00714 }
00715 case fourChar('h','2','6','3'): {
00716 fQTTimeUnitsPerSample = fQTTimeScale/fOurSink.fMovieFPS;
00717 break;
00718 }
00719 }
00720 }
00721 } else if (fQTMediaDataAtomCreator == &QuickTimeFileSink::addAtom_Qclp) {
00722
00723
00724
00725
00726 fQTBytesPerFrame = packetDataSize;
00727 }
00728
00729 useFrame(*fBuffer);
00730 if (fOurSink.fPacketLossCompensate) {
00731
00732 SubsessionBuffer* tmp = fPrevBuffer;
00733 fPrevBuffer = fBuffer;
00734 fBuffer = tmp;
00735 }
00736 fBuffer->reset();
00737
00738
00739 fOurSink.continuePlaying();
00740 }
00741
00742 void SubsessionIOState::useFrame(SubsessionBuffer& buffer) {
00743 unsigned char* const frameSource = buffer.dataStart();
00744 unsigned const frameSize = buffer.bytesInUse();
00745 struct timeval const& presentationTime = buffer.presentationTime();
00746 unsigned const destFileOffset = ftell(fOurSink.fOutFid);
00747 unsigned sampleNumberOfFrameStart = fQTTotNumSamples + 1;
00748
00749
00750
00751 if (!fOurSink.fSyncStreams
00752 || fQTcomponentSubtype != fourChar('v','i','d','e')) {
00753 unsigned const frameDuration = fQTTimeUnitsPerSample*fQTSamplesPerFrame;
00754
00755 fQTTotNumSamples += useFrame1(frameSize, presentationTime,
00756 frameDuration, destFileOffset);
00757 } else {
00758
00759
00760
00761 struct timeval const& ppt = fPrevFrameState.presentationTime;
00762 if (ppt.tv_sec != 0 || ppt.tv_usec != 0) {
00763
00764 double duration = (presentationTime.tv_sec - ppt.tv_sec)
00765 + (presentationTime.tv_usec - ppt.tv_usec)/1000000.0;
00766 if (duration < 0.0) duration = 0.0;
00767 unsigned frameDuration
00768 = (unsigned)((2*duration*fQTTimeScale+1)/2);
00769
00770 unsigned numSamples
00771 = useFrame1(fPrevFrameState.frameSize, ppt,
00772 frameDuration, fPrevFrameState.destFileOffset);
00773 fQTTotNumSamples += numSamples;
00774 sampleNumberOfFrameStart = fQTTotNumSamples + 1;
00775 }
00776
00777
00778 fPrevFrameState.frameSize = frameSize;
00779 fPrevFrameState.presentationTime = presentationTime;
00780 fPrevFrameState.destFileOffset = destFileOffset;
00781 }
00782
00783
00784 fwrite(frameSource, 1, frameSize, fOurSink.fOutFid);
00785
00786
00787 if (hasHintTrack()) {
00788
00789
00790 if (!fHaveBeenSynced) {
00791 fHaveBeenSynced
00792 = fOurSubsession.rtpSource()->hasBeenSynchronizedUsingRTCP();
00793 }
00794 if (fHaveBeenSynced) {
00795 fHintTrackForUs->useFrameForHinting(frameSize, presentationTime,
00796 sampleNumberOfFrameStart);
00797 }
00798 }
00799 }
00800
00801 void SubsessionIOState::useFrameForHinting(unsigned frameSize,
00802 struct timeval presentationTime,
00803 unsigned startSampleNumber) {
00804
00805
00806
00807
00808 Boolean hack263 = strcmp(fOurSubsession.codecName(), "H263-1998") == 0;
00809 Boolean hackm4a_generic = strcmp(fOurSubsession.mediumName(), "audio") == 0
00810 && strcmp(fOurSubsession.codecName(), "MPEG4-GENERIC") == 0;
00811 Boolean hackm4a_latm = strcmp(fOurSubsession.mediumName(), "audio") == 0
00812 && strcmp(fOurSubsession.codecName(), "MP4A-LATM") == 0;
00813 Boolean hackm4a = hackm4a_generic || hackm4a_latm;
00814 Boolean haveSpecialHeaders = (hack263 || hackm4a_generic);
00815
00816
00817
00818
00819 RTPSource* const rs = fOurSubsession.rtpSource();
00820 struct timeval const& ppt = fPrevFrameState.presentationTime;
00821 if (ppt.tv_sec != 0 || ppt.tv_usec != 0) {
00822 double duration = (presentationTime.tv_sec - ppt.tv_sec)
00823 + (presentationTime.tv_usec - ppt.tv_usec)/1000000.0;
00824 if (duration < 0.0) duration = 0.0;
00825 unsigned msDuration = (unsigned)(duration*1000);
00826 if (msDuration > fHINF.dmax) fHINF.dmax = msDuration;
00827 unsigned hintSampleDuration
00828 = (unsigned)((2*duration*fQTTimeScale+1)/2);
00829 if (hackm4a) {
00830
00831
00832
00833 hintSampleDuration = fTrackHintedByUs->fQTTimeUnitsPerSample;
00834
00835
00836
00837
00838 if (fTrackHintedByUs->fQTTimeScale != fOurSubsession.rtpTimestampFrequency()) {
00839 unsigned const scalingFactor
00840 = fOurSubsession.rtpTimestampFrequency()/fTrackHintedByUs->fQTTimeScale ;
00841 hintSampleDuration *= scalingFactor;
00842 }
00843 }
00844
00845 unsigned const hintSampleDestFileOffset = ftell(fOurSink.fOutFid);
00846
00847 unsigned const maxPacketSize = 1450;
00848 unsigned short numPTEntries
00849 = (fPrevFrameState.frameSize + (maxPacketSize-1))/maxPacketSize;
00850 unsigned char* immediateDataPtr = NULL;
00851 unsigned immediateDataBytesRemaining = 0;
00852 if (haveSpecialHeaders) {
00853 numPTEntries = fPrevFrameState.numSpecialHeaders;
00854 immediateDataPtr = fPrevFrameState.specialHeaderBytes;
00855 immediateDataBytesRemaining
00856 = fPrevFrameState.specialHeaderBytesLength;
00857 }
00858 unsigned hintSampleSize
00859 = fOurSink.addHalfWord(numPTEntries);
00860 hintSampleSize += fOurSink.addHalfWord(0x0000);
00861
00862 unsigned offsetWithinSample = 0;
00863 for (unsigned i = 0; i < numPTEntries; ++i) {
00864
00865 unsigned short numDTEntries = 1;
00866 unsigned short seqNum = fPrevFrameState.seqNum++;
00867
00868 unsigned rtpHeader = fPrevFrameState.rtpHeader;
00869 if (i+1 < numPTEntries) {
00870
00871 rtpHeader &=~ (1<<23);
00872 }
00873 unsigned dataFrameSize = (i+1 < numPTEntries)
00874 ? maxPacketSize : fPrevFrameState.frameSize - i*maxPacketSize;
00875 unsigned sampleNumber = fPrevFrameState.startSampleNumber;
00876
00877 unsigned char immediateDataLen = 0;
00878 if (haveSpecialHeaders) {
00879 ++numDTEntries;
00880 if (immediateDataBytesRemaining > 0) {
00881 if (hack263) {
00882 immediateDataLen = *immediateDataPtr++;
00883 --immediateDataBytesRemaining;
00884 if (immediateDataLen > immediateDataBytesRemaining) {
00885
00886 immediateDataLen = immediateDataBytesRemaining;
00887 }
00888 } else {
00889 immediateDataLen = fPrevFrameState.specialHeaderBytesLength;
00890 }
00891 }
00892 dataFrameSize = fPrevFrameState.packetSizes[i] - immediateDataLen;
00893
00894 if (hack263) {
00895 Boolean PbitSet
00896 = immediateDataLen >= 1 && (immediateDataPtr[0]&0x4) != 0;
00897 if (PbitSet) {
00898 offsetWithinSample += 2;
00899 }
00900 }
00901 }
00902
00903
00904 hintSampleSize += fOurSink.addWord(0);
00905 hintSampleSize += fOurSink.addWord(rtpHeader|seqNum);
00906
00907 hintSampleSize += fOurSink.addHalfWord(0x0000);
00908 hintSampleSize += fOurSink.addHalfWord(numDTEntries);
00909 unsigned totalPacketSize = 0;
00910
00911
00912 if (haveSpecialHeaders) {
00913
00914 hintSampleSize += fOurSink.addByte(1);
00915 unsigned char len = immediateDataLen > 14 ? 14 : immediateDataLen;
00916 hintSampleSize += fOurSink.addByte(len);
00917 totalPacketSize += len; fHINF.dimm += len;
00918 unsigned char j;
00919 for (j = 0; j < len; ++j) {
00920 hintSampleSize += fOurSink.addByte(immediateDataPtr[j]);
00921 }
00922 for (j = len; j < 14; ++j) {
00923 hintSampleSize += fOurSink.addByte(0);
00924 }
00925
00926 immediateDataPtr += immediateDataLen;
00927 immediateDataBytesRemaining -= immediateDataLen;
00928 }
00929
00930 hintSampleSize += fOurSink.addByte(2);
00931 hintSampleSize += fOurSink.addByte(0);
00932 hintSampleSize += fOurSink.addHalfWord(dataFrameSize);
00933 totalPacketSize += dataFrameSize; fHINF.dmed += dataFrameSize;
00934 hintSampleSize += fOurSink.addWord(sampleNumber);
00935 hintSampleSize += fOurSink.addWord(offsetWithinSample);
00936
00937 unsigned short const bytesPerCompressionBlock
00938 = fTrackHintedByUs->fQTBytesPerFrame;
00939 unsigned short const samplesPerCompressionBlock
00940 = fTrackHintedByUs->fQTSamplesPerFrame;
00941 hintSampleSize += fOurSink.addHalfWord(bytesPerCompressionBlock);
00942 hintSampleSize += fOurSink.addHalfWord(samplesPerCompressionBlock);
00943
00944 offsetWithinSample += dataFrameSize;
00945
00946
00947 fHINF.nump += 1;
00948 fHINF.tpyl += totalPacketSize;
00949 totalPacketSize += 12;
00950 fHINF.trpy += totalPacketSize;
00951 if (totalPacketSize > fHINF.pmax) fHINF.pmax = totalPacketSize;
00952 }
00953
00954
00955 fQTTotNumSamples += useFrame1(hintSampleSize, ppt, hintSampleDuration,
00956 hintSampleDestFileOffset);
00957 }
00958
00959
00960 fPrevFrameState.frameSize = frameSize;
00961 fPrevFrameState.presentationTime = presentationTime;
00962 fPrevFrameState.startSampleNumber = startSampleNumber;
00963 fPrevFrameState.rtpHeader
00964 = rs->curPacketMarkerBit()<<23
00965 | (rs->rtpPayloadFormat()&0x7F)<<16;
00966 if (hack263) {
00967 H263plusVideoRTPSource* rs_263 = (H263plusVideoRTPSource*)rs;
00968 fPrevFrameState.numSpecialHeaders = rs_263->fNumSpecialHeaders;
00969 fPrevFrameState.specialHeaderBytesLength = rs_263->fSpecialHeaderBytesLength;
00970 unsigned i;
00971 for (i = 0; i < rs_263->fSpecialHeaderBytesLength; ++i) {
00972 fPrevFrameState.specialHeaderBytes[i] = rs_263->fSpecialHeaderBytes[i];
00973 }
00974 for (i = 0; i < rs_263->fNumSpecialHeaders; ++i) {
00975 fPrevFrameState.packetSizes[i] = rs_263->fPacketSizes[i];
00976 }
00977 } else if (hackm4a_generic) {
00978
00979 unsigned const sizeLength = fOurSubsession.fmtp_sizelength();
00980 unsigned const indexLength = fOurSubsession.fmtp_indexlength();
00981 if (sizeLength + indexLength != 16) {
00982 envir() << "Warning: unexpected 'sizeLength' " << sizeLength
00983 << " and 'indexLength' " << indexLength
00984 << "seen when creating hint track\n";
00985 }
00986 fPrevFrameState.numSpecialHeaders = 1;
00987 fPrevFrameState.specialHeaderBytesLength = 4;
00988 fPrevFrameState.specialHeaderBytes[0] = 0;
00989 fPrevFrameState.specialHeaderBytes[1] = 16;
00990 fPrevFrameState.specialHeaderBytes[2] = ((frameSize<<indexLength)&0xFF00)>>8;
00991 fPrevFrameState.specialHeaderBytes[3] = (frameSize<<indexLength);
00992 fPrevFrameState.packetSizes[0]
00993 = fPrevFrameState.specialHeaderBytesLength + frameSize;
00994 }
00995 }
00996
00997 unsigned SubsessionIOState::useFrame1(unsigned sourceDataSize,
00998 struct timeval presentationTime,
00999 unsigned frameDuration,
01000 unsigned destFileOffset) {
01001
01002 unsigned frameSize = fQTBytesPerFrame;
01003 if (frameSize == 0) {
01004
01005 frameSize = sourceDataSize;
01006 }
01007 unsigned const numFrames = sourceDataSize/frameSize;
01008 unsigned const numSamples = numFrames*fQTSamplesPerFrame;
01009
01010
01011 ChunkDescriptor* newTailChunk;
01012 if (fTailChunk == NULL) {
01013 newTailChunk = fHeadChunk
01014 = new ChunkDescriptor(destFileOffset, sourceDataSize,
01015 frameSize, frameDuration, presentationTime);
01016 } else {
01017 newTailChunk = fTailChunk->extendChunk(destFileOffset, sourceDataSize,
01018 frameSize, frameDuration,
01019 presentationTime);
01020 }
01021 if (newTailChunk != fTailChunk) {
01022
01023 ++fNumChunks;
01024 fTailChunk = newTailChunk;
01025 }
01026
01027 return numSamples;
01028 }
01029
01030 void SubsessionIOState::onSourceClosure() {
01031 fOurSourceIsActive = False;
01032 fOurSink.onSourceClosure1();
01033 }
01034
01035 Boolean SubsessionIOState::syncOK(struct timeval presentationTime) {
01036 QuickTimeFileSink& s = fOurSink;
01037 if (!s.fSyncStreams) return True;
01038
01039 if (s.fNumSyncedSubsessions < s.fNumSubsessions) {
01040
01041
01042 if (!fHaveBeenSynced) {
01043
01044 if (fOurSubsession.rtpSource()->hasBeenSynchronizedUsingRTCP()) {
01045
01046 fHaveBeenSynced = True;
01047 fSyncTime = presentationTime;
01048 ++s.fNumSyncedSubsessions;
01049
01050 if (timevalGE(fSyncTime, s.fNewestSyncTime)) {
01051 s.fNewestSyncTime = fSyncTime;
01052 }
01053 }
01054 }
01055 }
01056
01057
01058 if (s.fNumSyncedSubsessions < s.fNumSubsessions) return False;
01059
01060
01061 return timevalGE(presentationTime, s.fNewestSyncTime);
01062 }
01063
01064 void SubsessionIOState::setHintTrack(SubsessionIOState* hintedTrack,
01065 SubsessionIOState* hintTrack) {
01066 if (hintedTrack != NULL) hintedTrack->fHintTrackForUs = hintTrack;
01067 if (hintTrack != NULL) hintTrack->fTrackHintedByUs = hintedTrack;
01068 }
01069
01070 void Count64::operator+=(unsigned arg) {
01071 unsigned newLo = lo + arg;
01072 if (newLo < lo) {
01073 ++hi;
01074 }
01075 lo = newLo;
01076 }
01077
01078 ChunkDescriptor
01079 ::ChunkDescriptor(unsigned offsetInFile, unsigned size,
01080 unsigned frameSize, unsigned frameDuration,
01081 struct timeval presentationTime)
01082 : fNextChunk(NULL), fOffsetInFile(offsetInFile),
01083 fNumFrames(size/frameSize),
01084 fFrameSize(frameSize), fFrameDuration(frameDuration),
01085 fPresentationTime(presentationTime) {
01086 }
01087
01088 ChunkDescriptor::~ChunkDescriptor() {
01089 delete fNextChunk;
01090 }
01091
01092 ChunkDescriptor* ChunkDescriptor
01093 ::extendChunk(unsigned newOffsetInFile, unsigned newSize,
01094 unsigned newFrameSize, unsigned newFrameDuration,
01095 struct timeval newPresentationTime) {
01096
01097
01098 if (newOffsetInFile == fOffsetInFile + fNumFrames*fFrameSize) {
01099
01100
01101 if (newFrameSize == fFrameSize && newFrameDuration == fFrameDuration) {
01102 fNumFrames += newSize/fFrameSize;
01103 return this;
01104 }
01105 }
01106
01107
01108 ChunkDescriptor* newDescriptor
01109 = new ChunkDescriptor(newOffsetInFile, newSize,
01110 newFrameSize, newFrameDuration,
01111 newPresentationTime);
01112
01113 fNextChunk = newDescriptor;
01114
01115 return newDescriptor;
01116 }
01117
01118
01120
01121 unsigned QuickTimeFileSink::addWord(unsigned word) {
01122 addByte(word>>24); addByte(word>>16);
01123 addByte(word>>8); addByte(word);
01124
01125 return 4;
01126 }
01127
01128 unsigned QuickTimeFileSink::addHalfWord(unsigned short halfWord) {
01129 addByte((unsigned char)(halfWord>>8)); addByte((unsigned char)halfWord);
01130
01131 return 2;
01132 }
01133
01134 unsigned QuickTimeFileSink::addZeroWords(unsigned numWords) {
01135 for (unsigned i = 0; i < numWords; ++i) {
01136 addWord(0);
01137 }
01138
01139 return numWords*4;
01140 }
01141
01142 unsigned QuickTimeFileSink::add4ByteString(char const* str) {
01143 addByte(str[0]); addByte(str[1]); addByte(str[2]); addByte(str[3]);
01144
01145 return 4;
01146 }
01147
01148 unsigned QuickTimeFileSink::addArbitraryString(char const* str,
01149 Boolean oneByteLength) {
01150 unsigned size = 0;
01151 if (oneByteLength) {
01152
01153 unsigned strLength = strlen(str);
01154 if (strLength >= 256) {
01155 envir() << "QuickTimeFileSink::addArbitraryString(\""
01156 << str << "\") saw string longer than we know how to handle ("
01157 << strLength << "\n";
01158 }
01159 size += addByte((unsigned char)strLength);
01160 }
01161
01162 while (*str != '\0') {
01163 size += addByte(*str++);
01164 }
01165
01166 return size;
01167 }
01168
01169 unsigned QuickTimeFileSink::addAtomHeader(char const* atomName) {
01170
01171 addWord(0);
01172
01173
01174 add4ByteString(atomName);
01175
01176 return 8;
01177 }
01178
01179 void QuickTimeFileSink::setWord(unsigned filePosn, unsigned size) {
01180 do {
01181 if (fseek(fOutFid, filePosn, SEEK_SET) < 0) break;
01182 addWord(size);
01183 if (fseek(fOutFid, 0, SEEK_END) < 0) break;
01184
01185 return;
01186 } while (0);
01187
01188
01189 envir() << "QuickTimeFileSink::setWord(): fseek failed (err "
01190 << envir().getErrno() << ")\n";
01191 }
01192
01193
01194
01195 #define addAtom(name) \
01196 unsigned QuickTimeFileSink::addAtom_##name() { \
01197 unsigned initFilePosn = ftell(fOutFid); \
01198 unsigned size = addAtomHeader("" #name "")
01199
01200 #define addAtomEnd \
01201 setWord(initFilePosn, size); \
01202 return size; \
01203 }
01204
01205 addAtom(ftyp);
01206 size += add4ByteString("mp42");
01207 size += addWord(0x00000000);
01208 size += add4ByteString("mp42");
01209 size += add4ByteString("isom");
01210 addAtomEnd;
01211
01212 addAtom(moov);
01213 size += addAtom_mvhd();
01214
01215 if (fGenerateMP4Format) {
01216 size += addAtom_iods();
01217 }
01218
01219
01220
01221
01222
01223 MediaSubsessionIterator iter(fInputSession);
01224 MediaSubsession* subsession;
01225 while ((subsession = iter.next()) != NULL) {
01226 fCurrentIOState = (SubsessionIOState*)(subsession->miscPtr);
01227 if (fCurrentIOState == NULL) continue;
01228 if (strcmp(subsession->mediumName(), "audio") != 0) continue;
01229
01230 size += addAtom_trak();
01231
01232 if (fCurrentIOState->hasHintTrack()) {
01233
01234 fCurrentIOState = fCurrentIOState->fHintTrackForUs;
01235 size += addAtom_trak();
01236 }
01237 }
01238 iter.reset();
01239 while ((subsession = iter.next()) != NULL) {
01240 fCurrentIOState = (SubsessionIOState*)(subsession->miscPtr);
01241 if (fCurrentIOState == NULL) continue;
01242 if (strcmp(subsession->mediumName(), "audio") == 0) continue;
01243
01244 size += addAtom_trak();
01245
01246 if (fCurrentIOState->hasHintTrack()) {
01247
01248 fCurrentIOState = fCurrentIOState->fHintTrackForUs;
01249 size += addAtom_trak();
01250 }
01251 }
01252 addAtomEnd;
01253
01254 addAtom(mvhd);
01255 size += addWord(0x00000000);
01256 size += addWord(fAppleCreationTime);
01257 size += addWord(fAppleCreationTime);
01258
01259
01260
01261 size += addWord(movieTimeScale());
01262
01263 unsigned const duration = fMaxTrackDurationM;
01264 fMVHD_durationPosn = ftell(fOutFid);
01265 size += addWord(duration);
01266
01267 size += addWord(0x00010000);
01268 size += addWord(0x01000000);
01269 size += addZeroWords(2);
01270 size += addWord(0x00010000);
01271 size += addZeroWords(3);
01272 size += addWord(0x00010000);
01273 size += addZeroWords(3);
01274 size += addWord(0x40000000);
01275 size += addZeroWords(6);
01276 size += addWord(SubsessionIOState::fCurrentTrackNumber+1);
01277 addAtomEnd;
01278
01279 addAtom(iods);
01280 size += addWord(0x00000000);
01281 size += addWord(0x10808080);
01282 size += addWord(0x07004FFF);
01283 size += addWord(0xFF0FFFFF);
01284 addAtomEnd;
01285
01286 addAtom(trak);
01287 size += addAtom_tkhd();
01288
01289
01290
01291 if (fCurrentIOState->fHeadChunk != NULL
01292 && (fSyncStreams || fCurrentIOState->isHintTrack())) {
01293 size += addAtom_edts();
01294 }
01295
01296
01297 if (fCurrentIOState->isHintTrack()) size += addAtom_tref();
01298
01299 size += addAtom_mdia();
01300
01301
01302 if (fCurrentIOState->isHintTrack()) size += addAtom_udta();
01303 addAtomEnd;
01304
01305 addAtom(tkhd);
01306 if (fCurrentIOState->fQTEnableTrack) {
01307 size += addWord(0x0000000F);
01308 } else {
01309
01310 size += addWord(0x00000000);
01311 }
01312 size += addWord(fAppleCreationTime);
01313 size += addWord(fAppleCreationTime);
01314 size += addWord(fCurrentIOState->fTrackID);
01315 size += addWord(0x00000000);
01316
01317 unsigned const duration = fCurrentIOState->fQTDurationM;
01318 fCurrentIOState->fTKHD_durationPosn = ftell(fOutFid);
01319 size += addWord(duration);
01320 size += addZeroWords(3);
01321 size += addWord(0x01000000);
01322 size += addWord(0x00010000);
01323 size += addZeroWords(3);
01324 size += addWord(0x00010000);
01325 size += addZeroWords(3);
01326 size += addWord(0x40000000);
01327 if (strcmp(fCurrentIOState->fOurSubsession.mediumName(), "video") == 0) {
01328 size += addWord(fMovieWidth<<16);
01329 size += addWord(fMovieHeight<<16);
01330 } else {
01331 size += addZeroWords(2);
01332 }
01333 addAtomEnd;
01334
01335 addAtom(edts);
01336 size += addAtom_elst();
01337 addAtomEnd;
01338
01339 #define addEdit1(duration,trackPosition) do { \
01340 unsigned trackDuration \
01341 = (unsigned) ((2*(duration)*movieTimeScale()+1)/2); \
01342 \
01343 size += addWord(trackDuration); \
01344 totalDurationOfEdits += trackDuration; \
01345 size += addWord(trackPosition); \
01346 size += addWord(0x00010000); \
01347 ++numEdits; \
01348 } while (0)
01349 #define addEdit(duration) addEdit1((duration),editTrackPosition)
01350 #define addEmptyEdit(duration) addEdit1((duration),(~0))
01351
01352 addAtom(elst);
01353 size += addWord(0x00000000);
01354
01355
01356
01357 unsigned numEntriesPosition = ftell(fOutFid);
01358 size += addWord(0);
01359 unsigned numEdits = 0;
01360 unsigned totalDurationOfEdits = 0;
01361
01362
01363
01364
01365
01366 double const syncThreshold = 0.1;
01367
01368
01369 struct timeval editStartTime = fFirstDataTime;
01370 unsigned editTrackPosition = 0;
01371 unsigned currentTrackPosition = 0;
01372 double trackDurationOfEdit = 0.0;
01373 unsigned chunkDuration = 0;
01374
01375 ChunkDescriptor* chunk = fCurrentIOState->fHeadChunk;
01376 while (chunk != NULL) {
01377 struct timeval const& chunkStartTime = chunk->fPresentationTime;
01378 double movieDurationOfEdit
01379 = (chunkStartTime.tv_sec - editStartTime.tv_sec)
01380 + (chunkStartTime.tv_usec - editStartTime.tv_usec)/1000000.0;
01381 trackDurationOfEdit = (currentTrackPosition-editTrackPosition)
01382 / (double)(fCurrentIOState->fQTTimeScale);
01383
01384 double outOfSync = movieDurationOfEdit - trackDurationOfEdit;
01385
01386 if (outOfSync > syncThreshold) {
01387
01388
01389
01390 if (trackDurationOfEdit > 0.0) addEdit(trackDurationOfEdit);
01391 addEmptyEdit(outOfSync);
01392
01393 editStartTime = chunkStartTime;
01394 editTrackPosition = currentTrackPosition;
01395 } else if (outOfSync < -syncThreshold) {
01396
01397
01398 if (movieDurationOfEdit > 0.0) addEdit(movieDurationOfEdit);
01399
01400 editStartTime = chunkStartTime;
01401 editTrackPosition = currentTrackPosition;
01402 }
01403
01404
01405 unsigned numChannels = fCurrentIOState->fOurSubsession.numChannels();
01406 chunkDuration = chunk->fNumFrames*chunk->fFrameDuration/numChannels;
01407 currentTrackPosition += chunkDuration;
01408
01409 chunk = chunk->fNextChunk;
01410 }
01411
01412
01413 trackDurationOfEdit
01414 += (double)chunkDuration/fCurrentIOState->fQTTimeScale;
01415 if (trackDurationOfEdit > 0.0) addEdit(trackDurationOfEdit);
01416
01417
01418 setWord(numEntriesPosition, numEdits);
01419
01420
01421
01422
01423 if (totalDurationOfEdits > fCurrentIOState->fQTDurationM) {
01424 fCurrentIOState->fQTDurationM = totalDurationOfEdits;
01425 setWord(fCurrentIOState->fTKHD_durationPosn, totalDurationOfEdits);
01426
01427
01428 if (totalDurationOfEdits > fMaxTrackDurationM) {
01429 fMaxTrackDurationM = totalDurationOfEdits;
01430 setWord(fMVHD_durationPosn, totalDurationOfEdits);
01431 }
01432
01433
01434 double scaleFactor
01435 = fCurrentIOState->fQTTimeScale/(double)movieTimeScale();
01436 fCurrentIOState->fQTDurationT
01437 = (unsigned)(totalDurationOfEdits*scaleFactor);
01438 }
01439 addAtomEnd;
01440
01441 addAtom(tref);
01442 size += addAtom_hint();
01443 addAtomEnd;
01444
01445 addAtom(hint);
01446 SubsessionIOState* hintedTrack = fCurrentIOState->fTrackHintedByUs;
01447
01448 size += addWord(hintedTrack->fTrackID);
01449 addAtomEnd;
01450
01451 addAtom(mdia);
01452 size += addAtom_mdhd();
01453 size += addAtom_hdlr();
01454 size += addAtom_minf();
01455 addAtomEnd;
01456
01457 addAtom(mdhd);
01458 size += addWord(0x00000000);
01459 size += addWord(fAppleCreationTime);
01460 size += addWord(fAppleCreationTime);
01461
01462 unsigned const timeScale = fCurrentIOState->fQTTimeScale;
01463 size += addWord(timeScale);
01464
01465 unsigned const duration = fCurrentIOState->fQTDurationT;
01466 size += addWord(duration);
01467
01468 size += addWord(0x00000000);
01469 addAtomEnd;
01470
01471 addAtom(hdlr);
01472 size += addWord(0x00000000);
01473 size += add4ByteString("mhlr");
01474 size += addWord(fCurrentIOState->fQTcomponentSubtype);
01475
01476 size += add4ByteString("appl");
01477 size += addWord(0x00000000);
01478 size += addWord(0x00000000);
01479 size += addArbitraryString(fCurrentIOState->fQTcomponentName);
01480
01481 addAtomEnd;
01482
01483 addAtom(minf);
01484 SubsessionIOState::atomCreationFunc mediaInformationAtomCreator
01485 = fCurrentIOState->fQTMediaInformationAtomCreator;
01486 size += (this->*mediaInformationAtomCreator)();
01487 size += addAtom_hdlr2();
01488 size += addAtom_dinf();
01489 size += addAtom_stbl();
01490 addAtomEnd;
01491
01492 addAtom(smhd);
01493 size += addZeroWords(2);
01494 addAtomEnd;
01495
01496 addAtom(vmhd);
01497 size += addWord(0x00000001);
01498 size += addWord(0x00408000);
01499 size += addWord(0x80008000);
01500 addAtomEnd;
01501
01502 addAtom(gmhd);
01503 size += addAtom_gmin();
01504 addAtomEnd;
01505
01506 addAtom(gmin);
01507 size += addWord(0x00000000);
01508
01509
01510 size += addWord(0x00408000);
01511 size += addWord(0x80008000);
01512 size += addWord(0x00000000);
01513 addAtomEnd;
01514
01515 unsigned QuickTimeFileSink::addAtom_hdlr2() {
01516 unsigned initFilePosn = ftell(fOutFid);
01517 unsigned size = addAtomHeader("hdlr");
01518 size += addWord(0x00000000);
01519 size += add4ByteString("dhlr");
01520 size += add4ByteString("alis");
01521 size += add4ByteString("appl");
01522 size += addZeroWords(2);
01523 size += addArbitraryString("Apple Alias Data Handler");
01524 addAtomEnd;
01525
01526 addAtom(dinf);
01527 size += addAtom_dref();
01528 addAtomEnd;
01529
01530 addAtom(dref);
01531 size += addWord(0x00000000);
01532 size += addWord(0x00000001);
01533 size += addAtom_alis();
01534 addAtomEnd;
01535
01536 addAtom(alis);
01537 size += addWord(0x00000001);
01538 addAtomEnd;
01539
01540 addAtom(stbl);
01541 size += addAtom_stsd();
01542 size += addAtom_stts();
01543 size += addAtom_stsc();
01544 size += addAtom_stsz();
01545 size += addAtom_stco();
01546 addAtomEnd;
01547
01548 addAtom(stsd);
01549 size += addWord(0x00000000);
01550 size += addWord(0x00000001);
01551 SubsessionIOState::atomCreationFunc mediaDataAtomCreator
01552 = fCurrentIOState->fQTMediaDataAtomCreator;
01553 size += (this->*mediaDataAtomCreator)();
01554 addAtomEnd;
01555
01556 unsigned QuickTimeFileSink::addAtom_genericMedia() {
01557 unsigned initFilePosn = ftell(fOutFid);
01558
01559
01560
01561 QuickTimeGenericRTPSource* rtpSource = (QuickTimeGenericRTPSource*)
01562 fCurrentIOState->fOurSubsession.rtpSource();
01563 QuickTimeGenericRTPSource::QTState& qtState = rtpSource->qtState;
01564 char const* from = qtState.sdAtom;
01565 unsigned size = qtState.sdAtomSize;
01566 for (unsigned i = 0; i < size; ++i) addByte(from[i]);
01567 addAtomEnd;
01568
01569 unsigned QuickTimeFileSink::addAtom_soundMediaGeneral() {
01570 unsigned initFilePosn = ftell(fOutFid);
01571 unsigned size = addAtomHeader(fCurrentIOState->fQTAudioDataType);
01572
01573
01574 size += addWord(0x00000000);
01575 size += addWord(0x00000001);
01576
01577 unsigned short const version = fCurrentIOState->fQTSoundSampleVersion;
01578 size += addWord(version<<16);
01579 size += addWord(0x00000000);
01580 unsigned short numChannels
01581 = (unsigned short)(fCurrentIOState->fOurSubsession.numChannels());
01582 size += addHalfWord(numChannels);
01583 size += addHalfWord(0x0010);
01584
01585 size += addWord(0xfffe0000);
01586
01587 unsigned const sampleRateFixedPoint = fCurrentIOState->fQTTimeScale << 16;
01588 size += addWord(sampleRateFixedPoint);
01589 addAtomEnd;
01590
01591 unsigned QuickTimeFileSink::addAtom_Qclp() {
01592
01593
01594 unsigned initFilePosn = ftell(fOutFid);
01595 fCurrentIOState->fQTAudioDataType = "Qclp";
01596 fCurrentIOState->fQTSoundSampleVersion = 1;
01597 unsigned size = addAtom_soundMediaGeneral();
01598
01599
01600
01601 size += addWord(0x000000a0);
01602 size += addWord(0x00000000);
01603 size += addWord(0x00000000);
01604 size += addWord(0x00000002);
01605
01606
01607 size += addAtom_wave();
01608 addAtomEnd;
01609
01610 addAtom(wave);
01611 size += addAtom_frma();
01612 if (strcmp(fCurrentIOState->fQTAudioDataType, "Qclp") == 0) {
01613 size += addWord(0x00000014);
01614 size += add4ByteString("Qclp");
01615 if (fCurrentIOState->fQTBytesPerFrame == 35) {
01616 size += addAtom_Fclp();
01617 } else {
01618 size += addAtom_Hclp();
01619 }
01620 size += addWord(0x00000008);
01621 size += addWord(0x00000000);
01622 size += addWord(0x00000000);
01623 size += addWord(0x00000008);
01624 } else if (strcmp(fCurrentIOState->fQTAudioDataType, "mp4a") == 0) {
01625 size += addWord(0x0000000c);
01626 size += add4ByteString("mp4a");
01627 size += addWord(0x00000000);
01628 size += addAtom_esds();
01629 size += addWord(0x00000008);
01630 size += addWord(0x00000000);
01631 }
01632 addAtomEnd;
01633
01634 addAtom(frma);
01635 size += add4ByteString(fCurrentIOState->fQTAudioDataType);
01636 addAtomEnd;
01637
01638 addAtom(Fclp);
01639 size += addWord(0x00000000);
01640 addAtomEnd;
01641
01642 addAtom(Hclp);
01643 size += addWord(0x00000000);
01644 addAtomEnd;
01645
01646 unsigned QuickTimeFileSink::addAtom_mp4a() {
01647
01648
01649 unsigned initFilePosn = ftell(fOutFid);
01650 fCurrentIOState->fQTAudioDataType = "mp4a";
01651 fCurrentIOState->fQTSoundSampleVersion = 1;
01652 unsigned size = addAtom_soundMediaGeneral();
01653
01654 if (fGenerateMP4Format) {
01655 size += addAtom_esds();
01656 } else {
01657
01658
01659 size += addWord(fCurrentIOState->fQTTimeUnitsPerSample);
01660 size += addWord(0x00000001);
01661 size += addWord(0x00000001);
01662 size += addWord(0x00000002);
01663
01664
01665 size += addAtom_wave();
01666 }
01667 addAtomEnd;
01668
01669 addAtom(esds);
01670
01671 MediaSubsession& subsession = fCurrentIOState->fOurSubsession;
01672 if (strcmp(subsession.mediumName(), "audio") == 0) {
01673
01674 size += addWord(0x00000000);
01675 size += addWord(0x03808080);
01676 size += addWord(0x2a000000);
01677 size += addWord(0x04808080);
01678 size += addWord(0x1c401500);
01679 size += addWord(0x18000000);
01680 size += addWord(0x6d600000);
01681 size += addWord(0x6d600580);
01682 size += addByte(0x80); size += addByte(0x80);
01683 } else if (strcmp(subsession.mediumName(), "video") == 0) {
01684
01685 size += addWord(0x00000000);
01686 size += addWord(0x03370000);
01687 size += addWord(0x1f042f20);
01688 size += addWord(0x1104fd46);
01689 size += addWord(0x000d4e10);
01690 size += addWord(0x000d4e10);
01691 size += addByte(0x05);
01692 }
01693
01694
01695 unsigned configSize;
01696 unsigned char* config
01697 = parseGeneralConfigStr(subsession.fmtp_config(), configSize);
01698 if (configSize > 0) --configSize;
01699 size += addByte(configSize);
01700 for (unsigned i = 0; i < configSize; ++i) {
01701 size += addByte(config[i]);
01702 }
01703
01704 if (strcmp(subsession.mediumName(), "audio") == 0) {
01705
01706 size += addWord(0x06808080);
01707 size += addByte(0x01);
01708 } else {
01709
01710 size += addHalfWord(0x0601);
01711 size += addByte(0x02);
01712 }
01713
01714 addAtomEnd;
01715
01716 addAtom(srcq);
01717
01718 size += addWord(0x00000040);
01719
01720 addAtomEnd;
01721
01722 addAtom(h263);
01723
01724 size += addWord(0x00000000);
01725 size += addWord(0x00000001);
01726
01727 size += addWord(0x00020001);
01728 size += add4ByteString("appl");
01729 size += addWord(0x00000000);
01730 size += addWord(0x000002fc);
01731 unsigned const widthAndHeight = (fMovieWidth<<16)|fMovieHeight;
01732 size += addWord(widthAndHeight);
01733 size += addWord(0x00480000);
01734 size += addWord(0x00480000);
01735 size += addWord(0x00000000);
01736 size += addWord(0x00010548);
01737
01738 size += addWord(0x2e323633);
01739 size += addZeroWords(6);
01740 size += addWord(0x00000018);
01741 size += addHalfWord(0xffff);
01742 addAtomEnd;
01743
01744 addAtom(mp4v);
01745
01746 size += addWord(0x00000000);
01747 size += addWord(0x00000001);
01748
01749 size += addWord(0x00020001);
01750 size += add4ByteString("appl");
01751 size += addWord(0x00000200);
01752 size += addWord(0x00000400);
01753 unsigned const widthAndHeight = (fMovieWidth<<16)|fMovieHeight;
01754 size += addWord(widthAndHeight);
01755 size += addWord(0x00480000);
01756 size += addWord(0x00480000);
01757 size += addWord(0x00000000);
01758 size += addWord(0x00010c4d);
01759
01760 size += addWord(0x5045472d);
01761 size += addWord(0x34205669);
01762 size += addWord(0x64656f00);
01763 size += addZeroWords(4);
01764 size += addWord(0x00000018);
01765 size += addHalfWord(0xffff);
01766 size += addAtom_esds();
01767 size += addWord(0x00000000);
01768 addAtomEnd;
01769
01770 unsigned QuickTimeFileSink::addAtom_rtp() {
01771 unsigned initFilePosn = ftell(fOutFid);
01772 unsigned size = addAtomHeader("rtp ");
01773
01774 size += addWord(0x00000000);
01775 size += addWord(0x00000001);
01776 size += addWord(0x00010001);
01777 size += addWord(1450);
01778
01779 size += addAtom_tims();
01780 addAtomEnd;
01781
01782 addAtom(tims);
01783 size += addWord(fCurrentIOState->fOurSubsession.rtpTimestampFrequency());
01784 addAtomEnd;
01785
01786 addAtom(stts);
01787 size += addWord(0x00000000);
01788
01789
01790
01791 unsigned numEntriesPosition = ftell(fOutFid);
01792 size += addWord(0);
01793
01794
01795
01796 unsigned numEntries = 0, numSamplesSoFar = 0;
01797 unsigned prevSampleDuration = 0;
01798 unsigned const samplesPerFrame = fCurrentIOState->fQTSamplesPerFrame;
01799 ChunkDescriptor* chunk = fCurrentIOState->fHeadChunk;
01800 while (chunk != NULL) {
01801 unsigned const sampleDuration = chunk->fFrameDuration/samplesPerFrame;
01802 if (sampleDuration != prevSampleDuration) {
01803
01804
01805 if (chunk != fCurrentIOState->fHeadChunk) {
01806 ++numEntries;
01807 size += addWord(numSamplesSoFar);
01808 size += addWord(prevSampleDuration);
01809 numSamplesSoFar = 0;
01810 }
01811 }
01812
01813 unsigned const numSamples = chunk->fNumFrames*samplesPerFrame;
01814 numSamplesSoFar += numSamples;
01815 prevSampleDuration = sampleDuration;
01816 chunk = chunk->fNextChunk;
01817 }
01818
01819
01820 ++numEntries;
01821 size += addWord(numSamplesSoFar);
01822 size += addWord(prevSampleDuration);
01823
01824
01825 setWord(numEntriesPosition, numEntries);
01826 addAtomEnd;
01827
01828 addAtom(stsc);
01829 size += addWord(0x00000000);
01830
01831
01832
01833 unsigned numEntriesPosition = ftell(fOutFid);
01834 size += addWord(0);
01835
01836
01837
01838 unsigned numEntries = 0, chunkNumber = 0;
01839 unsigned prevSamplesPerChunk = ~0;
01840 unsigned const samplesPerFrame = fCurrentIOState->fQTSamplesPerFrame;
01841 ChunkDescriptor* chunk = fCurrentIOState->fHeadChunk;
01842 while (chunk != NULL) {
01843 ++chunkNumber;
01844 unsigned const samplesPerChunk = chunk->fNumFrames*samplesPerFrame;
01845 if (samplesPerChunk != prevSamplesPerChunk) {
01846
01847 ++numEntries;
01848 size += addWord(chunkNumber);
01849 size += addWord(samplesPerChunk);
01850 size += addWord(0x00000001);
01851
01852 prevSamplesPerChunk = samplesPerChunk;
01853 }
01854 chunk = chunk->fNextChunk;
01855 }
01856
01857
01858 setWord(numEntriesPosition, numEntries);
01859 addAtomEnd;
01860
01861 addAtom(stsz);
01862 size += addWord(0x00000000);
01863
01864
01865
01866
01867 Boolean haveSingleEntryTable = True;
01868 double firstBPS = 0.0;
01869 ChunkDescriptor* chunk = fCurrentIOState->fHeadChunk;
01870 while (chunk != NULL) {
01871 double bps
01872 = (double)(chunk->fFrameSize)/(fCurrentIOState->fQTSamplesPerFrame);
01873 if (bps < 1.0) {
01874
01875
01876 break;
01877 }
01878
01879 if (firstBPS == 0.0) {
01880 firstBPS = bps;
01881 } else if (bps != firstBPS) {
01882 haveSingleEntryTable = False;
01883 break;
01884 }
01885
01886 chunk = chunk->fNextChunk;
01887 }
01888
01889 unsigned sampleSize;
01890 if (haveSingleEntryTable) {
01891 if (fCurrentIOState->isHintTrack()
01892 && fCurrentIOState->fHeadChunk != NULL) {
01893 sampleSize = fCurrentIOState->fHeadChunk->fFrameSize
01894 / fCurrentIOState->fQTSamplesPerFrame;
01895 } else {
01896
01897 sampleSize = fCurrentIOState->fQTTimeUnitsPerSample;
01898 }
01899 } else {
01900 sampleSize = 0;
01901 }
01902 size += addWord(sampleSize);
01903 unsigned const totNumSamples = fCurrentIOState->fQTTotNumSamples;
01904 size += addWord(totNumSamples);
01905
01906 if (!haveSingleEntryTable) {
01907
01908
01909 ChunkDescriptor* chunk = fCurrentIOState->fHeadChunk;
01910 while (chunk != NULL) {
01911 unsigned numSamples
01912 = chunk->fNumFrames*(fCurrentIOState->fQTSamplesPerFrame);
01913 unsigned sampleSize
01914 = chunk->fFrameSize/(fCurrentIOState->fQTSamplesPerFrame);
01915 for (unsigned i = 0; i < numSamples; ++i) {
01916 size += addWord(sampleSize);
01917 }
01918
01919 chunk = chunk->fNextChunk;
01920 }
01921 }
01922 addAtomEnd;
01923
01924 addAtom(stco);
01925 size += addWord(0x00000000);
01926 size += addWord(fCurrentIOState->fNumChunks);
01927
01928
01929 ChunkDescriptor* chunk = fCurrentIOState->fHeadChunk;
01930 while (chunk != NULL) {
01931 size += addWord(chunk->fOffsetInFile);
01932
01933 chunk = chunk->fNextChunk;
01934 }
01935 addAtomEnd;
01936
01937 addAtom(udta);
01938 size += addAtom_name();
01939 size += addAtom_hnti();
01940 size += addAtom_hinf();
01941 addAtomEnd;
01942
01943 addAtom(name);
01944 char description[100];
01945 sprintf(description, "Hinted %s track",
01946 fCurrentIOState->fOurSubsession.mediumName());
01947 size += addArbitraryString(description, False);
01948 addAtomEnd;
01949
01950 addAtom(hnti);
01951 size += addAtom_sdp();
01952 addAtomEnd;
01953
01954 unsigned QuickTimeFileSink::addAtom_sdp() {
01955 unsigned initFilePosn = ftell(fOutFid);
01956 unsigned size = addAtomHeader("sdp ");
01957
01958
01959 char const* sdpLines = fCurrentIOState->fOurSubsession.savedSDPLines();
01960
01961
01962 char* newSDPLines = new char[strlen(sdpLines)+100];
01963 char const* searchStr = "a=control:trackid=";
01964 Boolean foundSearchString = False;
01965 char const *p1, *p2, *p3;
01966 for (p1 = sdpLines; *p1 != '\0'; ++p1) {
01967 for (p2 = p1,p3 = searchStr; tolower(*p2) == *p3; ++p2,++p3) {}
01968 if (*p3 == '\0') {
01969
01970 int beforeTrackNumPosn = p2-sdpLines;
01971
01972 int trackNumLength;
01973 if (sscanf(p2, " %*d%n", &trackNumLength) < 0) break;
01974 int afterTrackNumPosn = beforeTrackNumPosn + trackNumLength;
01975
01976
01977 int i;
01978 for (i = 0; i < beforeTrackNumPosn; ++i) newSDPLines[i] = sdpLines[i];
01979 sprintf(&newSDPLines[i], "%d", fCurrentIOState->fTrackID);
01980 i = afterTrackNumPosn;
01981 int j = i + strlen(&newSDPLines[i]);
01982 while (1) {
01983 if ((newSDPLines[j] = sdpLines[i]) == '\0') break;
01984 ++i; ++j;
01985 }
01986
01987 foundSearchString = True;
01988 break;
01989 }
01990 }
01991
01992 if (!foundSearchString) {
01993
01994
01995 sprintf(newSDPLines, "%s%s%d\r\n",
01996 sdpLines, searchStr, fCurrentIOState->fTrackID);
01997 }
01998
01999 size += addArbitraryString(newSDPLines, False);
02000 delete[] newSDPLines;
02001 addAtomEnd;
02002
02003 addAtom(hinf);
02004 size += addAtom_totl();
02005 size += addAtom_npck();
02006 size += addAtom_tpay();
02007 size += addAtom_trpy();
02008 size += addAtom_nump();
02009 size += addAtom_tpyl();
02010
02011 size += addAtom_dmed();
02012 size += addAtom_dimm();
02013 size += addAtom_drep();
02014 size += addAtom_tmin();
02015 size += addAtom_tmax();
02016 size += addAtom_pmax();
02017 size += addAtom_dmax();
02018 size += addAtom_payt();
02019 addAtomEnd;
02020
02021 addAtom(totl);
02022 size += addWord(fCurrentIOState->fHINF.trpy.lo);
02023 addAtomEnd;
02024
02025 addAtom(npck);
02026 size += addWord(fCurrentIOState->fHINF.nump.lo);
02027 addAtomEnd;
02028
02029 addAtom(tpay);
02030 size += addWord(fCurrentIOState->fHINF.tpyl.lo);
02031 addAtomEnd;
02032
02033 addAtom(trpy);
02034 size += addWord(fCurrentIOState->fHINF.trpy.hi);
02035 size += addWord(fCurrentIOState->fHINF.trpy.lo);
02036 addAtomEnd;
02037
02038 addAtom(nump);
02039 size += addWord(fCurrentIOState->fHINF.nump.hi);
02040 size += addWord(fCurrentIOState->fHINF.nump.lo);
02041 addAtomEnd;
02042
02043 addAtom(tpyl);
02044 size += addWord(fCurrentIOState->fHINF.tpyl.hi);
02045 size += addWord(fCurrentIOState->fHINF.tpyl.lo);
02046 addAtomEnd;
02047
02048 addAtom(dmed);
02049 size += addWord(fCurrentIOState->fHINF.dmed.hi);
02050 size += addWord(fCurrentIOState->fHINF.dmed.lo);
02051 addAtomEnd;
02052
02053 addAtom(dimm);
02054 size += addWord(fCurrentIOState->fHINF.dimm.hi);
02055 size += addWord(fCurrentIOState->fHINF.dimm.lo);
02056 addAtomEnd;
02057
02058 addAtom(drep);
02059 size += addWord(0);
02060 size += addWord(0);
02061 addAtomEnd;
02062
02063 addAtom(tmin);
02064 size += addWord(0);
02065 addAtomEnd;
02066
02067 addAtom(tmax);
02068 size += addWord(0);
02069 addAtomEnd;
02070
02071 addAtom(pmax);
02072 size += addWord(fCurrentIOState->fHINF.pmax);
02073 addAtomEnd;
02074
02075 addAtom(dmax);
02076 size += addWord(fCurrentIOState->fHINF.dmax);
02077 addAtomEnd;
02078
02079 addAtom(payt);
02080 MediaSubsession& ourSubsession = fCurrentIOState->fOurSubsession;
02081 RTPSource* rtpSource = ourSubsession.rtpSource();
02082 size += addByte(rtpSource->rtpPayloadFormat());
02083
02084
02085 unsigned rtpmapStringLength = strlen(ourSubsession.codecName()) + 20;
02086 char* rtpmapString = new char[rtpmapStringLength];
02087 sprintf(rtpmapString, "%s/%d",
02088 ourSubsession.codecName(), rtpSource->timestampFrequency());
02089 size += addArbitraryString(rtpmapString);
02090 delete[] rtpmapString;
02091 addAtomEnd;
02092
02093
02094 unsigned QuickTimeFileSink::addAtom_dummy() {
02095 unsigned initFilePosn = ftell(fOutFid);
02096 unsigned size = addAtomHeader("????");
02097 addAtomEnd;