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