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