1 /**
2     \file ADM_audioStream
3     \brief Base class
4 
5 (C) Mean 2008
6 GPL-v2
7 */
8 #include "ADM_default.h"
9 #include "ADM_audioStreamMP3.h"
10 #include "ADM_mp3info.h"
11 #include "DIA_working.h"
12 #include "ADM_clock.h"
13 #include "ADM_vidMisc.h"
14 
15 #if 1
16 #define aprintf(...) {}
17 #else
18 #define aprintf printf
19 #endif
20 /**
21     \fn ADM_audioStreamMP3
22     \brief constructor
23 */
ADM_audioStreamMP3(WAVHeader * header,ADM_audioAccess * access,bool createMap)24 ADM_audioStreamMP3::ADM_audioStreamMP3(WAVHeader *header,ADM_audioAccess *access,bool createMap) : ADM_audioStreamBuffered(header,access)
25 {
26     // Suppress repeated debug messages
27     _msg_counter=0;
28     _msg_ratelimit=new ADMCountdown(200);
29     _msg_ratelimit->reset();
30     // If hinted..., compute the duration ourselves
31     if(access->isCBR()==true && access->canSeekOffset()==true)
32     {
33         // We can compute the duration from the length
34         double size=access->getLength();
35         size/=header->byterate; // Result is in second
36         size*=1000;
37         size*=1000; // s->us
38         durationInUs=(uint64_t)size;
39         return;
40     }
41     // and built vbr map if needed
42     // The 2 conditions below means there is no gap i.e. avi style stream
43     // else not needed
44     if( access->canSeekTime()==false)
45     {
46         ADM_assert(access->canSeekOffset()==true);
47         if(true==createMap)
48         {
49             buildTimeMap();
50             uint32_t nb=seekPoints.size();
51             if(nb)
52                 durationInUs=seekPoints[nb-1]->timeStamp;
53             return;
54         }
55     }
56     // Time based
57     durationInUs=access->getDurationInUs();
58 }
59 
60 /**
61     \fn ADM_audioStream
62     \brief destructor
63 */
~ADM_audioStreamMP3()64 ADM_audioStreamMP3::~ADM_audioStreamMP3()
65 {
66     // Delete our map if needed...
67    for(int i=0;i<seekPoints.size();i++)
68    {
69         delete seekPoints[i];
70         seekPoints[i]=NULL;
71     }
72     if(_msg_ratelimit)
73         delete _msg_ratelimit;
74     _msg_ratelimit = NULL;
75 }
76 /**
77     \fn goToTime
78     \brief goToTime
79 */
goToTime(uint64_t nbUs)80 bool         ADM_audioStreamMP3::goToTime(uint64_t nbUs)
81 {
82     if(access->canSeekTime()==true)
83     {
84         if( access->goToTime(nbUs)==true)
85         {
86            setDts(nbUs);
87            limit=start=0;
88            refill();
89            return 1;
90         }
91         return 1;
92     }
93     // If CBR we can use the default way
94     if(access->isCBR()==true)
95         return ADM_audioStream::goToTime(nbUs);
96     // if VBR use our time map
97     if(!seekPoints.size())
98     {
99         ADM_error("VBR MP2/MP3 stream with no time map, cannot seek\n");
100         return false;
101     }
102     if(nbUs<=seekPoints[0]->timeStamp) // too early
103     {
104             start=limit=0;
105             access->setPos(0);
106             setDts(0);
107             return true;
108     }
109     // Search the switching point..
110     for(int i=0;i<seekPoints.size()-1;i++)
111     {
112         //printf("[%d]Target %u * %u * %u *\n",i,nbUs,seekPoints[i]->timeStamp,seekPoints[i+1]->timeStamp);
113         if(seekPoints[i]->timeStamp<=nbUs && seekPoints[i+1]->timeStamp>=nbUs)
114         {
115             start=limit=0;
116             access->setPos(seekPoints[i]->offset);
117             setDts(seekPoints[i]->timeStamp);
118             ADM_info("MP3 : Time map : Seek request for %s\n",ADM_us2plain(nbUs));
119             ADM_info("MP3 : Sync found at %s\n",ADM_us2plain(seekPoints[i]->timeStamp));
120             return true;
121         }
122     }
123     ADM_error("VBR MP2/MP3 request for time outside of time map, cannot seek\n");
124     return false;
125 }
126 /**
127         \fn getPacket
128 */
getPacket(uint8_t * buffer,uint32_t * size,uint32_t sizeMax,uint32_t * nbSample,uint64_t * dts)129 uint8_t ADM_audioStreamMP3::getPacket(uint8_t *buffer,uint32_t *size, uint32_t sizeMax,uint32_t *nbSample,uint64_t *dts)
130 {
131 #define ADM_LOOK_AHEAD 4 // Need 4 bytes...
132 uint8_t data[ADM_LOOK_AHEAD];
133 MpegAudioInfo info;
134 uint32_t offset;
135 int nbSyncBytes=0;
136     while(1)
137     {
138         // Do we have enough ? Refill if needed ?
139         if(needBytes(ADM_LOOK_AHEAD)==false)
140         {
141             if(_msg_ratelimit->done())
142             {
143                 if(_msg_counter)
144                 {
145                     ADM_warning("MP3: Not enough data to lookup header (message repeated %" PRIu32" times)\n",_msg_counter);
146                     _msg_counter=0;
147                 }else
148                 {
149                     ADM_warning("MP3: Not enough data to lookup header\n");
150                 }
151                 _msg_ratelimit->reset();
152             }else
153             {
154                 _msg_counter++;
155             }
156             return 0;
157         }
158         // Peek
159         peek(ADM_LOOK_AHEAD,data);
160         if(getMpegFrameInfo(data,ADM_LOOK_AHEAD, &info,NULL,&offset))
161         {
162             ADM_assert(info.size<=sizeMax);
163             if(needBytes(info.size)==true)
164             {
165                 *size=info.size;
166                 read(*size,buffer);
167                 *nbSample=info.samples;
168                 //if(info.samples!=1152) ADM_assert(0);
169                 *dts=lastDts;
170             //    printf("MP3 DTS =%"PRId64" ->",*dts);
171                 advanceDtsBySample(*nbSample);
172                 //printf("%"PRId64" , size=%d\n",*dts,*size);
173                 if(nbSyncBytes)
174                         ADM_info("[MP3 Stream] Sync found after %d bytes...\n",nbSyncBytes);
175                 _msg_counter=0;
176                 return 1;
177             }
178 
179         }
180         //discard one byte
181         nbSyncBytes++;
182 
183         read8();
184     }
185 }
186  /**
187       \fn buildTimeMap
188      \brief compute a map between time<->Offset. It is only used for stream in Offset mode with no gap (avi like)
189             In that case, the incoming dts is irrelevant.
190             We may have up to one packet error
191 
192   */
193 #define TIME_BETWEEN_UPDATE 1500
194 #define SAVE_EVERY_N_BLOCKS 3    // One seek point every ~ 60 ms
buildTimeMap(void)195 bool ADM_audioStreamMP3::buildTimeMap(void)
196 {
197 uint32_t size;
198 uint64_t newDts,pos;
199 DIA_workingBase *work=createWorking(QT_TRANSLATE_NOOP("adm","Building time map"));
200 
201     ADM_assert(access->canSeekOffset()==true);
202     access->setPos(0);
203     ADM_info("Starting MP3 time map\n");
204     rewind();
205     Clock *clk=new Clock();
206     clk->reset();
207     uint32_t nextUpdate=clk->getElapsedMS()+TIME_BETWEEN_UPDATE;
208     int markCounter=SAVE_EVERY_N_BLOCKS;
209     while(1)
210     {
211         // Push where we are...
212 
213         if(markCounter>SAVE_EVERY_N_BLOCKS)
214         {
215             MP3_seekPoint *seek=new MP3_seekPoint;
216             seek->offset=access->getPos();
217             seek->timeStamp=lastDts;
218             // Mark this point
219             seekPoints.append(seek);
220             markCounter=0;
221         }
222         // Shrink ?
223         if(limit>ADM_AUDIOSTREAM_BUFFER_SIZE && start> 10*1024)
224         {
225             memmove(buffer.at(0), buffer.at(start),limit-start);
226             limit-=start;
227             start=0;
228         }
229 
230         if(false==access->getPacket(buffer.at(limit), &size, 2*ADM_AUDIOSTREAM_BUFFER_SIZE-limit,&newDts))
231         {
232             aprintf("Get packet failed\n");
233             break;
234         }
235         aprintf("Got MP3 packet : size =%d\n",(int)size);
236         limit+=size;
237         // Start at...
238         pos=access->getPos();
239         uint32_t now=clk->getElapsedMS();
240         if(now>nextUpdate)
241         {
242             work->update(pos,access->getLength());
243             nextUpdate=now+TIME_BETWEEN_UPDATE;
244         }
245 
246         // consume all packets in the buffer we just got
247         MpegAudioInfo info;
248         uint32_t offset;
249 
250         while(1)
251         {
252             if(limit-start<ADM_LOOK_AHEAD) break;
253             if(!getMpegFrameInfo(buffer.at(start),ADM_LOOK_AHEAD, &info,NULL,&offset))
254             {
255                 start++;
256                 continue;
257             }
258             // Enough bytes ?
259             if(limit-start>=info.size)
260             {
261                 start+=info.size;
262                 advanceDtsBySample(info.samples);
263                 markCounter++;
264                 continue;
265             }
266             break;
267         }
268 
269     }
270     rewind();
271     delete work;
272     delete clk;
273     access->setPos(0);
274     ADM_info("Finishing MP3 time map\n");
275     return true;
276 }
277 
278   // EOF
279 
280