#include <QuickTimeFileSink.hh>
Inheritance diagram for QuickTimeFileSink:


Public Types | |
| typedef void( | afterPlayingFunc )(void *clientData) |
Public Member Functions | |
| Boolean | startPlaying (afterPlayingFunc *afterFunc, void *afterClientData) |
| unsigned | numActiveSubsessions () const |
| UsageEnvironment & | envir () const |
| char const * | name () const |
| virtual Boolean | isSource () const |
| virtual Boolean | isSink () const |
| virtual Boolean | isRTCPInstance () const |
| virtual Boolean | isRTSPClient () const |
| virtual Boolean | isRTSPServer () const |
| virtual Boolean | isMediaSession () const |
| virtual Boolean | isServerMediaSession () const |
| virtual Boolean | isDarwinInjector () const |
Static Public Member Functions | |
| static QuickTimeFileSink * | createNew (UsageEnvironment &env, MediaSession &inputSession, char const *outputFileName, unsigned bufferSize=20000, unsigned short movieWidth=240, unsigned short movieHeight=180, unsigned movieFPS=15, Boolean packetLossCompensate=False, Boolean syncStreams=False, Boolean generateHintTracks=False, Boolean generateMP4Format=False) |
| static Boolean | lookupByName (UsageEnvironment &env, char const *mediumName, Medium *&resultMedium) |
| static void | close (UsageEnvironment &env, char const *mediumName) |
| static void | close (Medium *medium) |
Protected Member Functions | |
| TaskToken & | nextTask () |
Private Member Functions | |
| QuickTimeFileSink (UsageEnvironment &env, MediaSession &inputSession, FILE *outFid, unsigned bufferSize, unsigned short movieWidth, unsigned short movieHeight, unsigned movieFPS, Boolean packetLossCompensate, Boolean syncStreams, Boolean generateHintTracks, Boolean generateMP4Format) | |
| virtual | ~QuickTimeFileSink () |
| Boolean | continuePlaying () |
| void | onSourceClosure1 () |
| void | completeOutputFile () |
| unsigned | addWord (unsigned word) |
| unsigned | addHalfWord (unsigned short halfWord) |
| unsigned | addByte (unsigned char byte) |
| unsigned | addZeroWords (unsigned numWords) |
| unsigned | add4ByteString (char const *str) |
| unsigned | addArbitraryString (char const *str, Boolean oneByteLength=True) |
| unsigned | addAtomHeader (char const *atomName) |
| void | setWord (unsigned filePosn, unsigned size) |
| unsigned | movieTimeScale () const |
| _atom (ftyp) | |
| _atom (moov) | |
| _atom (mvhd) | |
| _atom (iods) | |
| _atom (trak) | |
| _atom (tkhd) | |
| _atom (edts) | |
| _atom (elst) | |
| _atom (tref) | |
| _atom (hint) | |
| _atom (mdia) | |
| _atom (mdhd) | |
| _atom (hdlr) | |
| _atom (minf) | |
| _atom (smhd) | |
| _atom (vmhd) | |
| _atom (gmhd) | |
| _atom (gmin) | |
| unsigned | addAtom_hdlr2 () |
| _atom (dinf) | |
| _atom (dref) | |
| _atom (alis) | |
| _atom (stbl) | |
| _atom (stsd) | |
| unsigned | addAtom_genericMedia () |
| unsigned | addAtom_soundMediaGeneral () |
| _atom (ulaw) | |
| _atom (alaw) | |
| _atom (Qclp) | |
| _atom (wave) | |
| _atom (frma) | |
| _atom (Fclp) | |
| _atom (Hclp) | |
| _atom (mp4a) | |
| _atom (esds) | |
| _atom (srcq) | |
| _atom (h263) | |
| _atom (avc1) | |
| _atom (avcC) | |
| _atom (mp4v) | |
| _atom (rtp) | |
| _atom (tims) | |
| _atom (stts) | |
| _atom (stsc) | |
| _atom (stsz) | |
| _atom (stco) | |
| _atom (udta) | |
| _atom (name) | |
| _atom (hnti) | |
| _atom (sdp) | |
| _atom (hinf) | |
| _atom (totl) | |
| _atom (npck) | |
| _atom (tpay) | |
| _atom (trpy) | |
| _atom (nump) | |
| _atom (tpyl) | |
| _atom (dmed) | |
| _atom (dimm) | |
| _atom (drep) | |
| _atom (tmin) | |
| _atom (tmax) | |
| _atom (pmax) | |
| _atom (dmax) | |
| _atom (payt) | |
| unsigned | addAtom_dummy () |
Static Private Member Functions | |
| static void | afterGettingFrame (void *clientData, unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime, unsigned durationInMicroseconds) |
| static void | onSourceClosure (void *clientData) |
| static void | onRTCPBye (void *clientData) |
Private Attributes | |
| MediaSession & | fInputSession |
| FILE * | fOutFid |
| unsigned | fBufferSize |
| Boolean | fPacketLossCompensate |
| Boolean | fSyncStreams |
| Boolean | fGenerateMP4Format |
| timeval fNewestSyncTime | fFirstDataTime |
| Boolean | fAreCurrentlyBeingPlayed |
| afterPlayingFunc * | fAfterFunc |
| void * | fAfterClientData |
| unsigned | fAppleCreationTime |
| unsigned | fLargestRTPtimestampFrequency |
| unsigned | fNumSubsessions |
| unsigned | fNumSyncedSubsessions |
| timeval | fStartTime |
| Boolean | fHaveCompletedOutputFile |
| unsigned short | fMovieWidth |
| unsigned short | fMovieHeight |
| unsigned | fMovieFPS |
| unsigned | fMDATposition |
| unsigned | fMVHD_durationPosn |
| unsigned | fMaxTrackDurationM |
| SubsessionIOState * | fCurrentIOState |
Friends | |
| class | SubsessionIOState |
Definition at line 28 of file QuickTimeFileSink.hh.
| typedef void( QuickTimeFileSink::afterPlayingFunc)(void *clientData) |
Definition at line 42 of file QuickTimeFileSink.hh.
| QuickTimeFileSink::QuickTimeFileSink | ( | UsageEnvironment & | env, | |
| MediaSession & | inputSession, | |||
| FILE * | outFid, | |||
| unsigned | bufferSize, | |||
| unsigned short | movieWidth, | |||
| unsigned short | movieHeight, | |||
| unsigned | movieFPS, | |||
| Boolean | packetLossCompensate, | |||
| Boolean | syncStreams, | |||
| Boolean | generateHintTracks, | |||
| Boolean | generateMP4Format | |||
| ) | [private] |
Definition at line 206 of file QuickTimeFileSink.cpp.
References addAtomHeader(), fAppleCreationTime, fFirstDataTime, fInputSession, fLargestRTPtimestampFrequency, fMDATposition, fMovieFPS, fMovieHeight, fMovieWidth, fNumSubsessions, fOutFid, fStartTime, iter, MediaSubsession::miscPtr, MediaSubsessionIterator::next(), NULL, onRTCPBye(), MediaSubsession::readSource(), MediaSubsession::rtcpInstance(), MediaSubsession::rtpTimestampFrequency(), RTCPInstance::setByeHandler(), SubsessionIOState::setHintTrack(), SubsessionIOState::setQTstate(), subsession, SubsessionIOState, MediaSubsession::videoFPS(), MediaSubsession::videoHeight(), and MediaSubsession::videoWidth().
Referenced by createNew().
00217 : Medium(env), fInputSession(inputSession), fOutFid(outFid), 00218 fBufferSize(bufferSize), fPacketLossCompensate(packetLossCompensate), 00219 fSyncStreams(syncStreams), fGenerateMP4Format(generateMP4Format), 00220 fAreCurrentlyBeingPlayed(False), 00221 fLargestRTPtimestampFrequency(0), 00222 fNumSubsessions(0), fNumSyncedSubsessions(0), 00223 fHaveCompletedOutputFile(False), 00224 fMovieWidth(movieWidth), fMovieHeight(movieHeight), 00225 fMovieFPS(movieFPS), fMaxTrackDurationM(0) { 00226 fNewestSyncTime.tv_sec = fNewestSyncTime.tv_usec = 0; 00227 fFirstDataTime.tv_sec = fFirstDataTime.tv_usec = (unsigned)(~0); 00228 00229 // Set up I/O state for each input subsession: 00230 MediaSubsessionIterator iter(fInputSession); 00231 MediaSubsession* subsession; 00232 while ((subsession = iter.next()) != NULL) { 00233 // Ignore subsessions without a data source: 00234 FramedSource* subsessionSource = subsession->readSource(); 00235 if (subsessionSource == NULL) continue; 00236 00237 // If "subsession's" SDP description specified screen dimension 00238 // or frame rate parameters, then use these. (Note that this must 00239 // be done before the call to "setQTState()" below.) 00240 if (subsession->videoWidth() != 0) { 00241 fMovieWidth = subsession->videoWidth(); 00242 } 00243 if (subsession->videoHeight() != 0) { 00244 fMovieHeight = subsession->videoHeight(); 00245 } 00246 if (subsession->videoFPS() != 0) { 00247 fMovieFPS = subsession->videoFPS(); 00248 } 00249 00250 SubsessionIOState* ioState 00251 = new SubsessionIOState(*this, *subsession); 00252 if (ioState == NULL || !ioState->setQTstate()) { 00253 // We're not able to output a QuickTime track for this subsession 00254 delete ioState; ioState = NULL; 00255 continue; 00256 } 00257 subsession->miscPtr = (void*)ioState; 00258 00259 if (generateHintTracks) { 00260 // Also create a hint track for this track: 00261 SubsessionIOState* hintTrack 00262 = new SubsessionIOState(*this, *subsession); 00263 SubsessionIOState::setHintTrack(ioState, hintTrack); 00264 if (!hintTrack->setQTstate()) { 00265 delete hintTrack; 00266 SubsessionIOState::setHintTrack(ioState, NULL); 00267 } 00268 } 00269 00270 // Also set a 'BYE' handler for this subsession's RTCP instance: 00271 if (subsession->rtcpInstance() != NULL) { 00272 subsession->rtcpInstance()->setByeHandler(onRTCPBye, ioState); 00273 } 00274 00275 unsigned rtpTimestampFrequency = subsession->rtpTimestampFrequency(); 00276 if (rtpTimestampFrequency > fLargestRTPtimestampFrequency) { 00277 fLargestRTPtimestampFrequency = rtpTimestampFrequency; 00278 } 00279 00280 ++fNumSubsessions; 00281 } 00282 00283 // Use the current time as the file's creation and modification 00284 // time. Use Apple's time format: seconds since January 1, 1904 00285 00286 gettimeofday(&fStartTime, NULL); 00287 fAppleCreationTime = fStartTime.tv_sec - 0x83dac000; 00288 00289 // Begin by writing a "mdat" atom at the start of the file. 00290 // (Later, when we've finished copying data to the file, we'll come 00291 // back and fill in its size.) 00292 fMDATposition = ftell(fOutFid); 00293 addAtomHeader("mdat"); 00294 }
| QuickTimeFileSink::~QuickTimeFileSink | ( | ) | [private, virtual] |
Definition at line 296 of file QuickTimeFileSink.cpp.
References completeOutputFile(), SubsessionIOState::fHintTrackForUs, fInputSession, iter, MediaSubsession::miscPtr, MediaSubsessionIterator::next(), NULL, and subsession.
00296 { 00297 completeOutputFile(); 00298 00299 // Then, delete each active "SubsessionIOState": 00300 MediaSubsessionIterator iter(fInputSession); 00301 MediaSubsession* subsession; 00302 while ((subsession = iter.next()) != NULL) { 00303 SubsessionIOState* ioState 00304 = (SubsessionIOState*)(subsession->miscPtr); 00305 if (ioState == NULL) continue; 00306 00307 delete ioState->fHintTrackForUs; // if any 00308 delete ioState; 00309 } 00310 }
| QuickTimeFileSink * QuickTimeFileSink::createNew | ( | UsageEnvironment & | env, | |
| MediaSession & | inputSession, | |||
| char const * | outputFileName, | |||
| unsigned | bufferSize = 20000, |
|||
| unsigned short | movieWidth = 240, |
|||
| unsigned short | movieHeight = 180, |
|||
| unsigned | movieFPS = 15, |
|||
| Boolean | packetLossCompensate = False, |
|||
| Boolean | syncStreams = False, |
|||
| Boolean | generateHintTracks = False, |
|||
| Boolean | generateMP4Format = False | |||
| ) | [static] |
Definition at line 313 of file QuickTimeFileSink.cpp.
References env, NULL, OpenOutputFile(), and QuickTimeFileSink().
Referenced by main().
00323 { 00324 do { 00325 FILE* fid = OpenOutputFile(env, outputFileName); 00326 if (fid == NULL) break; 00327 00328 return new QuickTimeFileSink(env, inputSession, fid, bufferSize, 00329 movieWidth, movieHeight, movieFPS, 00330 packetLossCompensate, syncStreams, 00331 generateHintTracks, generateMP4Format); 00332 } while (0); 00333 00334 return NULL; 00335 }
| Boolean QuickTimeFileSink::startPlaying | ( | afterPlayingFunc * | afterFunc, | |
| void * | afterClientData | |||
| ) |
Definition at line 337 of file QuickTimeFileSink.cpp.
References continuePlaying(), Medium::envir(), fAfterClientData, fAfterFunc, False, fAreCurrentlyBeingPlayed, UsageEnvironment::setResultMsg(), and True.
Referenced by main().
00338 { 00339 // Make sure we're not already being played: 00340 if (fAreCurrentlyBeingPlayed) { 00341 envir().setResultMsg("This sink has already been played"); 00342 return False; 00343 } 00344 00345 fAreCurrentlyBeingPlayed = True; 00346 fAfterFunc = afterFunc; 00347 fAfterClientData = afterClientData; 00348 00349 return continuePlaying(); 00350 }
| unsigned QuickTimeFileSink::numActiveSubsessions | ( | ) | const [inline] |
Definition at line 46 of file QuickTimeFileSink.hh.
References fNumSubsessions.
Referenced by checkForPacketArrival().
00046 { return fNumSubsessions; }
| Boolean QuickTimeFileSink::continuePlaying | ( | ) | [private] |
Definition at line 352 of file QuickTimeFileSink.cpp.
References afterGettingFrame(), SubsessionBuffer::bytesAvailable(), SubsessionBuffer::dataEnd(), Medium::envir(), False, SubsessionIOState::fBuffer, fInputSession, FramedSource::getNextFrame(), FramedSource::isCurrentlyAwaitingData(), iter, MediaSubsession::miscPtr, MediaSubsessionIterator::next(), NULL, onSourceClosure(), MediaSubsession::readSource(), UsageEnvironment::setResultMsg(), subsession, and True.
Referenced by SubsessionIOState::afterGettingFrame(), afterGettingFrame(), and startPlaying().
00352 { 00353 // Run through each of our input session's 'subsessions', 00354 // asking for a frame from each one: 00355 Boolean haveActiveSubsessions = False; 00356 MediaSubsessionIterator iter(fInputSession); 00357 MediaSubsession* subsession; 00358 while ((subsession = iter.next()) != NULL) { 00359 FramedSource* subsessionSource = subsession->readSource(); 00360 if (subsessionSource == NULL) continue; 00361 00362 if (subsessionSource->isCurrentlyAwaitingData()) continue; 00363 00364 SubsessionIOState* ioState 00365 = (SubsessionIOState*)(subsession->miscPtr); 00366 if (ioState == NULL) continue; 00367 00368 haveActiveSubsessions = True; 00369 unsigned char* toPtr = ioState->fBuffer->dataEnd(); 00370 unsigned toSize = ioState->fBuffer->bytesAvailable(); 00371 subsessionSource->getNextFrame(toPtr, toSize, 00372 afterGettingFrame, ioState, 00373 onSourceClosure, ioState); 00374 } 00375 if (!haveActiveSubsessions) { 00376 envir().setResultMsg("No subsessions are currently active"); 00377 return False; 00378 } 00379 00380 return True; 00381 }
| void QuickTimeFileSink::afterGettingFrame | ( | void * | clientData, | |
| unsigned | frameSize, | |||
| unsigned | numTruncatedBytes, | |||
| struct timeval | presentationTime, | |||
| unsigned | durationInMicroseconds | |||
| ) | [static, private] |
Definition at line 384 of file QuickTimeFileSink.cpp.
References SubsessionIOState::afterGettingFrame(), continuePlaying(), SubsessionIOState::fOurSink, and SubsessionIOState::syncOK().
Referenced by continuePlaying().
00387 { 00388 SubsessionIOState* ioState = (SubsessionIOState*)clientData; 00389 if (!ioState->syncOK(presentationTime)) { 00390 // Ignore this data: 00391 ioState->fOurSink.continuePlaying(); 00392 return; 00393 } 00394 ioState->afterGettingFrame(packetDataSize, presentationTime); 00395 }
| void QuickTimeFileSink::onSourceClosure | ( | void * | clientData | ) | [static, private] |
Definition at line 397 of file QuickTimeFileSink.cpp.
References SubsessionIOState::onSourceClosure().
Referenced by continuePlaying().
00397 { 00398 SubsessionIOState* ioState = (SubsessionIOState*)clientData; 00399 ioState->onSourceClosure(); 00400 }
| void QuickTimeFileSink::onSourceClosure1 | ( | ) | [private] |
Definition at line 402 of file QuickTimeFileSink.cpp.
References completeOutputFile(), fAfterClientData, fAfterFunc, fInputSession, SubsessionIOState::fOurSourceIsActive, iter, MediaSubsession::miscPtr, MediaSubsessionIterator::next(), NULL, and subsession.
Referenced by SubsessionIOState::onSourceClosure().
00402 { 00403 // Check whether *all* of the subsession sources have closed. 00404 // If not, do nothing for now: 00405 MediaSubsessionIterator iter(fInputSession); 00406 MediaSubsession* subsession; 00407 while ((subsession = iter.next()) != NULL) { 00408 SubsessionIOState* ioState 00409 = (SubsessionIOState*)(subsession->miscPtr); 00410 if (ioState == NULL) continue; 00411 00412 if (ioState->fOurSourceIsActive) return; // this source hasn't closed 00413 } 00414 00415 completeOutputFile(); 00416 00417 // Call our specified 'after' function: 00418 if (fAfterFunc != NULL) { 00419 (*fAfterFunc)(fAfterClientData); 00420 } 00421 }
| void QuickTimeFileSink::onRTCPBye | ( | void * | clientData | ) | [static, private] |
Definition at line 423 of file QuickTimeFileSink.cpp.
References MediaSubsession::codecName(), SubsessionIOState::envir(), SubsessionIOState::fOurSink, SubsessionIOState::fOurSubsession, fStartTime, MediaSubsession::mediumName(), NULL, SubsessionIOState::onSourceClosure(), and subsession.
Referenced by QuickTimeFileSink().
00423 { 00424 SubsessionIOState* ioState = (SubsessionIOState*)clientData; 00425 00426 struct timeval timeNow; 00427 gettimeofday(&timeNow, NULL); 00428 unsigned secsDiff 00429 = timeNow.tv_sec - ioState->fOurSink.fStartTime.tv_sec; 00430 00431 MediaSubsession& subsession = ioState->fOurSubsession; 00432 ioState->envir() << "Received RTCP \"BYE\" on \"" 00433 << subsession.mediumName() 00434 << "/" << subsession.codecName() 00435 << "\" subsession (after " 00436 << secsDiff << " seconds)\n"; 00437 00438 // Handle the reception of a RTCP "BYE" as if the source had closed: 00439 ioState->onSourceClosure(); 00440 }
| void QuickTimeFileSink::completeOutputFile | ( | ) | [private] |
Definition at line 449 of file QuickTimeFileSink.cpp.
References fFirstDataTime, fGenerateMP4Format, fHaveCompletedOutputFile, SubsessionIOState::fHeadChunk, SubsessionIOState::fHintTrackForUs, fInputSession, fMDATposition, fOutFid, ChunkDescriptor::fPresentationTime, SubsessionIOState::hasHintTrack(), iter, MediaSubsession::miscPtr, MediaSubsessionIterator::next(), NULL, MediaSubsessionIterator::reset(), SubsessionIOState::setFinalQTstate(), setWord(), subsession, timevalGE(), and True.
Referenced by onSourceClosure1(), and ~QuickTimeFileSink().
00449 { 00450 if (fHaveCompletedOutputFile || fOutFid == NULL) return; 00451 00452 // Begin by filling in the initial "mdat" atom with the current 00453 // file size: 00454 unsigned curFileSize = ftell(fOutFid); 00455 setWord(fMDATposition, curFileSize); 00456 00457 // Then, note the time of the first received data: 00458 MediaSubsessionIterator iter(fInputSession); 00459 MediaSubsession* subsession; 00460 while ((subsession = iter.next()) != NULL) { 00461 SubsessionIOState* ioState 00462 = (SubsessionIOState*)(subsession->miscPtr); 00463 if (ioState == NULL) continue; 00464 00465 ChunkDescriptor* const headChunk = ioState->fHeadChunk; 00466 if (headChunk != NULL 00467 && timevalGE(fFirstDataTime, headChunk->fPresentationTime)) { 00468 fFirstDataTime = headChunk->fPresentationTime; 00469 } 00470 } 00471 00472 // Then, update the QuickTime-specific state for each active track: 00473 iter.reset(); 00474 while ((subsession = iter.next()) != NULL) { 00475 SubsessionIOState* ioState 00476 = (SubsessionIOState*)(subsession->miscPtr); 00477 if (ioState == NULL) continue; 00478 00479 ioState->setFinalQTstate(); 00480 // Do the same for a hint track (if any): 00481 if (ioState->hasHintTrack()) { 00482 ioState->fHintTrackForUs->setFinalQTstate(); 00483 } 00484 } 00485 00486 if (fGenerateMP4Format) { 00487 // Begin with a "ftyp" atom: 00488 addAtom_ftyp(); 00489 } 00490 00491 // Then, add a "moov" atom for the file metadata: 00492 addAtom_moov(); 00493 00494 // We're done: 00495 fHaveCompletedOutputFile = True; 00496 }
| unsigned QuickTimeFileSink::addWord | ( | unsigned | word | ) | [private] |
Definition at line 1129 of file QuickTimeFileSink.cpp.
References addByte().
Referenced by addAtom_hdlr2(), addAtomHeader(), addZeroWords(), if(), setWord(), SubsessionIOState::useFrame(), and SubsessionIOState::useFrameForHinting().
01129 { 01130 addByte(word>>24); addByte(word>>16); 01131 addByte(word>>8); addByte(word); 01132 01133 return 4; 01134 }
| unsigned QuickTimeFileSink::addHalfWord | ( | unsigned short | halfWord | ) | [private] |
Definition at line 1136 of file QuickTimeFileSink.cpp.
References addByte().
Referenced by addAtom_hdlr2(), and SubsessionIOState::useFrameForHinting().
01136 { 01137 addByte((unsigned char)(halfWord>>8)); addByte((unsigned char)halfWord); 01138 01139 return 2; 01140 }
| unsigned QuickTimeFileSink::addByte | ( | unsigned char | byte | ) | [inline, private] |
Definition at line 90 of file QuickTimeFileSink.hh.
References fOutFid.
Referenced by add4ByteString(), addArbitraryString(), addAtom_hdlr2(), addHalfWord(), addWord(), and SubsessionIOState::useFrameForHinting().
00090 { 00091 putc(byte, fOutFid); 00092 return 1; 00093 }
| unsigned QuickTimeFileSink::addZeroWords | ( | unsigned | numWords | ) | [private] |
Definition at line 1142 of file QuickTimeFileSink.cpp.
References addWord().
Referenced by addAtom_hdlr2().
01142 { 01143 for (unsigned i = 0; i < numWords; ++i) { 01144 addWord(0); 01145 } 01146 01147 return numWords*4; 01148 }
| unsigned QuickTimeFileSink::add4ByteString | ( | char const * | str | ) | [private] |
Definition at line 1150 of file QuickTimeFileSink.cpp.
References addByte().
Referenced by addAtom_hdlr2(), and addAtomHeader().
01150 { 01151 addByte(str[0]); addByte(str[1]); addByte(str[2]); addByte(str[3]); 01152 01153 return 4; 01154 }
| unsigned QuickTimeFileSink::addArbitraryString | ( | char const * | str, | |
| Boolean | oneByteLength = True | |||
| ) | [private] |
Definition at line 1156 of file QuickTimeFileSink.cpp.
References addByte(), Medium::envir(), and size.
Referenced by addAtom_hdlr2().
01157 { 01158 unsigned size = 0; 01159 if (oneByteLength) { 01160 // Begin with a byte containing the string length: 01161 unsigned strLength = strlen(str); 01162 if (strLength >= 256) { 01163 envir() << "QuickTimeFileSink::addArbitraryString(\"" 01164 << str << "\") saw string longer than we know how to handle (" 01165 << strLength << "\n"; 01166 } 01167 size += addByte((unsigned char)strLength); 01168