xref: /haiku/src/apps/mediaplayer/supplier/MediaTrackAudioSupplier.cpp (revision adb0d19d561947362090081e81d90dde59142026)
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::"); printf(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 	: AudioTrackSupplier(),
59 	  fMediaTrack(mediaTrack),
60 	  fBuffer(NULL),
61 	  fBufferOffset(0),
62 	  fBufferSize(0),
63 	  fBuffers(10),
64 	  fHasKeyFrames(false),
65 	  fCountFrames(0),
66 	  fReportSeekError(true),
67 	  fTrackIndex(trackIndex)
68 {
69 	_InitFromTrack();
70 }
71 
72 
73 MediaTrackAudioSupplier::~MediaTrackAudioSupplier()
74 {
75 	_FreeBuffers();
76 	delete[] fBuffer;
77 }
78 
79 
80 const media_format&
81 MediaTrackAudioSupplier::Format() const
82 {
83 	return AudioReader::Format();
84 }
85 
86 
87 status_t
88 MediaTrackAudioSupplier::GetEncodedFormat(media_format* format) const
89 {
90 	if (!fMediaTrack)
91 		return B_NO_INIT;
92 	return fMediaTrack->EncodedFormat(format);
93 }
94 
95 
96 status_t
97 MediaTrackAudioSupplier::GetCodecInfo(media_codec_info* info) const
98 {
99 	if (!fMediaTrack)
100 		return B_NO_INIT;
101 	return fMediaTrack->GetCodecInfo(info);
102 }
103 
104 
105 bigtime_t
106 MediaTrackAudioSupplier::Duration() const
107 {
108 	if (!fMediaTrack)
109 		return 0;
110 
111 	return fMediaTrack->Duration();
112 }
113 
114 
115 // #pragma mark - AudioReader
116 
117 
118 // Read
119 status_t
120 MediaTrackAudioSupplier::Read(void* buffer, int64 pos, int64 frames)
121 {
122 	TRACE("Read(%p, %lld, %lld)\n", buffer, pos,
123 		frames);
124 	TRACE("  this: %p, fOutOffset: %lld\n", this, fOutOffset);
125 
126 	status_t error = InitCheck();
127 	if (error != B_OK) {
128 		TRACE("Read() done\n");
129 		return error;
130 	}
131 
132 	// convert pos according to our offset
133 	pos += fOutOffset;
134 	// Fill the frames after the end of the track with silence.
135 	if (pos + frames > fCountFrames) {
136 		int64 size = max(0LL, fCountFrames - pos);
137 		ReadSilence(SkipFrames(buffer, size), frames - size);
138 		frames = size;
139 	}
140 
141 	TRACE("  after eliminating the frames after the track end: %p, %lld, %lld\n",
142 		buffer, pos, frames);
143 
144 	// read the cached frames
145 	bigtime_t time = system_time();
146 	if (frames > 0)
147 		_ReadCachedFrames(buffer, pos, frames, time);
148 
149 	TRACE("  after reading from cache: %p, %lld, %lld\n", buffer, pos, frames);
150 
151 	// read the remaining (uncached) frames
152 	if (frames > 0)
153 		_ReadUncachedFrames(buffer, pos, frames, time);
154 
155 	TRACE("Read() done\n");
156 
157 	return B_OK;
158 }
159 
160 // InitCheck
161 status_t
162 MediaTrackAudioSupplier::InitCheck() const
163 {
164 	status_t error = AudioReader::InitCheck();
165 	if (error == B_OK && (!fMediaTrack || !fBuffer))
166 		error = B_NO_INIT;
167 	return error;
168 }
169 
170 // #pragma mark -
171 
172 // _InitFromTrack
173 void
174 MediaTrackAudioSupplier::_InitFromTrack()
175 {
176 	if (fMediaTrack && fMediaTrack->DecodedFormat(&fFormat) == B_OK
177 		&& fFormat.type == B_MEDIA_RAW_AUDIO) {
178 
179 		#ifdef TRACE_AUDIO_SUPPLIER
180 			char formatString[256];
181 			string_for_format(fFormat, formatString, 256);
182 			TRACE("MediaTrackAudioSupplier: format is: %s\n", formatString);
183 		#endif
184 
185 		fBuffer = new (nothrow) char[fFormat.u.raw_audio.buffer_size];
186 		_AllocateBuffers();
187 
188 		// Find out, if the track has key frames: as a heuristic we
189 		// check, if the first and the second frame have the same backward
190 		// key frame.
191 		// Note: It shouldn't harm that much, if we're wrong and the
192 		// track has key frame although we found out that it has not.
193 		int64 keyFrame0 = 0;
194 		int64 keyFrame1 = 1;
195 		fMediaTrack->FindKeyFrameForFrame(&keyFrame0,
196 			B_MEDIA_SEEK_CLOSEST_BACKWARD);
197 		fMediaTrack->FindKeyFrameForFrame(&keyFrame1,
198 			B_MEDIA_SEEK_CLOSEST_BACKWARD);
199 		fHasKeyFrames = (keyFrame0 == keyFrame1);
200 
201 		// get the length of the track
202 		fCountFrames = fMediaTrack->CountFrames();
203 
204 		TRACE("MediaTrackAudioSupplier: keyframes: %d, frame count: %lld\n",
205 			fHasKeyFrames, fCountFrames);
206 	} else
207 		fMediaTrack = NULL;
208 }
209 
210 // _FramesPerBuffer
211 int64
212 MediaTrackAudioSupplier::_FramesPerBuffer() const
213 {
214 	int64 sampleSize = fFormat.u.raw_audio.format
215 		& media_raw_audio_format::B_AUDIO_SIZE_MASK;
216 	int64 frameSize = sampleSize * fFormat.u.raw_audio.channel_count;
217 	return fFormat.u.raw_audio.buffer_size / frameSize;
218 }
219 
220 // _CopyFrames
221 //
222 // Given two buffers starting at different frame offsets, this function
223 // copies /frames/ frames at position /position/ from the source to the
224 // target buffer.
225 // Note that no range checking is done.
226 void
227 MediaTrackAudioSupplier::_CopyFrames(void* source, int64 sourceOffset,
228 							  void* target, int64 targetOffset,
229 							  int64 position, int64 frames) const
230 {
231 	int64 sampleSize = fFormat.u.raw_audio.format
232 					   & media_raw_audio_format::B_AUDIO_SIZE_MASK;
233 	int64 frameSize = sampleSize * fFormat.u.raw_audio.channel_count;
234 	source = (char*)source + frameSize * (position - sourceOffset);
235 	target = (char*)target + frameSize * (position - targetOffset);
236 	memcpy(target, source, frames * frameSize);
237 }
238 
239 // _CopyFrames
240 //
241 // Given two buffers starting at different frame offsets, this function
242 // copies /frames/ frames at position /position/ from the source to the
243 // target buffer. This version expects a cache buffer as source.
244 // Note that no range checking is done.
245 void
246 MediaTrackAudioSupplier::_CopyFrames(Buffer* buffer,
247 							  void* target, int64 targetOffset,
248 							  int64 position, int64 frames) const
249 {
250 	_CopyFrames(buffer->data, buffer->offset, target, targetOffset, position,
251 				frames);
252 }
253 
254 // _AllocateBuffers
255 //
256 // Allocates a set of buffers.
257 void
258 MediaTrackAudioSupplier::_AllocateBuffers()
259 {
260 	int32 count = 10;
261 	_FreeBuffers();
262 	int32 bufferSize = fFormat.u.raw_audio.buffer_size;
263 	char* data = new (nothrow) char[bufferSize * count];
264 	for (; count > 0; count--) {
265 		Buffer* buffer = new (nothrow) Buffer;
266 		if (!buffer || !fBuffers.AddItem(buffer)) {
267 			delete buffer;
268 			if (fBuffers.CountItems() == 0)
269 				delete[] data;
270 			return;
271 		}
272 		buffer->data = data;
273 		data += bufferSize;
274 		buffer->offset = 0;
275 		buffer->size = 0;
276 		buffer->time_stamp = 0;
277 	}
278 }
279 
280 // _FreeBuffers
281 //
282 // Frees the allocated buffers.
283 void
284 MediaTrackAudioSupplier::_FreeBuffers()
285 {
286 	if (fBuffers.CountItems() > 0) {
287 		delete[] (char*)_BufferAt(0)->data;
288 		for (int32 i = 0; Buffer* buffer = _BufferAt(i); i++)
289 			delete buffer;
290 		fBuffers.MakeEmpty();
291 	}
292 }
293 
294 // _BufferAt
295 //
296 // Returns the buffer at index /index/.
297 MediaTrackAudioSupplier::Buffer*
298 MediaTrackAudioSupplier::_BufferAt(int32 index) const
299 {
300 	return (Buffer*)fBuffers.ItemAt(index);
301 }
302 
303 // _FindBufferAtFrame
304 //
305 // If any buffer starts at offset /frame/, it is returned, NULL otherwise.
306 MediaTrackAudioSupplier::Buffer*
307 MediaTrackAudioSupplier::_FindBufferAtFrame(int64 frame) const
308 {
309 	Buffer* buffer = NULL;
310 	for (int32 i = 0;
311 		 ((buffer = _BufferAt(i))) && buffer->offset != frame;
312 		 i++);
313 	return buffer;
314 }
315 
316 // _FindUnusedBuffer
317 //
318 // Returns the first unused buffer or NULL if all buffers are used.
319 MediaTrackAudioSupplier::Buffer*
320 MediaTrackAudioSupplier::_FindUnusedBuffer() const
321 {
322 	Buffer* buffer = NULL;
323 	for (int32 i = 0; ((buffer = _BufferAt(i))) && buffer->size != 0; i++);
324 	return buffer;
325 }
326 
327 // _FindUsableBuffer
328 //
329 // Returns either an unused buffer or, if all buffers are used, the least
330 // recently used buffer.
331 // In every case a buffer is returned.
332 MediaTrackAudioSupplier::Buffer*
333 MediaTrackAudioSupplier::_FindUsableBuffer() const
334 {
335 	Buffer* result = _FindUnusedBuffer();
336 	if (!result) {
337 		// find the least recently used buffer.
338 		result = _BufferAt(0);
339 		for (int32 i = 1; Buffer* buffer = _BufferAt(i); i++) {
340 			if (buffer->time_stamp < result->time_stamp)
341 				result = buffer;
342 		}
343 	}
344 	return result;
345 }
346 
347 // _FindUsableBufferFor
348 //
349 // In case there already exists a buffer that starts at position this
350 // one is returned. Otherwise the function returns either an unused
351 // buffer or, if all buffers are used, the least recently used buffer.
352 // In every case a buffer is returned.
353 MediaTrackAudioSupplier::Buffer*
354 MediaTrackAudioSupplier::_FindUsableBufferFor(int64 position) const
355 {
356 	Buffer* buffer = _FindBufferAtFrame(position);
357 	if (!buffer)
358 		buffer = _FindUsableBuffer();
359 	return buffer;
360 }
361 
362 // _GetBuffersFor
363 //
364 // Adds pointers to all buffers to the list that contain data of the
365 // supplied interval.
366 void
367 MediaTrackAudioSupplier::_GetBuffersFor(BList& buffers, int64 position,
368 								 int64 frames) const
369 {
370 	buffers.MakeEmpty();
371 	for (int32 i = 0; Buffer* buffer = _BufferAt(i); i++) {
372 		// Calculate the intersecting interval and add the buffer if it is
373 		// not empty.
374 		int32 startFrame = max(position, buffer->offset);
375 		int32 endFrame = min(position + frames, buffer->offset + buffer->size);
376 		if (startFrame < endFrame)
377 			buffers.AddItem(buffer);
378 	}
379 }
380 
381 // _TouchBuffer
382 //
383 // Sets a buffer's time stamp to the current system time.
384 void
385 MediaTrackAudioSupplier::_TouchBuffer(Buffer* buffer)
386 {
387 	buffer->time_stamp = system_time();
388 }
389 
390 // _ReadBuffer
391 //
392 // Read a buffer from the current position (which is supplied in /position/)
393 // into /buffer/. The buffer's time stamp is set to the current system time.
394 status_t
395 MediaTrackAudioSupplier::_ReadBuffer(Buffer* buffer, int64 position)
396 {
397 	return _ReadBuffer(buffer, position, system_time());
398 }
399 
400 // _ReadBuffer
401 //
402 // Read a buffer from the current position (which is supplied in /position/)
403 // into /buffer/. The buffer's time stamp is set to the supplied time.
404 status_t
405 MediaTrackAudioSupplier::_ReadBuffer(Buffer* buffer, int64 position,
406 	bigtime_t time)
407 {
408 	status_t error = fMediaTrack->ReadFrames(buffer->data, &buffer->size);
409 
410 	TRACE("Read(%p, %lld): %s\n", buffer->data, buffer->size, strerror(error));
411 
412 	buffer->offset = position;
413 	buffer->time_stamp = time;
414 	if (error != B_OK)
415 		buffer->size = 0;
416 	return error;
417 }
418 
419 // _ReadCachedFrames
420 //
421 // Tries to read as much as possible data from the cache. The supplied
422 // buffer pointer as well as position and number of frames are adjusted
423 // accordingly. The used cache buffers are stamped with the current
424 // system time.
425 void
426 MediaTrackAudioSupplier::_ReadCachedFrames(void*& dest, int64& pos,
427 	int64& frames)
428 {
429 	_ReadCachedFrames(dest, pos, frames, system_time());
430 }
431 
432 // _ReadCachedFrames
433 //
434 // Tries to read as much as possible data from the cache. The supplied
435 // buffer pointer as well as position and number of frames are adjusted
436 // accordingly. The used cache buffers are stamped with the supplied
437 // time.
438 void
439 MediaTrackAudioSupplier::_ReadCachedFrames(void*& dest, int64& pos,
440 	int64& frames, bigtime_t time)
441 {
442 	// Get a list of all cache buffers that contain data of the interval,
443 	// and sort it.
444 	BList buffers(10);
445 	_GetBuffersFor(buffers, pos, frames);
446 	buffers.SortItems(Buffer::CompareOffset);
447 	// Step forward through the list of cache buffers and try to read as
448 	// much data from the beginning as possible.
449 	for (int32 i = 0; Buffer* buffer = (Buffer*)buffers.ItemAt(i); i++) {
450 		if (buffer->offset <= pos && buffer->offset + buffer->size > pos) {
451 			// read from the beginning
452 			int64 size = min(frames, buffer->offset + buffer->size - pos);
453 			_CopyFrames(buffer->data, buffer->offset, dest, pos, pos, size);
454 			pos += size;
455 			frames -= size;
456 			dest = SkipFrames(dest, size);
457 		}
458 		buffer->time_stamp = time;
459 	}
460 	// Step backward through the list of cache buffers and try to read as
461 	// much data from the end as possible.
462 	for (int32 i = buffers.CountItems() - 1;
463 		 Buffer* buffer = (Buffer*)buffers.ItemAt(i);
464 		 i++) {
465 		if (buffer->offset < pos + frames
466 			&& buffer->offset + buffer->size >= pos + frames) {
467 			// read from the end
468 			int64 size = min(frames, pos + frames - buffer->offset);
469 			_CopyFrames(buffer->data, buffer->offset, dest, pos,
470 						pos + frames - size, size);
471 			frames -= size;
472 		}
473 	}
474 }
475 
476 // _ReadUncachedFrames
477 //
478 // Reads /frames/ frames from /position/ into /buffer/. The frames are not
479 // read from the cache, but read frames are cached, if possible.
480 // New cache buffers are stamped with the system time.
481 // If an error occurs, the untouched part of the buffer is set to 0.
482 status_t
483 MediaTrackAudioSupplier::_ReadUncachedFrames(void* buffer, int64 position,
484 									  int64 frames)
485 {
486 	return _ReadUncachedFrames(buffer, position, frames, system_time());
487 }
488 
489 // _ReadUncachedFrames
490 //
491 // Reads /frames/ frames from /position/ into /buffer/. The frames are not
492 // read from the cache, but read frames are cached, if possible.
493 // New cache buffers are stamped with the supplied time.
494 // If an error occurs, the untouched part of the buffer is set to 0.
495 status_t
496 MediaTrackAudioSupplier::_ReadUncachedFrames(void* buffer, int64 position,
497 									  int64 frames, bigtime_t time)
498 {
499 	status_t error = B_OK;
500 	// seek to the position
501 	int64 currentPos = position;
502 	if (frames > 0) {
503 		error = _SeekToKeyFrameBackward(currentPos);
504 		TRACE("_ReadUncachedFrames() - seeked to position: %lld\n", currentPos);
505 	}
506 	// read the frames
507 	while (error == B_OK && frames > 0) {
508 		Buffer* cacheBuffer = _FindUsableBufferFor(currentPos);
509 		TRACE("_ReadUncachedFrames() - usable buffer found: %p\n", cacheBuffer);
510 		error = _ReadBuffer(cacheBuffer, currentPos, time);
511 		if (error == B_OK) {
512 			int64 size = min(position + frames,
513 							 cacheBuffer->offset + cacheBuffer->size)
514 						 - position;
515 			if (size > 0) {
516 				_CopyFrames(cacheBuffer, buffer, position, position, size);
517 				buffer = SkipFrames(buffer, size);
518 				position += size;
519 				frames -= size;
520 			}
521 			currentPos += cacheBuffer->size;
522 		}
523 	}
524 	// Ensure that all frames up to the next key frame are cached.
525 	// This avoids, that each read
526 	if (error == B_OK) {
527 		int64 nextKeyFrame = currentPos;
528 		if (_FindKeyFrameForward(nextKeyFrame) == B_OK) {
529 			while (currentPos < nextKeyFrame) {
530 				// Check, if data at this position are cache.
531 				// If not read it.
532 				Buffer* cacheBuffer = _FindBufferAtFrame(currentPos);
533 				if (!cacheBuffer || cacheBuffer->size == 0) {
534 					cacheBuffer = _FindUsableBufferFor(currentPos);
535 					if (_ReadBuffer(cacheBuffer, currentPos, time) != B_OK)
536 						break;
537 				}
538 				if (cacheBuffer)
539 					currentPos += cacheBuffer->size;
540 			}
541 		}
542 	}
543 	// on error fill up the buffer with silence
544 	if (error != B_OK && frames > 0)
545 		ReadSilence(buffer, frames);
546 	return error;
547 }
548 
549 // _FindKeyFrameForward
550 status_t
551 MediaTrackAudioSupplier::_FindKeyFrameForward(int64& position)
552 {
553 	status_t error = B_OK;
554 // NOTE: the keyframe version confuses the Frauenhofer MP3 decoder,
555 // it works fine with the non-keyframe version, so let's hope this
556 // is the case for all other keyframe based BeOS codecs...
557 //	if (fHasKeyFrames) {
558 //		error = fMediaTrack->FindKeyFrameForFrame(
559 //			&position, B_MEDIA_SEEK_CLOSEST_FORWARD);
560 //	} else {
561 		int64 framesPerBuffer = _FramesPerBuffer();
562 		position += framesPerBuffer - 1;
563 		position = position % framesPerBuffer;
564 //	}
565 	return error;
566 }
567 
568 // _FindKeyFrameBackward
569 status_t
570 MediaTrackAudioSupplier::_FindKeyFrameBackward(int64& position)
571 {
572 	status_t error = B_OK;
573 	if (fHasKeyFrames) {
574 		error = fMediaTrack->FindKeyFrameForFrame(
575 			&position, B_MEDIA_SEEK_CLOSEST_BACKWARD);
576 	} else
577 		position -= position % _FramesPerBuffer();
578 	return error;
579 }
580 
581 // _SeekToKeyFrameForward
582 status_t
583 MediaTrackAudioSupplier::_SeekToKeyFrameForward(int64& position)
584 {
585 	if (position == fMediaTrack->CurrentFrame())
586 		return B_OK;
587 
588 	status_t error = B_OK;
589 	if (fHasKeyFrames) {
590 		#ifdef TRACE_AUDIO_SUPPLIER
591 		int64 oldPosition = position;
592 		#endif
593 		error = fMediaTrack->SeekToFrame(&position,
594 			B_MEDIA_SEEK_CLOSEST_FORWARD);
595 		TRACE("_SeekToKeyFrameForward() - seek to key frame forward: "
596 			"%lld -> %lld (%lld)\n", oldPosition, position,
597 			fMediaTrack->CurrentFrame());
598 	} else {
599 		_FindKeyFrameForward(position);
600 		error = fMediaTrack->SeekToFrame(&position);
601 	}
602 	return error;
603 }
604 
605 // _SeekToKeyFrameBackward
606 status_t
607 MediaTrackAudioSupplier::_SeekToKeyFrameBackward(int64& position)
608 {
609 	if (position == fMediaTrack->CurrentFrame())
610 		return B_OK;
611 
612 	status_t error = B_OK;
613 	if (fHasKeyFrames) {
614 		int64 oldPosition = position;
615 		error = fMediaTrack->FindKeyFrameForFrame(&position,
616 			B_MEDIA_SEEK_CLOSEST_BACKWARD);
617 		if (error >= B_OK)
618 			error = fMediaTrack->SeekToFrame(&position, 0);
619 		if (error < B_OK) {
620 			position = fMediaTrack->CurrentFrame();
621 			if (fReportSeekError) {
622 				printf("  seek to key frame backward: %lld -> %lld (%lld) "
623 					"- %s\n", oldPosition, position,
624 					fMediaTrack->CurrentFrame(), strerror(error));
625 				fReportSeekError = false;
626 			}
627 		} else {
628 			fReportSeekError = true;
629 		}
630 	} else {
631 		_FindKeyFrameBackward(position);
632 		error = fMediaTrack->SeekToFrame(&position);
633 	}
634 	return error;
635 }
636 
637