xref: /haiku/src/apps/mediaplayer/supplier/MediaTrackAudioSupplier.cpp (revision 7749d0bb0c358a3279b1b9cc76d8376e900130a5)
1 /*
2  * Copyright © 2000-2004 Ingo Weinhold <ingo_weinhold@gmx.de>
3  * Copyright © 2006-2008 Stephan Aßmus <superstippi@gmx.de>
4  * All rights reserved. Distributed under the terms of the MIT License.
5  */
6 #include "MediaTrackAudioSupplier.h"
7 
8 #include <new>
9 #include <algorithm>
10 #include <stdio.h>
11 #include <string.h>
12 
13 #include <MediaFile.h>
14 #include <MediaTrack.h>
15 
16 using namespace std;
17 
18 //#define TRACE_AUDIO_SUPPLIER
19 #ifdef TRACE_AUDIO_SUPPLIER
20 # define TRACE(x...)	printf("MediaTrackAudioSupplier::" x)
21 #else
22 # define TRACE(x...)
23 #endif
24 
25 
26 // #pragma mark - Buffer
27 
28 
29 struct MediaTrackAudioSupplier::Buffer {
30 			void*				data;
31 			int64				offset;
32 			int64				size;
33 			bigtime_t			time_stamp;
34 
35 	static	int					CompareOffset(const void* a, const void* b);
36 };
37 
38 
39 int
40 MediaTrackAudioSupplier::Buffer::CompareOffset(const void* a, const void* b)
41 {
42 	const Buffer* buffer1 = *(const Buffer**)a;
43 	const Buffer* buffer2 = *(const Buffer**)b;
44 	int result = 0;
45 	if (buffer1->offset < buffer2->offset)
46 		result = -1;
47 	else if (buffer1->offset > buffer2->offset)
48 		result = 1;
49 	return result;
50 }
51 
52 
53 // #pragma mark - MediaTrackAudioSupplier
54 
55 
56 MediaTrackAudioSupplier::MediaTrackAudioSupplier(BMediaTrack* mediaTrack,
57 		int32 trackIndex)
58 	:
59 	AudioTrackSupplier(),
60 	fMediaTrack(mediaTrack),
61 	fBuffer(NULL),
62 	fBufferOffset(0),
63 	fBufferSize(0),
64 	fBuffers(10),
65 	fHasKeyFrames(false),
66 	fCountFrames(0),
67 	fReportSeekError(true),
68 	fTrackIndex(trackIndex)
69 {
70 	_InitFromTrack();
71 }
72 
73 
74 MediaTrackAudioSupplier::~MediaTrackAudioSupplier()
75 {
76 	_FreeBuffers();
77 	delete[] fBuffer;
78 }
79 
80 
81 const media_format&
82 MediaTrackAudioSupplier::Format() const
83 {
84 	return AudioReader::Format();
85 }
86 
87 
88 status_t
89 MediaTrackAudioSupplier::GetEncodedFormat(media_format* format) const
90 {
91 	if (!fMediaTrack)
92 		return B_NO_INIT;
93 	return fMediaTrack->EncodedFormat(format);
94 }
95 
96 
97 status_t
98 MediaTrackAudioSupplier::GetCodecInfo(media_codec_info* info) const
99 {
100 	if (!fMediaTrack)
101 		return B_NO_INIT;
102 	return fMediaTrack->GetCodecInfo(info);
103 }
104 
105 
106 bigtime_t
107 MediaTrackAudioSupplier::Duration() const
108 {
109 	if (!fMediaTrack)
110 		return 0;
111 
112 	return fMediaTrack->Duration();
113 }
114 
115 
116 // #pragma mark - AudioReader
117 
118 
119 bigtime_t
120 MediaTrackAudioSupplier::InitialLatency() const
121 {
122 	// TODO: this is just a wild guess, and not really founded on anything.
123 	return 100000;
124 }
125 
126 
127 status_t
128 MediaTrackAudioSupplier::Read(void* buffer, int64 pos, int64 frames)
129 {
130 	TRACE("Read(%p, %lld, %lld)\n", buffer, pos,
131 		frames);
132 	TRACE("  this: %p, fOutOffset: %lld\n", this, fOutOffset);
133 
134 //printf("MediaTrackAudioSupplier::Read(%p, %lld, %lld)\n", buffer, pos, frames);
135 
136 	status_t error = InitCheck();
137 	if (error != B_OK) {
138 		TRACE("Read() InitCheck failed\n");
139 		return error;
140 	}
141 
142 	// convert pos according to our offset
143 	pos += fOutOffset;
144 	// Fill the frames after the end of the track with silence.
145 	if (fCountFrames > 0 && pos + frames > fCountFrames) {
146 		int64 size = max(0LL, fCountFrames - pos);
147 		ReadSilence(SkipFrames(buffer, size), frames - size);
148 		frames = size;
149 	}
150 
151 	TRACE("  after eliminating the frames after the track end: %p, %lld, %lld\n",
152 		buffer, pos, frames);
153 
154 #if 0
155 	const media_format& format = Format();
156 	int64 size = format.u.raw_audio.buffer_size;
157 	uint32 bytesPerFrame = format.u.raw_audio.channel_count
158 		* (format.u.raw_audio.format
159 			& media_raw_audio_format::B_AUDIO_SIZE_MASK);
160 	uint32 framesPerBuffer = size / bytesPerFrame;
161 
162 	if (fMediaTrack->CurrentFrame() != pos) {
163 printf("  needing to seek: %lld (%lld)\n", pos,
164 	fMediaTrack->CurrentFrame());
165 
166 		int64 keyFrame = pos;
167 		error = fMediaTrack->FindKeyFrameForFrame(&keyFrame,
168 			B_MEDIA_SEEK_CLOSEST_BACKWARD);
169 		if (error == B_OK) {
170 			error = fMediaTrack->SeekToFrame(&keyFrame,
171 				B_MEDIA_SEEK_CLOSEST_BACKWARD);
172 		}
173 		if (error != B_OK) {
174 printf("  error seeking to position: %lld (%lld)\n", pos,
175 	fMediaTrack->CurrentFrame());
176 
177 			return error;
178 		}
179 
180 		if (keyFrame < pos) {
181 printf("  need to skip %lld frames\n", pos - keyFrame);
182 			uint8 dummyBuffer[size];
183 			while (pos - keyFrame >= framesPerBuffer) {
184 printf("  skipped %lu frames (full buffer)\n", framesPerBuffer);
185 				int64 sizeToRead = size;
186 				fMediaTrack->ReadFrames(dummyBuffer, &sizeToRead);
187 				keyFrame += framesPerBuffer;
188 			}
189 			int64 restSize = pos - keyFrame;
190 			if (restSize > 0) {
191 printf("  skipped %lu frames (rest)\n", framesPerBuffer);
192 				fMediaTrack->ReadFrames(dummyBuffer, &restSize);
193 			}
194 		}
195 	}
196 	while (frames > 0) {
197 printf("  reading %lu frames (full buffer)\n", framesPerBuffer);
198 		int64 sizeToRead = min_c(size, frames * bytesPerFrame);
199 		fMediaTrack->ReadFrames(buffer, &sizeToRead);
200 		buffer = (uint8*)buffer + sizeToRead;
201 		frames -= framesPerBuffer;
202 	}
203 printf("  done\n\n");
204 
205 #else
206 	// read the cached frames
207 	bigtime_t time = system_time();
208 	if (frames > 0)
209 		_ReadCachedFrames(buffer, pos, frames, time);
210 
211 	TRACE("  after reading from cache: %p, %lld, %lld\n", buffer, pos, frames);
212 
213 	// read the remaining (uncached) frames
214 	if (frames > 0)
215 		_ReadUncachedFrames(buffer, pos, frames, time);
216 
217 #endif
218 	TRACE("Read() done\n");
219 
220 	return B_OK;
221 }
222 
223 // InitCheck
224 status_t
225 MediaTrackAudioSupplier::InitCheck() const
226 {
227 	status_t error = AudioReader::InitCheck();
228 	if (error == B_OK && (!fMediaTrack || !fBuffer))
229 		error = B_NO_INIT;
230 	return error;
231 }
232 
233 // #pragma mark -
234 
235 // _InitFromTrack
236 void
237 MediaTrackAudioSupplier::_InitFromTrack()
238 {
239 	TRACE("_InitFromTrack()\n");
240 	// Try to suggest a big buffer size, we do a lot of caching...
241 	fFormat.u.raw_audio.buffer_size = 16384;
242 	if (fMediaTrack == NULL || fMediaTrack->DecodedFormat(&fFormat) != B_OK
243 		|| fFormat.type != B_MEDIA_RAW_AUDIO) {
244 		fMediaTrack = NULL;
245 		return;
246 	}
247 
248 	#ifdef TRACE_AUDIO_SUPPLIER
249 		char formatString[256];
250 		string_for_format(fFormat, formatString, 256);
251 		TRACE("_InitFromTrack(): format is: %s\n", formatString);
252 		TRACE("_InitFromTrack(): buffer size: %ld\n",
253 			fFormat.u.raw_audio.buffer_size);
254 	#endif
255 
256 	fBuffer = new (nothrow) char[fFormat.u.raw_audio.buffer_size];
257 	_AllocateBuffers();
258 
259 	// Find out, if the track has key frames: as a heuristic we
260 	// check, if the first and the second frame have the same backward
261 	// key frame.
262 	// Note: It shouldn't harm that much, if we're wrong and the
263 	// track has key frame although we found out that it has not.
264 #if 0
265 	int64 keyFrame0 = 0;
266 	int64 keyFrame1 = 1;
267 	fMediaTrack->FindKeyFrameForFrame(&keyFrame0,
268 		B_MEDIA_SEEK_CLOSEST_BACKWARD);
269 	fMediaTrack->FindKeyFrameForFrame(&keyFrame1,
270 		B_MEDIA_SEEK_CLOSEST_BACKWARD);
271 	fHasKeyFrames = (keyFrame0 == keyFrame1);
272 #else
273 	fHasKeyFrames = true;
274 #endif
275 
276 	// get the length of the track
277 	fCountFrames = fMediaTrack->CountFrames();
278 
279 	TRACE("_InitFromTrack(): keyframes: %d, frame count: %lld\n",
280 		fHasKeyFrames, fCountFrames);
281 	printf("_InitFromTrack(): keyframes: %d, frame count: %lld\n",
282 		fHasKeyFrames, fCountFrames);
283 }
284 
285 // _FramesPerBuffer
286 int64
287 MediaTrackAudioSupplier::_FramesPerBuffer() const
288 {
289 	int64 sampleSize = fFormat.u.raw_audio.format
290 		& media_raw_audio_format::B_AUDIO_SIZE_MASK;
291 	int64 frameSize = sampleSize * fFormat.u.raw_audio.channel_count;
292 	return fFormat.u.raw_audio.buffer_size / frameSize;
293 }
294 
295 // _CopyFrames
296 //
297 // Given two buffers starting at different frame offsets, this function
298 // copies /frames/ frames at position /position/ from the source to the
299 // target buffer.
300 // Note that no range checking is done.
301 void
302 MediaTrackAudioSupplier::_CopyFrames(void* source, int64 sourceOffset,
303 							  void* target, int64 targetOffset,
304 							  int64 position, int64 frames) const
305 {
306 	int64 sampleSize = fFormat.u.raw_audio.format
307 					   & media_raw_audio_format::B_AUDIO_SIZE_MASK;
308 	int64 frameSize = sampleSize * fFormat.u.raw_audio.channel_count;
309 	source = (char*)source + frameSize * (position - sourceOffset);
310 	target = (char*)target + frameSize * (position - targetOffset);
311 	memcpy(target, source, frames * frameSize);
312 }
313 
314 // _CopyFrames
315 //
316 // Given two buffers starting at different frame offsets, this function
317 // copies /frames/ frames at position /position/ from the source to the
318 // target buffer. This version expects a cache buffer as source.
319 // Note that no range checking is done.
320 void
321 MediaTrackAudioSupplier::_CopyFrames(Buffer* buffer,
322 							  void* target, int64 targetOffset,
323 							  int64 position, int64 frames) const
324 {
325 	_CopyFrames(buffer->data, buffer->offset, target, targetOffset, position,
326 				frames);
327 }
328 
329 // _AllocateBuffers
330 //
331 // Allocates a set of buffers.
332 void
333 MediaTrackAudioSupplier::_AllocateBuffers()
334 {
335 	int32 count = 10;
336 	_FreeBuffers();
337 	int32 bufferSize = fFormat.u.raw_audio.buffer_size;
338 	char* data = new (nothrow) char[bufferSize * count];
339 	for (; count > 0; count--) {
340 		Buffer* buffer = new (nothrow) Buffer;
341 		if (!buffer || !fBuffers.AddItem(buffer)) {
342 			delete buffer;
343 			if (fBuffers.CountItems() == 0)
344 				delete[] data;
345 			return;
346 		}
347 		buffer->data = data;
348 		data += bufferSize;
349 		buffer->offset = 0;
350 		buffer->size = 0;
351 		buffer->time_stamp = 0;
352 	}
353 }
354 
355 // _FreeBuffers
356 //
357 // Frees the allocated buffers.
358 void
359 MediaTrackAudioSupplier::_FreeBuffers()
360 {
361 	if (fBuffers.CountItems() > 0) {
362 		delete[] (char*)_BufferAt(0)->data;
363 		for (int32 i = 0; Buffer* buffer = _BufferAt(i); i++)
364 			delete buffer;
365 		fBuffers.MakeEmpty();
366 	}
367 }
368 
369 // _BufferAt
370 //
371 // Returns the buffer at index /index/.
372 MediaTrackAudioSupplier::Buffer*
373 MediaTrackAudioSupplier::_BufferAt(int32 index) const
374 {
375 	return (Buffer*)fBuffers.ItemAt(index);
376 }
377 
378 // _FindBufferAtFrame
379 //
380 // If any buffer starts at offset /frame/, it is returned, NULL otherwise.
381 MediaTrackAudioSupplier::Buffer*
382 MediaTrackAudioSupplier::_FindBufferAtFrame(int64 frame) const
383 {
384 	Buffer* buffer = NULL;
385 	for (int32 i = 0;
386 		 ((buffer = _BufferAt(i))) && buffer->offset != frame;
387 		 i++);
388 	return buffer;
389 }
390 
391 // _FindUnusedBuffer
392 //
393 // Returns the first unused buffer or NULL if all buffers are used.
394 MediaTrackAudioSupplier::Buffer*
395 MediaTrackAudioSupplier::_FindUnusedBuffer() const
396 {
397 	Buffer* buffer = NULL;
398 	for (int32 i = 0; ((buffer = _BufferAt(i))) && buffer->size != 0; i++)
399 		;
400 	return buffer;
401 }
402 
403 // _FindUsableBuffer
404 //
405 // Returns either an unused buffer or, if all buffers are used, the least
406 // recently used buffer.
407 // In every case a buffer is returned.
408 MediaTrackAudioSupplier::Buffer*
409 MediaTrackAudioSupplier::_FindUsableBuffer() const
410 {
411 	Buffer* result = _FindUnusedBuffer();
412 	if (!result) {
413 		// find the least recently used buffer.
414 		result = _BufferAt(0);
415 		for (int32 i = 1; Buffer* buffer = _BufferAt(i); i++) {
416 			if (buffer->time_stamp < result->time_stamp)
417 				result = buffer;
418 		}
419 	}
420 	return result;
421 }
422 
423 // _FindUsableBufferFor
424 //
425 // In case there already exists a buffer that starts at position this
426 // one is returned. Otherwise the function returns either an unused
427 // buffer or, if all buffers are used, the least recently used buffer.
428 // In every case a buffer is returned.
429 MediaTrackAudioSupplier::Buffer*
430 MediaTrackAudioSupplier::_FindUsableBufferFor(int64 position) const
431 {
432 	Buffer* buffer = _FindBufferAtFrame(position);
433 	if (buffer == NULL)
434 		buffer = _FindUsableBuffer();
435 	return buffer;
436 }
437 
438 // _GetBuffersFor
439 //
440 // Adds pointers to all buffers to the list that contain data of the
441 // supplied interval.
442 void
443 MediaTrackAudioSupplier::_GetBuffersFor(BList& buffers, int64 position,
444 								 int64 frames) const
445 {
446 	buffers.MakeEmpty();
447 	for (int32 i = 0; Buffer* buffer = _BufferAt(i); i++) {
448 		// Calculate the intersecting interval and add the buffer if it is
449 		// not empty.
450 		int32 startFrame = max(position, buffer->offset);
451 		int32 endFrame = min(position + frames, buffer->offset + buffer->size);
452 		if (startFrame < endFrame)
453 			buffers.AddItem(buffer);
454 	}
455 }
456 
457 // _TouchBuffer
458 //
459 // Sets a buffer's time stamp to the current system time.
460 void
461 MediaTrackAudioSupplier::_TouchBuffer(Buffer* buffer)
462 {
463 	buffer->time_stamp = system_time();
464 }
465 
466 // _ReadBuffer
467 //
468 // Read a buffer from the current position (which is supplied in /position/)
469 // into /buffer/. The buffer's time stamp is set to the current system time.
470 status_t
471 MediaTrackAudioSupplier::_ReadBuffer(Buffer* buffer, int64 position)
472 {
473 	return _ReadBuffer(buffer, position, system_time());
474 }
475 
476 // _ReadBuffer
477 //
478 // Read a buffer from the current position (which is supplied in /position/)
479 // into /buffer/. The buffer's time stamp is set to the supplied time.
480 status_t
481 MediaTrackAudioSupplier::_ReadBuffer(Buffer* buffer, int64 position,
482 	bigtime_t time)
483 {
484 	status_t error = fMediaTrack->ReadFrames(buffer->data, &buffer->size);
485 
486 	TRACE("_ReadBuffer(%p, %lld): %s\n", buffer->data, buffer->size,
487 		strerror(error));
488 
489 	buffer->offset = position;
490 	buffer->time_stamp = time;
491 	if (error != B_OK)
492 		buffer->size = 0;
493 	return error;
494 }
495 
496 // _ReadCachedFrames
497 //
498 // Tries to read as much as possible data from the cache. The supplied
499 // buffer pointer as well as position and number of frames are adjusted
500 // accordingly. The used cache buffers are stamped with the supplied
501 // time.
502 void
503 MediaTrackAudioSupplier::_ReadCachedFrames(void*& dest, int64& pos,
504 	int64& frames, bigtime_t time)
505 {
506 	// Get a list of all cache buffers that contain data of the interval,
507 	// and sort it.
508 	BList buffers(10);
509 	_GetBuffersFor(buffers, pos, frames);
510 	buffers.SortItems(Buffer::CompareOffset);
511 	// Step forward through the list of cache buffers and try to read as
512 	// much data from the beginning as possible.
513 	for (int32 i = 0; Buffer* buffer = (Buffer*)buffers.ItemAt(i); i++) {
514 		if (buffer->offset <= pos && buffer->offset + buffer->size > pos) {
515 			// read from the beginning
516 			int64 size = min(frames, buffer->offset + buffer->size - pos);
517 			_CopyFrames(buffer->data, buffer->offset, dest, pos, pos, size);
518 			pos += size;
519 			frames -= size;
520 			dest = SkipFrames(dest, size);
521 			buffer->time_stamp = time;
522 		}
523 	}
524 	// Step backward through the list of cache buffers and try to read as
525 	// much data from the end as possible.
526 	for (int32 i = buffers.CountItems() - 1;
527 		 Buffer* buffer = (Buffer*)buffers.ItemAt(i);
528 		 i++) {
529 		if (buffer->offset < pos + frames
530 			&& buffer->offset + buffer->size >= pos + frames) {
531 			// read from the end
532 			int64 size = min(frames, pos + frames - buffer->offset);
533 			_CopyFrames(buffer->data, buffer->offset, dest, pos,
534 						pos + frames - size, size);
535 			frames -= size;
536 			buffer->time_stamp = time;
537 		}
538 	}
539 }
540 
541 
542 /*!	Reads /frames/ frames from /position/ into /buffer/. The frames are not
543 	read from the cache, but read frames are cached, if possible.
544 	New cache buffers are stamped with the supplied time.
545 	If an error occurs, the untouched part of the buffer is set to 0.
546 */
547 status_t
548 MediaTrackAudioSupplier::_ReadUncachedFrames(void* buffer, int64 position,
549 	int64 frames, bigtime_t time)
550 {
551 	TRACE("_ReadUncachedFrames()\n");
552 	status_t error = B_OK;
553 	// seek to the position
554 	int64 currentPos = position;
555 	if (frames > 0) {
556 		error = _SeekToKeyFrameBackward(currentPos);
557 		TRACE("_ReadUncachedFrames() - seeked to position: %lld\n", currentPos);
558 //		if (position - currentPos > 100000)
559 //			printf("MediaTrackAudioSupplier::_ReadUncachedFrames() - "
560 //				"keyframe was far away: %lld -> %lld\n", position, currentPos);
561 	}
562 	// read the frames
563 	// TODO: Calculate timeout, 0.25 times duration of "frames" seems good.
564 	bigtime_t timeout = 10000;
565 	while (error == B_OK && frames > 0) {
566 		Buffer* cacheBuffer = _FindUsableBufferFor(currentPos);
567 		TRACE("_ReadUncachedFrames() - usable buffer found: %p, "
568 			"position: %lld/%lld\n", cacheBuffer, currentPos, position);
569 		error = _ReadBuffer(cacheBuffer, currentPos, time);
570 		if (error == B_OK) {
571 			int64 size = min(position + frames,
572 				cacheBuffer->offset + cacheBuffer->size) - position;
573 			if (size > 0) {
574 				_CopyFrames(cacheBuffer, buffer, position, position, size);
575 				buffer = SkipFrames(buffer, size);
576 				position += size;
577 				frames -= size;
578 			}
579 			currentPos += cacheBuffer->size;
580 		}
581 		if (system_time() - time > timeout) {
582 			error = B_TIMED_OUT;
583 			break;
584 		}
585 	}
586 
587 #if 0
588 	// Ensure that all frames up to the next key frame are cached.
589 	// This avoids, that each read reaches the BMediaTrack.
590 	if (error == B_OK) {
591 		int64 nextKeyFrame = currentPos;
592 		if (_FindKeyFrameForward(nextKeyFrame) == B_OK) {
593 			while (currentPos < nextKeyFrame) {
594 				// Check, if data at this position are cache.
595 				// If not read it.
596 				Buffer* cacheBuffer = _FindBufferAtFrame(currentPos);
597 				if (!cacheBuffer || cacheBuffer->size == 0) {
598 					cacheBuffer = _FindUsableBufferFor(currentPos);
599 					if (_ReadBuffer(cacheBuffer, currentPos, time) != B_OK)
600 						break;
601 				}
602 				if (cacheBuffer)
603 					currentPos += cacheBuffer->size;
604 			}
605 		}
606 	}
607 #endif
608 
609 	// on error fill up the buffer with silence
610 	if (error != B_OK && frames > 0)
611 		ReadSilence(buffer, frames);
612 	return error;
613 }
614 
615 // _FindKeyFrameForward
616 status_t
617 MediaTrackAudioSupplier::_FindKeyFrameForward(int64& position)
618 {
619 	status_t error = B_OK;
620 	if (fHasKeyFrames) {
621 		error = fMediaTrack->FindKeyFrameForFrame(
622 			&position, B_MEDIA_SEEK_CLOSEST_FORWARD);
623 	} else {
624 		int64 framesPerBuffer = _FramesPerBuffer();
625 		position += framesPerBuffer - 1;
626 		position = position % framesPerBuffer;
627 	}
628 	return error;
629 }
630 
631 // _FindKeyFrameBackward
632 status_t
633 MediaTrackAudioSupplier::_FindKeyFrameBackward(int64& position)
634 {
635 	status_t error = B_OK;
636 	if (fHasKeyFrames) {
637 		error = fMediaTrack->FindKeyFrameForFrame(
638 			&position, B_MEDIA_SEEK_CLOSEST_BACKWARD);
639 	} else
640 		position -= position % _FramesPerBuffer();
641 	return error;
642 }
643 
644 #if 0
645 // _SeekToKeyFrameForward
646 status_t
647 MediaTrackAudioSupplier::_SeekToKeyFrameForward(int64& position)
648 {
649 	if (position == fMediaTrack->CurrentFrame())
650 		return B_OK;
651 
652 	status_t error = B_OK;
653 	if (fHasKeyFrames) {
654 		#ifdef TRACE_AUDIO_SUPPLIER
655 		int64 oldPosition = position;
656 		#endif
657 		error = fMediaTrack->SeekToFrame(&position,
658 			B_MEDIA_SEEK_CLOSEST_FORWARD);
659 		TRACE("_SeekToKeyFrameForward() - seek to key frame forward: "
660 			"%lld -> %lld (%lld)\n", oldPosition, position,
661 			fMediaTrack->CurrentFrame());
662 	} else {
663 		_FindKeyFrameForward(position);
664 		error = fMediaTrack->SeekToFrame(&position);
665 	}
666 	return error;
667 }
668 #endif
669 
670 // _SeekToKeyFrameBackward
671 status_t
672 MediaTrackAudioSupplier::_SeekToKeyFrameBackward(int64& position)
673 {
674 	int64 currentPosition = fMediaTrack->CurrentFrame();
675 	if (position == currentPosition)
676 		return B_OK;
677 
678 	status_t error = B_OK;
679 	if (fHasKeyFrames) {
680 		int64 wantedPosition = position;
681 		error = fMediaTrack->FindKeyFrameForFrame(&position,
682 			B_MEDIA_SEEK_CLOSEST_BACKWARD);
683 		if (error == B_OK && currentPosition > position
684 			&& currentPosition < wantedPosition) {
685 			// The current position is before the wanted position,
686 			// but later than the keyframe, so seeking is worse.
687 			position = currentPosition;
688 			return B_OK;
689 		}
690 		if (error == B_OK && position > wantedPosition) {
691 			// We asked to seek backwards, but the extractor seeked
692 			// forwards! Returning an error here will cause silence
693 			// to be produced.
694 			return B_ERROR;
695 		}
696 		if (error == B_OK)
697 			error = fMediaTrack->SeekToFrame(&position, 0);
698 		if (error != B_OK) {
699 			position = fMediaTrack->CurrentFrame();
700 //			if (fReportSeekError) {
701 				printf("  seek to key frame backward: %lld -> %lld (%lld) "
702 					"- %s\n", wantedPosition, position,
703 					fMediaTrack->CurrentFrame(), strerror(error));
704 				fReportSeekError = false;
705 //			}
706 		} else {
707 			fReportSeekError = true;
708 		}
709 	} else {
710 		_FindKeyFrameBackward(position);
711 		error = fMediaTrack->SeekToFrame(&position);
712 	}
713 	return error;
714 }
715 
716