diff options
author | Adam Boot <rotator@gmail.com> | 2006-11-08 22:00:45 +0000 |
---|---|---|
committer | Adam Boot <rotator@gmail.com> | 2006-11-08 22:00:45 +0000 |
commit | c1916afa41f46dc525e9f40c31616c75b4235ca6 (patch) | |
tree | 216420d3e082425131ad5cd7f19e1745624266f9 /apps/codecs/flac.c | |
parent | 0a220b6da6ac800e5969c7d47f5add3cc36fc49b (diff) | |
download | rockbox-c1916afa41f46dc525e9f40c31616c75b4235ca6.tar.gz rockbox-c1916afa41f46dc525e9f40c31616c75b4235ca6.zip |
Sample-accurate seeking for FLAC
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11474 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/codecs/flac.c')
-rw-r--r-- | apps/codecs/flac.c | 295 |
1 files changed, 241 insertions, 54 deletions
diff --git a/apps/codecs/flac.c b/apps/codecs/flac.c index ce54e67643..1e9c30f1a9 100644 --- a/apps/codecs/flac.c +++ b/apps/codecs/flac.c | |||
@@ -46,9 +46,8 @@ int32_t decoded1[MAX_BLOCKSIZE] IBSS_ATTR; | |||
46 | uint64_t offset | 46 | uint64_t offset |
47 | uint32_t blocksize | 47 | uint32_t blocksize |
48 | 48 | ||
49 | We don't store the blocksize (of the target frame) and we also | 49 | We also limit the sample and offset values to 32-bits - Rockbox doesn't |
50 | limit the sample and offset values to 32-bits - Rockbox doesn't support | 50 | support files bigger than 2GB on FAT32 filesystems. |
51 | files bigger than 2GB on FAT32 filesystems. | ||
52 | 51 | ||
53 | The reference FLAC encoder produces a seek table with points every | 52 | The reference FLAC encoder produces a seek table with points every |
54 | 10 seconds, but this can be overridden by the user when encoding a file. | 53 | 10 seconds, but this can be overridden by the user when encoding a file. |
@@ -72,17 +71,22 @@ int32_t decoded1[MAX_BLOCKSIZE] IBSS_ATTR; | |||
72 | struct FLACseekpoints { | 71 | struct FLACseekpoints { |
73 | uint32_t sample; | 72 | uint32_t sample; |
74 | uint32_t offset; | 73 | uint32_t offset; |
74 | uint16_t blocksize; | ||
75 | }; | 75 | }; |
76 | 76 | ||
77 | struct FLACseekpoints seekpoints[MAX_SUPPORTED_SEEKTABLE_SIZE]; | 77 | struct FLACseekpoints seekpoints[MAX_SUPPORTED_SEEKTABLE_SIZE]; |
78 | int nseekpoints; | 78 | int nseekpoints; |
79 | 79 | ||
80 | static int8_t *bit_buffer; | ||
81 | static size_t buff_size; | ||
82 | |||
80 | static bool flac_init(FLACContext* fc, int first_frame_offset) | 83 | static bool flac_init(FLACContext* fc, int first_frame_offset) |
81 | { | 84 | { |
82 | unsigned char buf[255]; | 85 | unsigned char buf[255]; |
83 | bool found_streaminfo=false; | 86 | bool found_streaminfo=false; |
84 | uint32_t seekpoint_hi,seekpoint_lo; | 87 | uint32_t seekpoint_hi,seekpoint_lo; |
85 | uint32_t offset_hi,offset_lo; | 88 | uint32_t offset_hi,offset_lo; |
89 | uint16_t blocksize; | ||
86 | int endofmetadata=0; | 90 | int endofmetadata=0; |
87 | int blocklength; | 91 | int blocklength; |
88 | int n; | 92 | int n; |
@@ -90,6 +94,8 @@ static bool flac_init(FLACContext* fc, int first_frame_offset) | |||
90 | ci->memset(fc,0,sizeof(FLACContext)); | 94 | ci->memset(fc,0,sizeof(FLACContext)); |
91 | nseekpoints=0; | 95 | nseekpoints=0; |
92 | 96 | ||
97 | fc->sample_skip = 0; | ||
98 | |||
93 | /* Skip any foreign tags at start of file */ | 99 | /* Skip any foreign tags at start of file */ |
94 | ci->seek_buffer(first_frame_offset); | 100 | ci->seek_buffer(first_frame_offset); |
95 | 101 | ||
@@ -158,14 +164,14 @@ static bool flac_init(FLACContext* fc, int first_frame_offset) | |||
158 | offset_lo=(buf[12] << 24) | (buf[13] << 16) | | 164 | offset_lo=(buf[12] << 24) | (buf[13] << 16) | |
159 | (buf[14] << 8) | buf[15]; | 165 | (buf[14] << 8) | buf[15]; |
160 | 166 | ||
161 | /* The final two bytes contain the number of samples in the | 167 | blocksize=(buf[16] << 8) | buf[17]; |
162 | target frame - but we don't care about that. */ | ||
163 | 168 | ||
164 | /* Only store seekpoints where the high 32 bits are zero */ | 169 | /* Only store seekpoints where the high 32 bits are zero */ |
165 | if ((seekpoint_hi == 0) && (seekpoint_lo != 0xffffffff) && | 170 | if ((seekpoint_hi == 0) && (seekpoint_lo != 0xffffffff) && |
166 | (offset_hi == 0)) { | 171 | (offset_hi == 0)) { |
167 | seekpoints[nseekpoints].sample=seekpoint_lo; | 172 | seekpoints[nseekpoints].sample=seekpoint_lo; |
168 | seekpoints[nseekpoints].offset=offset_lo; | 173 | seekpoints[nseekpoints].offset=offset_lo; |
174 | seekpoints[nseekpoints].blocksize=blocksize; | ||
169 | nseekpoints++; | 175 | nseekpoints++; |
170 | } | 176 | } |
171 | } | 177 | } |
@@ -186,57 +192,234 @@ static bool flac_init(FLACContext* fc, int first_frame_offset) | |||
186 | } | 192 | } |
187 | } | 193 | } |
188 | 194 | ||
189 | /* A very simple seek implementation - seek to the seekpoint before | 195 | /* Synchronize to next frame in stream - adapted from libFLAC 1.1.3b2 */ |
190 | the target sample. | 196 | bool frame_sync(FLACContext* fc) { |
197 | unsigned int x = 0; | ||
198 | bool cached = false; | ||
191 | 199 | ||
192 | This needs to be improved to seek with greater accuracy | 200 | /* Make sure we're byte aligned. */ |
193 | */ | 201 | align_get_bits(&fc->gb); |
194 | bool flac_seek(FLACContext* fc, uint32_t newsample) { | 202 | |
195 | uint32_t offset; | 203 | while(1) { |
196 | 204 | if(fc->gb.size_in_bits - get_bits_count(&fc->gb) < 8) { | |
197 | if (nseekpoints==0) { | 205 | /* Error, end of bitstream, a valid stream should never reach here |
198 | /* No seekpoints = no seeking */ | 206 | * since the buffer should contain at least one frame header. |
199 | return false; | 207 | */ |
200 | } else { | 208 | return false; |
201 | int i=nseekpoints-1; | 209 | } |
202 | while ((i > 0) && (seekpoints[i].sample > newsample)) { | 210 | |
203 | i--; | 211 | if(cached) |
204 | } | 212 | cached = false; |
205 | 213 | else | |
206 | if ((i==0) && (seekpoints[i].sample > newsample)) { | 214 | x = get_bits(&fc->gb, 8); |
207 | offset=0; | 215 | |
208 | } else { | 216 | if(x == 0xff) { /* MAGIC NUMBER for first 8 frame sync bits. */ |
209 | offset=seekpoints[i].offset; | 217 | x = get_bits(&fc->gb, 8); |
210 | } | 218 | /* We have to check if we just read two 0xff's in a row; the second |
211 | } | 219 | * may actually be the beginning of the sync code. |
212 | 220 | */ | |
213 | return ci->seek_buffer(offset+fc->metadatalength); | 221 | if(x == 0xff) { /* MAGIC NUMBER for first 8 frame sync bits. */ |
222 | cached = true; | ||
223 | } | ||
224 | else if(x >> 2 == 0x3e) { /* MAGIC NUMBER for last 6 sync bits. */ | ||
225 | /* Succesfully synced. */ | ||
226 | break; | ||
227 | } | ||
228 | } | ||
229 | } | ||
230 | |||
231 | /* Advance and init bit buffer to the new frame. */ | ||
232 | ci->advance_buffer((get_bits_count(&fc->gb)-16)>>3); /* consumed bytes */ | ||
233 | bit_buffer = ci->request_buffer(&buff_size, MAX_FRAMESIZE); | ||
234 | init_get_bits(&fc->gb, bit_buffer, buff_size*8); | ||
235 | |||
236 | /* Decode the frame to verify the frame crc and | ||
237 | * fill fc with its metadata. | ||
238 | */ | ||
239 | if(flac_decode_frame(fc, decoded0, decoded1, | ||
240 | bit_buffer, buff_size, ci->yield) < 0) { | ||
241 | return false; | ||
242 | } | ||
243 | |||
244 | return true; | ||
214 | } | 245 | } |
215 | 246 | ||
216 | /* A very simple seek implementation - seek to the seekpoint before | 247 | /* Seek to sample - adapted from libFLAC 1.1.3b2+ */ |
217 | the target offset. | 248 | bool flac_seek(FLACContext* fc, uint32_t target_sample) { |
249 | off_t orig_pos = ci->curpos; | ||
250 | off_t pos = -1; | ||
251 | unsigned long lower_bound, upper_bound; | ||
252 | unsigned long lower_bound_sample, upper_bound_sample; | ||
253 | int i; | ||
254 | unsigned approx_bytes_per_frame; | ||
255 | uint32_t this_frame_sample = fc->samplenumber; | ||
256 | unsigned this_block_size = fc->blocksize; | ||
257 | bool needs_seek = true, first_seek = true; | ||
258 | |||
259 | /* We are just guessing here. */ | ||
260 | if(fc->max_framesize > 0) | ||
261 | approx_bytes_per_frame = (fc->max_framesize + fc->min_framesize)/2 + 1; | ||
262 | /* Check if it's a known fixed-blocksize stream. */ | ||
263 | else if(fc->min_blocksize == fc->max_blocksize && fc->min_blocksize > 0) | ||
264 | approx_bytes_per_frame = fc->min_blocksize*fc->channels*fc->bps/8 + 64; | ||
265 | else | ||
266 | approx_bytes_per_frame = 4608 * fc->channels * fc->bps/8 + 64; | ||
267 | |||
268 | /* Set an upper and lower bound on where in the stream we will search. */ | ||
269 | lower_bound = fc->metadatalength; | ||
270 | lower_bound_sample = 0; | ||
271 | upper_bound = fc->filesize; | ||
272 | upper_bound_sample = fc->totalsamples>0 ? fc->totalsamples : target_sample; | ||
273 | |||
274 | /* Refine the bounds if we have a seektable with suitable points. */ | ||
275 | if(nseekpoints > 0) { | ||
276 | /* Find the closest seek point <= target_sample, if it exists. */ | ||
277 | for(i = nseekpoints-1; i >= 0; i--) { | ||
278 | if(seekpoints[i].sample <= target_sample) | ||
279 | break; | ||
280 | } | ||
281 | if(i >= 0) { /* i.e. we found a suitable seek point... */ | ||
282 | lower_bound = fc->metadatalength + seekpoints[i].offset; | ||
283 | lower_bound_sample = seekpoints[i].sample; | ||
284 | } | ||
218 | 285 | ||
219 | This needs to be improved to seek with greater accuracy | 286 | /* Find the closest seek point > target_sample, if it exists. */ |
220 | */ | 287 | for(i = 0; i < nseekpoints; i++) { |
288 | if(seekpoints[i].sample > target_sample) | ||
289 | break; | ||
290 | } | ||
291 | if(i < nseekpoints) { /* i.e. we found a suitable seek point... */ | ||
292 | upper_bound = fc->metadatalength + seekpoints[i].offset; | ||
293 | upper_bound_sample = seekpoints[i].sample; | ||
294 | } | ||
295 | } | ||
296 | |||
297 | while(1) { | ||
298 | /* Check if bounds are still ok. */ | ||
299 | if(lower_bound_sample >= upper_bound_sample || | ||
300 | lower_bound > upper_bound) { | ||
301 | return false; | ||
302 | } | ||
303 | |||
304 | /* Calculate new seek position */ | ||
305 | if(needs_seek) { | ||
306 | pos = (off_t)(lower_bound + | ||
307 | (((target_sample - lower_bound_sample) * | ||
308 | (int64_t)(upper_bound - lower_bound)) / | ||
309 | (upper_bound_sample - lower_bound_sample)) - | ||
310 | approx_bytes_per_frame); | ||
311 | |||
312 | if(pos >= (off_t)upper_bound) | ||
313 | pos = (off_t)upper_bound-1; | ||
314 | if(pos < (off_t)lower_bound) | ||
315 | pos = (off_t)lower_bound; | ||
316 | } | ||
317 | |||
318 | if(!ci->seek_buffer(pos)) | ||
319 | return false; | ||
320 | |||
321 | bit_buffer = ci->request_buffer(&buff_size, MAX_FRAMESIZE); | ||
322 | init_get_bits(&fc->gb, bit_buffer, buff_size*8); | ||
323 | |||
324 | /* Now we need to get a frame. It is possible for our seek | ||
325 | * to land in the middle of audio data that looks exactly like | ||
326 | * a frame header from a future version of an encoder. When | ||
327 | * that happens, frame_sync() will return false. | ||
328 | * But there is a remote possibility that it is properly | ||
329 | * synced at such a "future-codec frame", so to make sure, | ||
330 | * we wait to see several "unparseable" errors in a row before | ||
331 | * bailing out. | ||
332 | */ | ||
333 | { | ||
334 | unsigned unparseable_count; | ||
335 | bool got_a_frame = false; | ||
336 | for(unparseable_count = 0; !got_a_frame | ||
337 | && unparseable_count < 10; unparseable_count++) { | ||
338 | if(frame_sync(fc)) | ||
339 | got_a_frame = true; | ||
340 | } | ||
341 | if(!got_a_frame) { | ||
342 | ci->seek_buffer(orig_pos); | ||
343 | return false; | ||
344 | } | ||
345 | } | ||
346 | |||
347 | this_frame_sample = fc->samplenumber; | ||
348 | this_block_size = fc->blocksize; | ||
349 | |||
350 | if(target_sample >= this_frame_sample | ||
351 | && target_sample < this_frame_sample+this_block_size) { | ||
352 | /* Found the frame containing the target sample. */ | ||
353 | fc->sample_skip = target_sample - this_frame_sample; | ||
354 | break; | ||
355 | } | ||
356 | |||
357 | if(this_frame_sample + this_block_size >= upper_bound_sample && | ||
358 | !first_seek) { | ||
359 | if(pos == (off_t)lower_bound || !needs_seek) { | ||
360 | ci->seek_buffer(orig_pos); | ||
361 | return false; | ||
362 | } | ||
363 | /* Our last move backwards wasn't big enough, try again. */ | ||
364 | approx_bytes_per_frame *= 2; | ||
365 | continue; | ||
366 | } | ||
367 | /* Allow one seek over upper bound, | ||
368 | * required for streams with unknown total samples. | ||
369 | */ | ||
370 | first_seek = false; | ||
371 | |||
372 | /* Make sure we are not seeking in a corrupted stream */ | ||
373 | if(this_frame_sample < lower_bound_sample) { | ||
374 | ci->seek_buffer(orig_pos); | ||
375 | return false; | ||
376 | } | ||
377 | |||
378 | approx_bytes_per_frame = this_block_size*fc->channels*fc->bps/8 + 64; | ||
379 | |||
380 | /* We need to narrow the search. */ | ||
381 | if(target_sample < this_frame_sample) { | ||
382 | upper_bound_sample = this_frame_sample + this_block_size; | ||
383 | upper_bound = ci->curpos; | ||
384 | } | ||
385 | else { /* Target is beyond this frame. */ | ||
386 | /* We are close, continue in decoding next frames. */ | ||
387 | if(target_sample < this_frame_sample + 4*this_block_size) { | ||
388 | pos = ci->curpos + fc->framesize; | ||
389 | needs_seek = false; | ||
390 | } | ||
391 | |||
392 | lower_bound_sample = this_frame_sample + this_block_size; | ||
393 | lower_bound = ci->curpos + fc->framesize; | ||
394 | } | ||
395 | } | ||
396 | |||
397 | return true; | ||
398 | } | ||
399 | |||
400 | /* Seek to file offset */ | ||
221 | bool flac_seek_offset(FLACContext* fc, uint32_t offset) { | 401 | bool flac_seek_offset(FLACContext* fc, uint32_t offset) { |
222 | if (nseekpoints==0) { | 402 | unsigned unparseable_count; |
223 | /* No seekpoints = no seeking */ | 403 | bool got_a_frame = false; |
224 | return false; | 404 | |
225 | } else { | 405 | if(!ci->seek_buffer(offset)) |
226 | offset-=fc->metadatalength; | 406 | return false; |
227 | int i=nseekpoints-1; | 407 | |
228 | while ((i > 0) && (seekpoints[i].offset > offset)) { | 408 | bit_buffer = ci->request_buffer(&buff_size, MAX_FRAMESIZE); |
229 | i--; | 409 | init_get_bits(&fc->gb, bit_buffer, buff_size*8); |
230 | } | 410 | |
231 | 411 | for(unparseable_count = 0; !got_a_frame | |
232 | if ((i==0) && (seekpoints[i].offset > offset)) { | 412 | && unparseable_count < 10; unparseable_count++) { |
233 | offset=0; | 413 | if(frame_sync(fc)) |
234 | } else { | 414 | got_a_frame = true; |
235 | offset=seekpoints[i].offset; | 415 | } |
236 | } | 416 | |
237 | } | 417 | if(!got_a_frame) { |
238 | 418 | ci->seek_buffer(fc->metadatalength); | |
239 | return ci->seek_buffer(offset+fc->metadatalength); | 419 | return false; |
420 | } | ||
421 | |||
422 | return true; | ||
240 | } | 423 | } |
241 | 424 | ||
242 | /* this is the codec entry point */ | 425 | /* this is the codec entry point */ |
@@ -306,7 +489,8 @@ enum codec_status codec_start(struct codec_api* api) | |||
306 | 489 | ||
307 | /* Deal with any pending seek requests */ | 490 | /* Deal with any pending seek requests */ |
308 | if (ci->seek_time) { | 491 | if (ci->seek_time) { |
309 | if (flac_seek(&fc,((ci->seek_time-1)/20)*(ci->id3->frequency/50))) { | 492 | if (flac_seek(&fc,(uint32_t)(((uint64_t)(ci->seek_time-1) |
493 | *ci->id3->frequency)/1000))) { | ||
310 | /* Refill the input buffer */ | 494 | /* Refill the input buffer */ |
311 | buf = ci->request_buffer(&bytesleft, MAX_FRAMESIZE); | 495 | buf = ci->request_buffer(&bytesleft, MAX_FRAMESIZE); |
312 | } | 496 | } |
@@ -323,10 +507,13 @@ enum codec_status codec_start(struct codec_api* api) | |||
323 | frame++; | 507 | frame++; |
324 | 508 | ||
325 | ci->yield(); | 509 | ci->yield(); |
326 | while(!ci->pcmbuf_insert_split((char*)decoded0,(char*)decoded1, | 510 | while(!ci->pcmbuf_insert_split((char*)&decoded0[fc.sample_skip], |
327 | fc.blocksize*4)) { | 511 | (char*)&decoded1[fc.sample_skip], |
512 | (fc.blocksize-fc.sample_skip)*4)) { | ||
328 | ci->yield(); | 513 | ci->yield(); |
329 | } | 514 | } |
515 | |||
516 | fc.sample_skip = 0; | ||
330 | 517 | ||
331 | /* Update the elapsed-time indicator */ | 518 | /* Update the elapsed-time indicator */ |
332 | samplesdone=fc.samplenumber+fc.blocksize; | 519 | samplesdone=fc.samplenumber+fc.blocksize; |