1 | /********** |
2 | This library is free software; you can redistribute it and/or modify it under |
3 | the terms of the GNU Lesser General Public License as published by the |
4 | Free Software Foundation; either version 3 of the License, or (at your |
5 | option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.) |
6 | |
7 | This library is distributed in the hope that it will be useful, but WITHOUT |
8 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
9 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for |
10 | more details. |
11 | |
12 | You should have received a copy of the GNU Lesser General Public License |
13 | along with this library; if not, write to the Free Software Foundation, Inc., |
14 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
15 | **********/ |
16 | // "liveMedia" |
17 | // Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved. |
18 | // 'ADU' MP3 streams (for improved loss-tolerance) |
19 | // Implementation |
20 | |
21 | #include "MP3ADU.hh" |
22 | #include "MP3ADUdescriptor.hh" |
23 | #include "MP3Internals.hh" |
24 | #include <string.h> |
25 | |
26 | #ifdef TEST_LOSS |
27 | #include "GroupsockHelper.hh" |
28 | #endif |
29 | |
30 | // Segment data structures, used in the implementation below: |
31 | |
32 | #define SegmentBufSize 2000 /* conservatively high */ |
33 | |
34 | class Segment { |
35 | public: |
36 | unsigned char buf[SegmentBufSize]; |
37 | unsigned char* dataStart() { return &buf[descriptorSize]; } |
38 | unsigned frameSize; // if it's a non-ADU frame |
39 | unsigned dataHere(); // if it's a non-ADU frame |
40 | |
41 | unsigned descriptorSize; |
42 | static unsigned const ; |
43 | unsigned sideInfoSize, aduSize; |
44 | unsigned backpointer; |
45 | |
46 | struct timeval presentationTime; |
47 | unsigned durationInMicroseconds; |
48 | }; |
49 | |
50 | unsigned const Segment:: = 4; |
51 | |
52 | #define SegmentQueueSize 20 |
53 | |
54 | class SegmentQueue { |
55 | public: |
56 | SegmentQueue(Boolean directionIsToADU, Boolean includeADUdescriptors) |
57 | : fDirectionIsToADU(directionIsToADU), |
58 | fIncludeADUdescriptors(includeADUdescriptors) { |
59 | reset(); |
60 | } |
61 | |
62 | Segment s[SegmentQueueSize]; |
63 | |
64 | unsigned headIndex() {return fHeadIndex;} |
65 | Segment& headSegment() {return s[fHeadIndex];} |
66 | |
67 | unsigned nextFreeIndex() {return fNextFreeIndex;} |
68 | Segment& nextFreeSegment() {return s[fNextFreeIndex];} |
69 | Boolean isEmpty() {return isEmptyOrFull() && totalDataSize() == 0;} |
70 | Boolean isFull() {return isEmptyOrFull() && totalDataSize() > 0;} |
71 | |
72 | static unsigned nextIndex(unsigned ix) {return (ix+1)%SegmentQueueSize;} |
73 | static unsigned prevIndex(unsigned ix) {return (ix+SegmentQueueSize-1)%SegmentQueueSize;} |
74 | |
75 | unsigned totalDataSize() {return fTotalDataSize;} |
76 | |
77 | void enqueueNewSegment(FramedSource* inputSource, FramedSource* usingSource); |
78 | |
79 | Boolean dequeue(); |
80 | |
81 | Boolean insertDummyBeforeTail(unsigned backpointer); |
82 | |
83 | void reset() { fHeadIndex = fNextFreeIndex = fTotalDataSize = 0; } |
84 | |
85 | private: |
86 | static void sqAfterGettingSegment(void* clientData, |
87 | unsigned numBytesRead, |
88 | unsigned numTruncatedBytes, |
89 | struct timeval presentationTime, |
90 | unsigned durationInMicroseconds); |
91 | |
92 | Boolean sqAfterGettingCommon(Segment& seg, unsigned numBytesRead); |
93 | Boolean isEmptyOrFull() {return headIndex() == nextFreeIndex();} |
94 | |
95 | unsigned fHeadIndex, fNextFreeIndex, fTotalDataSize; |
96 | |
97 | // The following is used for asynchronous reads: |
98 | FramedSource* fUsingSource; |
99 | |
100 | // This tells us whether the direction in which we're being used |
101 | // is MP3->ADU, or vice-versa. (This flag is used for debugging output.) |
102 | Boolean fDirectionIsToADU; |
103 | |
104 | // The following is true iff we're used to enqueue incoming |
105 | // ADU frames, and these have an ADU descriptor in front |
106 | Boolean fIncludeADUdescriptors; |
107 | }; |
108 | |
109 | ////////// ADUFromMP3Source ////////// |
110 | |
111 | ADUFromMP3Source::ADUFromMP3Source(UsageEnvironment& env, |
112 | FramedSource* inputSource, |
113 | Boolean includeADUdescriptors) |
114 | : FramedFilter(env, inputSource), |
115 | fAreEnqueueingMP3Frame(False), |
116 | fSegments(new SegmentQueue(True /* because we're MP3->ADU */, |
117 | False /*no descriptors in incoming frames*/)), |
118 | fIncludeADUdescriptors(includeADUdescriptors), |
119 | fTotalDataSizeBeforePreviousRead(0), fScale(1), fFrameCounter(0) { |
120 | } |
121 | |
122 | ADUFromMP3Source::~ADUFromMP3Source() { |
123 | delete fSegments; |
124 | } |
125 | |
126 | |
127 | char const* ADUFromMP3Source::MIMEtype() const { |
128 | return "audio/MPA-ROBUST" ; |
129 | } |
130 | |
131 | ADUFromMP3Source* ADUFromMP3Source::createNew(UsageEnvironment& env, |
132 | FramedSource* inputSource, |
133 | Boolean includeADUdescriptors) { |
134 | // The source must be a MPEG audio source: |
135 | if (strcmp(inputSource->MIMEtype(), "audio/MPEG" ) != 0) { |
136 | env.setResultMsg(inputSource->name(), " is not an MPEG audio source" ); |
137 | return NULL; |
138 | } |
139 | |
140 | return new ADUFromMP3Source(env, inputSource, includeADUdescriptors); |
141 | } |
142 | |
143 | void ADUFromMP3Source::resetInput() { |
144 | fSegments->reset(); |
145 | } |
146 | |
147 | Boolean ADUFromMP3Source::setScaleFactor(int scale) { |
148 | if (scale < 1) return False; |
149 | fScale = scale; |
150 | return True; |
151 | } |
152 | |
153 | void ADUFromMP3Source::doGetNextFrame() { |
154 | if (!fAreEnqueueingMP3Frame) { |
155 | // Arrange to enqueue a new MP3 frame: |
156 | fTotalDataSizeBeforePreviousRead = fSegments->totalDataSize(); |
157 | fAreEnqueueingMP3Frame = True; |
158 | fSegments->enqueueNewSegment(fInputSource, this); |
159 | } else { |
160 | // Deliver an ADU from a previously-read MP3 frame: |
161 | fAreEnqueueingMP3Frame = False; |
162 | |
163 | if (!doGetNextFrame1()) { |
164 | // An internal error occurred; act as if our source went away: |
165 | handleClosure(); |
166 | } |
167 | } |
168 | } |
169 | |
170 | Boolean ADUFromMP3Source::doGetNextFrame1() { |
171 | // First, check whether we have enough previously-read data to output an |
172 | // ADU for the last-read MP3 frame: |
173 | unsigned tailIndex; |
174 | Segment* tailSeg; |
175 | Boolean needMoreData; |
176 | |
177 | if (fSegments->isEmpty()) { |
178 | needMoreData = True; |
179 | tailSeg = NULL; tailIndex = 0; // unneeded, but stops compiler warnings |
180 | } else { |
181 | tailIndex = SegmentQueue::prevIndex(fSegments->nextFreeIndex()); |
182 | tailSeg = &(fSegments->s[tailIndex]); |
183 | |
184 | needMoreData |
185 | = fTotalDataSizeBeforePreviousRead < tailSeg->backpointer // bp points back too far |
186 | || tailSeg->backpointer + tailSeg->dataHere() < tailSeg->aduSize; // not enough data |
187 | } |
188 | |
189 | if (needMoreData) { |
190 | // We don't have enough data to output an ADU from the last-read MP3 |
191 | // frame, so need to read another one and try again: |
192 | doGetNextFrame(); |
193 | return True; |
194 | } |
195 | |
196 | // Output an ADU from the tail segment: |
197 | fFrameSize = tailSeg->headerSize+tailSeg->sideInfoSize+tailSeg->aduSize; |
198 | fPresentationTime = tailSeg->presentationTime; |
199 | fDurationInMicroseconds = tailSeg->durationInMicroseconds; |
200 | unsigned descriptorSize |
201 | = fIncludeADUdescriptors ? ADUdescriptor::computeSize(fFrameSize) : 0; |
202 | #ifdef DEBUG |
203 | fprintf(stderr, "m->a:outputting ADU %d<-%d, nbr:%d, sis:%d, dh:%d, (descriptor size: %d)\n" , tailSeg->aduSize, tailSeg->backpointer, fFrameSize, tailSeg->sideInfoSize, tailSeg->dataHere(), descriptorSize); |
204 | #endif |
205 | if (descriptorSize + fFrameSize > fMaxSize) { |
206 | envir() << "ADUFromMP3Source::doGetNextFrame1(): not enough room (" |
207 | << descriptorSize + fFrameSize << ">" |
208 | << fMaxSize << ")\n" ; |
209 | fFrameSize = 0; |
210 | return False; |
211 | } |
212 | |
213 | unsigned char* toPtr = fTo; |
214 | // output the ADU descriptor: |
215 | if (fIncludeADUdescriptors) { |
216 | fFrameSize += ADUdescriptor::generateDescriptor(toPtr, fFrameSize); |
217 | } |
218 | |
219 | // output header and side info: |
220 | memmove(toPtr, tailSeg->dataStart(), |
221 | tailSeg->headerSize + tailSeg->sideInfoSize); |
222 | toPtr += tailSeg->headerSize + tailSeg->sideInfoSize; |
223 | |
224 | // go back to the frame that contains the start of our data: |
225 | unsigned offset = 0; |
226 | unsigned i = tailIndex; |
227 | unsigned prevBytes = tailSeg->backpointer; |
228 | while (prevBytes > 0) { |
229 | i = SegmentQueue::prevIndex(i); |
230 | unsigned dataHere = fSegments->s[i].dataHere(); |
231 | if (dataHere < prevBytes) { |
232 | prevBytes -= dataHere; |
233 | } else { |
234 | offset = dataHere - prevBytes; |
235 | break; |
236 | } |
237 | } |
238 | |
239 | // dequeue any segments that we no longer need: |
240 | while (fSegments->headIndex() != i) { |
241 | fSegments->dequeue(); // we're done with it |
242 | } |
243 | |
244 | unsigned bytesToUse = tailSeg->aduSize; |
245 | while (bytesToUse > 0) { |
246 | Segment& seg = fSegments->s[i]; |
247 | unsigned char* fromPtr |
248 | = &seg.dataStart()[seg.headerSize + seg.sideInfoSize + offset]; |
249 | unsigned dataHere = seg.dataHere() - offset; |
250 | unsigned bytesUsedHere = dataHere < bytesToUse ? dataHere : bytesToUse; |
251 | memmove(toPtr, fromPtr, bytesUsedHere); |
252 | bytesToUse -= bytesUsedHere; |
253 | toPtr += bytesUsedHere; |
254 | offset = 0; |
255 | i = SegmentQueue::nextIndex(i); |
256 | } |
257 | |
258 | |
259 | if (fFrameCounter++%fScale == 0) { |
260 | // Call our own 'after getting' function. Because we're not a 'leaf' |
261 | // source, we can call this directly, without risking infinite recursion. |
262 | afterGetting(this); |
263 | } else { |
264 | // Don't use this frame; get another one: |
265 | doGetNextFrame(); |
266 | } |
267 | |
268 | return True; |
269 | } |
270 | |
271 | |
272 | ////////// MP3FromADUSource ////////// |
273 | |
274 | MP3FromADUSource::MP3FromADUSource(UsageEnvironment& env, |
275 | FramedSource* inputSource, |
276 | Boolean includeADUdescriptors) |
277 | : FramedFilter(env, inputSource), |
278 | fAreEnqueueingADU(False), |
279 | fSegments(new SegmentQueue(False /* because we're ADU->MP3 */, |
280 | includeADUdescriptors)) { |
281 | } |
282 | |
283 | MP3FromADUSource::~MP3FromADUSource() { |
284 | delete fSegments; |
285 | } |
286 | |
287 | char const* MP3FromADUSource::MIMEtype() const { |
288 | return "audio/MPEG" ; |
289 | } |
290 | |
291 | MP3FromADUSource* MP3FromADUSource::createNew(UsageEnvironment& env, |
292 | FramedSource* inputSource, |
293 | Boolean includeADUdescriptors) { |
294 | // The source must be an MP3 ADU source: |
295 | if (strcmp(inputSource->MIMEtype(), "audio/MPA-ROBUST" ) != 0) { |
296 | env.setResultMsg(inputSource->name(), " is not an MP3 ADU source" ); |
297 | return NULL; |
298 | } |
299 | |
300 | return new MP3FromADUSource(env, inputSource, includeADUdescriptors); |
301 | } |
302 | |
303 | |
304 | void MP3FromADUSource::doGetNextFrame() { |
305 | if (fAreEnqueueingADU) insertDummyADUsIfNecessary(); |
306 | fAreEnqueueingADU = False; |
307 | |
308 | if (needToGetAnADU()) { |
309 | // Before returning a frame, we must enqueue at least one ADU: |
310 | #ifdef TEST_LOSS |
311 | NOTE: This code no longer works, because it uses synchronous reads, |
312 | which are no longer supported. |
313 | static unsigned const framesPerPacket = 10; |
314 | static unsigned const frameCount = 0; |
315 | static Boolean packetIsLost; |
316 | while (1) { |
317 | if ((frameCount++)%framesPerPacket == 0) { |
318 | packetIsLost = (our_random()%10 == 0); // simulate 10% packet loss ##### |
319 | } |
320 | |
321 | if (packetIsLost) { |
322 | // Read and discard the next input frame (that would be part of |
323 | // a lost packet): |
324 | Segment dummySegment; |
325 | unsigned numBytesRead; |
326 | struct timeval presentationTime; |
327 | // (this works only if the source can be read synchronously) |
328 | fInputSource->syncGetNextFrame(dummySegment.buf, |
329 | sizeof dummySegment.buf, numBytesRead, |
330 | presentationTime); |
331 | } else { |
332 | break; // from while (1) |
333 | } |
334 | } |
335 | #endif |
336 | |
337 | fAreEnqueueingADU = True; |
338 | fSegments->enqueueNewSegment(fInputSource, this); |
339 | } else { |
340 | // Return a frame now: |
341 | generateFrameFromHeadADU(); |
342 | // sets fFrameSize, fPresentationTime, and fDurationInMicroseconds |
343 | |
344 | // Call our own 'after getting' function. Because we're not a 'leaf' |
345 | // source, we can call this directly, without risking infinite recursion. |
346 | afterGetting(this); |
347 | } |
348 | } |
349 | |
350 | Boolean MP3FromADUSource::needToGetAnADU() { |
351 | // Check whether we need to first enqueue a new ADU before we |
352 | // can generate a frame for our head ADU. |
353 | Boolean needToEnqueue = True; |
354 | |
355 | if (!fSegments->isEmpty()) { |
356 | unsigned index = fSegments->headIndex(); |
357 | Segment* seg = &(fSegments->headSegment()); |
358 | int const endOfHeadFrame = (int) seg->dataHere(); |
359 | unsigned frameOffset = 0; |
360 | |
361 | while (1) { |
362 | int endOfData = frameOffset - seg->backpointer + seg->aduSize; |
363 | if (endOfData >= endOfHeadFrame) { |
364 | // We already have enough data to generate a frame |
365 | needToEnqueue = False; |
366 | break; |
367 | } |
368 | |
369 | frameOffset += seg->dataHere(); |
370 | index = SegmentQueue::nextIndex(index); |
371 | if (index == fSegments->nextFreeIndex()) break; |
372 | seg = &(fSegments->s[index]); |
373 | } |
374 | } |
375 | |
376 | return needToEnqueue; |
377 | } |
378 | |
379 | void MP3FromADUSource::insertDummyADUsIfNecessary() { |
380 | if (fSegments->isEmpty()) return; // shouldn't happen |
381 | |
382 | // The tail segment (ADU) is assumed to have been recently |
383 | // enqueued. If its backpointer would overlap the data |
384 | // of the previous ADU, then we need to insert one or more |
385 | // empty, 'dummy' ADUs ahead of it. (This situation should occur |
386 | // only if an intermediate ADU was lost.) |
387 | |
388 | unsigned tailIndex |
389 | = SegmentQueue::prevIndex(fSegments->nextFreeIndex()); |
390 | Segment* tailSeg = &(fSegments->s[tailIndex]); |
391 | |
392 | while (1) { |
393 | unsigned prevADUend; // relative to the start of the new ADU |
394 | if (fSegments->headIndex() != tailIndex) { |
395 | // there is a previous segment |
396 | unsigned prevIndex = SegmentQueue::prevIndex(tailIndex); |
397 | Segment& prevSegment = fSegments->s[prevIndex]; |
398 | prevADUend = prevSegment.dataHere() + prevSegment.backpointer; |
399 | if (prevSegment.aduSize > prevADUend) { |
400 | // shouldn't happen if the previous ADU was well-formed |
401 | prevADUend = 0; |
402 | } else { |
403 | prevADUend -= prevSegment.aduSize; |
404 | } |
405 | } else { |
406 | prevADUend = 0; |
407 | } |
408 | |
409 | if (tailSeg->backpointer > prevADUend) { |
410 | // We need to insert a dummy ADU in front of the tail |
411 | #ifdef DEBUG |
412 | fprintf(stderr, "a->m:need to insert a dummy ADU (%d, %d, %d) [%d, %d]\n" , tailSeg->backpointer, prevADUend, tailSeg->dataHere(), fSegments->headIndex(), fSegments->nextFreeIndex()); |
413 | #endif |
414 | tailIndex = fSegments->nextFreeIndex(); |
415 | if (!fSegments->insertDummyBeforeTail(prevADUend)) return; |
416 | tailSeg = &(fSegments->s[tailIndex]); |
417 | } else { |
418 | break; // no more dummy ADUs need to be inserted |
419 | } |
420 | } |
421 | } |
422 | |
423 | Boolean MP3FromADUSource::generateFrameFromHeadADU() { |
424 | // Output a frame for the head ADU: |
425 | if (fSegments->isEmpty()) return False; |
426 | unsigned index = fSegments->headIndex(); |
427 | Segment* seg = &(fSegments->headSegment()); |
428 | #ifdef DEBUG |
429 | fprintf(stderr, "a->m:outputting frame for %d<-%d (fs %d, dh %d), (descriptorSize: %d)\n" , seg->aduSize, seg->backpointer, seg->frameSize, seg->dataHere(), seg->descriptorSize); |
430 | #endif |
431 | unsigned char* toPtr = fTo; |
432 | |
433 | // output header and side info: |
434 | fFrameSize = seg->frameSize; |
435 | fPresentationTime = seg->presentationTime; |
436 | fDurationInMicroseconds = seg->durationInMicroseconds; |
437 | memmove(toPtr, seg->dataStart(), seg->headerSize + seg->sideInfoSize); |
438 | toPtr += seg->headerSize + seg->sideInfoSize; |
439 | |
440 | // zero out the rest of the frame, in case ADU data doesn't fill it all in |
441 | unsigned bytesToZero = seg->dataHere(); |
442 | for (unsigned i = 0; i < bytesToZero; ++i) { |
443 | toPtr[i] = '\0'; |
444 | } |
445 | |
446 | // Fill in the frame with appropriate ADU data from this and |
447 | // subsequent ADUs: |
448 | unsigned frameOffset = 0; |
449 | unsigned toOffset = 0; |
450 | unsigned const endOfHeadFrame = seg->dataHere(); |
451 | |
452 | while (toOffset < endOfHeadFrame) { |
453 | int startOfData = frameOffset - seg->backpointer; |
454 | if (startOfData > (int)endOfHeadFrame) break; // no more ADUs needed |
455 | |
456 | int endOfData = startOfData + seg->aduSize; |
457 | if (endOfData > (int)endOfHeadFrame) { |
458 | endOfData = endOfHeadFrame; |
459 | } |
460 | |
461 | unsigned fromOffset; |
462 | if (startOfData <= (int)toOffset) { |
463 | fromOffset = toOffset - startOfData; |
464 | startOfData = toOffset; |
465 | if (endOfData < startOfData) endOfData = startOfData; |
466 | } else { |
467 | fromOffset = 0; |
468 | |
469 | // we may need some padding bytes beforehand |
470 | unsigned bytesToZero = startOfData - toOffset; |
471 | #ifdef DEBUG |
472 | if (bytesToZero > 0) fprintf(stderr, "a->m:outputting %d zero bytes (%d, %d, %d, %d)\n" , bytesToZero, startOfData, toOffset, frameOffset, seg->backpointer); |
473 | #endif |
474 | toOffset += bytesToZero; |
475 | } |
476 | |
477 | unsigned char* fromPtr |
478 | = &seg->dataStart()[seg->headerSize + seg->sideInfoSize + fromOffset]; |
479 | unsigned bytesUsedHere = endOfData - startOfData; |
480 | #ifdef DEBUG |
481 | if (bytesUsedHere > 0) fprintf(stderr, "a->m:outputting %d bytes from %d<-%d\n" , bytesUsedHere, seg->aduSize, seg->backpointer); |
482 | #endif |
483 | memmove(toPtr + toOffset, fromPtr, bytesUsedHere); |
484 | toOffset += bytesUsedHere; |
485 | |
486 | frameOffset += seg->dataHere(); |
487 | index = SegmentQueue::nextIndex(index); |
488 | if (index == fSegments->nextFreeIndex()) break; |
489 | seg = &(fSegments->s[index]); |
490 | } |
491 | |
492 | fSegments->dequeue(); |
493 | |
494 | return True; |
495 | } |
496 | |
497 | |
498 | ////////// Segment ////////// |
499 | |
500 | unsigned Segment::dataHere() { |
501 | int result = frameSize - (headerSize + sideInfoSize); |
502 | if (result < 0) { |
503 | return 0; |
504 | } |
505 | |
506 | return (unsigned)result; |
507 | } |
508 | |
509 | ////////// SegmentQueue ////////// |
510 | |
511 | void SegmentQueue::enqueueNewSegment(FramedSource* inputSource, |
512 | FramedSource* usingSource) { |
513 | if (isFull()) { |
514 | usingSource->envir() << "SegmentQueue::enqueueNewSegment() overflow\n" ; |
515 | usingSource->handleClosure(); |
516 | return; |
517 | } |
518 | |
519 | fUsingSource = usingSource; |
520 | |
521 | Segment& seg = nextFreeSegment(); |
522 | inputSource->getNextFrame(seg.buf, sizeof seg.buf, |
523 | sqAfterGettingSegment, this, |
524 | FramedSource::handleClosure, usingSource); |
525 | } |
526 | |
527 | void SegmentQueue::sqAfterGettingSegment(void* clientData, |
528 | unsigned numBytesRead, |
529 | unsigned /*numTruncatedBytes*/, |
530 | struct timeval presentationTime, |
531 | unsigned durationInMicroseconds) { |
532 | SegmentQueue* segQueue = (SegmentQueue*)clientData; |
533 | Segment& seg = segQueue->nextFreeSegment(); |
534 | |
535 | seg.presentationTime = presentationTime; |
536 | seg.durationInMicroseconds = durationInMicroseconds; |
537 | |
538 | if (segQueue->sqAfterGettingCommon(seg, numBytesRead)) { |
539 | #ifdef DEBUG |
540 | char const* direction = segQueue->fDirectionIsToADU ? "m->a" : "a->m" ; |
541 | fprintf(stderr, "%s:read frame %d<-%d, fs:%d, sis:%d, dh:%d, (descriptor size: %d)\n" , direction, seg.aduSize, seg.backpointer, seg.frameSize, seg.sideInfoSize, seg.dataHere(), seg.descriptorSize); |
542 | #endif |
543 | } |
544 | |
545 | // Continue our original calling source where it left off: |
546 | segQueue->fUsingSource->doGetNextFrame(); |
547 | } |
548 | |
549 | // Common code called after a new segment is enqueued |
550 | Boolean SegmentQueue::sqAfterGettingCommon(Segment& seg, |
551 | unsigned numBytesRead) { |
552 | unsigned char* fromPtr = seg.buf; |
553 | |
554 | if (fIncludeADUdescriptors) { |
555 | // The newly-read data is assumed to be an ADU with a descriptor |
556 | // in front |
557 | (void)ADUdescriptor::getRemainingFrameSize(fromPtr); |
558 | seg.descriptorSize = (unsigned)(fromPtr-seg.buf); |
559 | } else { |
560 | seg.descriptorSize = 0; |
561 | } |
562 | |
563 | // parse the MP3-specific info in the frame to get the ADU params |
564 | unsigned hdr; |
565 | MP3SideInfo sideInfo; |
566 | if (!GetADUInfoFromMP3Frame(fromPtr, numBytesRead, |
567 | hdr, seg.frameSize, |
568 | sideInfo, seg.sideInfoSize, |
569 | seg.backpointer, seg.aduSize)) { |
570 | return False; |
571 | } |
572 | |
573 | // If we've just read an ADU (rather than a regular MP3 frame), then use the |
574 | // entire "numBytesRead" data for the 'aduSize', so that we include any |
575 | // 'ancillary data' that may be present at the end of the ADU: |
576 | if (!fDirectionIsToADU) { |
577 | unsigned newADUSize |
578 | = numBytesRead - seg.descriptorSize - 4/*header size*/ - seg.sideInfoSize; |
579 | if (newADUSize > seg.aduSize) seg.aduSize = newADUSize; |
580 | } |
581 | fTotalDataSize += seg.dataHere(); |
582 | fNextFreeIndex = nextIndex(fNextFreeIndex); |
583 | |
584 | return True; |
585 | } |
586 | |
587 | Boolean SegmentQueue::dequeue() { |
588 | if (isEmpty()) { |
589 | fUsingSource->envir() << "SegmentQueue::dequeue(): underflow!\n" ; |
590 | return False; |
591 | } |
592 | |
593 | Segment& seg = s[headIndex()]; |
594 | fTotalDataSize -= seg.dataHere(); |
595 | fHeadIndex = nextIndex(fHeadIndex); |
596 | return True; |
597 | } |
598 | |
599 | Boolean SegmentQueue::insertDummyBeforeTail(unsigned backpointer) { |
600 | if (isEmptyOrFull()) return False; |
601 | |
602 | // Copy the current tail segment to its new position, then modify the |
603 | // old tail segment to be a 'dummy' ADU |
604 | |
605 | unsigned newTailIndex = nextFreeIndex(); |
606 | Segment& newTailSeg = s[newTailIndex]; |
607 | |
608 | unsigned oldTailIndex = prevIndex(newTailIndex); |
609 | Segment& oldTailSeg = s[oldTailIndex]; |
610 | |
611 | newTailSeg = oldTailSeg; // structure copy |
612 | |
613 | // Begin by setting (replacing) the ADU descriptor of the dummy ADU: |
614 | unsigned char* ptr = oldTailSeg.buf; |
615 | if (fIncludeADUdescriptors) { |
616 | unsigned remainingFrameSize |
617 | = oldTailSeg.headerSize + oldTailSeg.sideInfoSize + 0 /* 0-size ADU */; |
618 | unsigned currentDescriptorSize = oldTailSeg.descriptorSize; |
619 | |
620 | if (currentDescriptorSize == 2) { |
621 | ADUdescriptor::generateTwoByteDescriptor(ptr, remainingFrameSize); |
622 | } else { |
623 | (void)ADUdescriptor::generateDescriptor(ptr, remainingFrameSize); |
624 | } |
625 | } |
626 | |
627 | // Then zero out the side info of the dummy frame: |
628 | if (!ZeroOutMP3SideInfo(ptr, oldTailSeg.frameSize, |
629 | backpointer)) return False; |
630 | |
631 | unsigned dummyNumBytesRead |
632 | = oldTailSeg.descriptorSize + 4/*header size*/ + oldTailSeg.sideInfoSize; |
633 | return sqAfterGettingCommon(oldTailSeg, dummyNumBytesRead); |
634 | } |
635 | |