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
16using 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
29struct 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
39int
40MediaTrackAudioSupplier::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
56MediaTrackAudioSupplier::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
74MediaTrackAudioSupplier::~MediaTrackAudioSupplier()
75{
76	_FreeBuffers();
77	delete[] fBuffer;
78}
79
80
81const media_format&
82MediaTrackAudioSupplier::Format() const
83{
84	return AudioReader::Format();
85}
86
87
88status_t
89MediaTrackAudioSupplier::GetEncodedFormat(media_format* format) const
90{
91	if (!fMediaTrack)
92		return B_NO_INIT;
93	return fMediaTrack->EncodedFormat(format);
94}
95
96
97status_t
98MediaTrackAudioSupplier::GetCodecInfo(media_codec_info* info) const
99{
100	if (!fMediaTrack)
101		return B_NO_INIT;
102	return fMediaTrack->GetCodecInfo(info);
103}
104
105
106bigtime_t
107MediaTrackAudioSupplier::Duration() const
108{
109	if (!fMediaTrack)
110		return 0;
111
112	return fMediaTrack->Duration();
113}
114
115
116// #pragma mark - AudioReader
117
118
119bigtime_t
120MediaTrackAudioSupplier::InitialLatency() const
121{
122	// TODO: this is just a wild guess, and not really founded on anything.
123	return 100000;
124}
125
126
127status_t
128MediaTrackAudioSupplier::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((int64)0, 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) {
163printf("  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) {
174printf("  error seeking to position: %lld (%lld)\n", pos,
175	fMediaTrack->CurrentFrame());
176
177			return error;
178		}
179
180		if (keyFrame < pos) {
181printf("  need to skip %lld frames\n", pos - keyFrame);
182			uint8 dummyBuffer[size];
183			while (pos - keyFrame >= framesPerBuffer) {
184printf("  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) {
191printf("  skipped %lu frames (rest)\n", framesPerBuffer);
192				fMediaTrack->ReadFrames(dummyBuffer, &restSize);
193			}
194		}
195	}
196	while (frames > 0) {
197printf("  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	}
203printf("  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
224status_t
225MediaTrackAudioSupplier::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
236void
237MediaTrackAudioSupplier::_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: %" B_PRId16 ", frame count: %" B_PRId64
280		"\n", fHasKeyFrames, fCountFrames);
281	printf("_InitFromTrack(): keyframes: %" B_PRId16 ", frame count: %"
282		B_PRId64 "\n", fHasKeyFrames, fCountFrames);
283}
284
285// _FramesPerBuffer
286int64
287MediaTrackAudioSupplier::_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.
301void
302MediaTrackAudioSupplier::_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.
320void
321MediaTrackAudioSupplier::_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.
332void
333MediaTrackAudioSupplier::_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.
358void
359MediaTrackAudioSupplier::_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/.
372MediaTrackAudioSupplier::Buffer*
373MediaTrackAudioSupplier::_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.
381MediaTrackAudioSupplier::Buffer*
382MediaTrackAudioSupplier::_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.
394MediaTrackAudioSupplier::Buffer*
395MediaTrackAudioSupplier::_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.
408MediaTrackAudioSupplier::Buffer*
409MediaTrackAudioSupplier::_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.
429MediaTrackAudioSupplier::Buffer*
430MediaTrackAudioSupplier::_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.
442void
443MediaTrackAudioSupplier::_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.
460void
461MediaTrackAudioSupplier::_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.
470status_t
471MediaTrackAudioSupplier::_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.
480status_t
481MediaTrackAudioSupplier::_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.
502void
503MediaTrackAudioSupplier::_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*/
547status_t
548MediaTrackAudioSupplier::_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
616// _FindKeyFrameForward
617status_t
618MediaTrackAudioSupplier::_FindKeyFrameForward(int64& position)
619{
620	status_t error = B_OK;
621	if (fHasKeyFrames) {
622		error = fMediaTrack->FindKeyFrameForFrame(
623			&position, B_MEDIA_SEEK_CLOSEST_FORWARD);
624	} else {
625		int64 framesPerBuffer = _FramesPerBuffer();
626		position += framesPerBuffer - 1;
627		position = position % framesPerBuffer;
628	}
629	return error;
630}
631
632
633// _FindKeyFrameBackward
634status_t
635MediaTrackAudioSupplier::_FindKeyFrameBackward(int64& position)
636{
637	status_t error = B_OK;
638	if (fHasKeyFrames) {
639		error = fMediaTrack->FindKeyFrameForFrame(
640			&position, B_MEDIA_SEEK_CLOSEST_BACKWARD);
641	} else
642		position -= position % _FramesPerBuffer();
643	return error;
644}
645
646
647#if 0
648// _SeekToKeyFrameForward
649status_t
650MediaTrackAudioSupplier::_SeekToKeyFrameForward(int64& position)
651{
652	if (position == fMediaTrack->CurrentFrame())
653		return B_OK;
654
655	status_t error = B_OK;
656	if (fHasKeyFrames) {
657		#ifdef TRACE_AUDIO_SUPPLIER
658		int64 oldPosition = position;
659		#endif
660		error = fMediaTrack->SeekToFrame(&position,
661			B_MEDIA_SEEK_CLOSEST_FORWARD);
662		TRACE("_SeekToKeyFrameForward() - seek to key frame forward: "
663			"%lld -> %lld (%lld)\n", oldPosition, position,
664			fMediaTrack->CurrentFrame());
665	} else {
666		_FindKeyFrameForward(position);
667		error = fMediaTrack->SeekToFrame(&position);
668	}
669	return error;
670}
671#endif
672
673
674// _SeekToKeyFrameBackward
675status_t
676MediaTrackAudioSupplier::_SeekToKeyFrameBackward(int64& position)
677{
678	int64 currentPosition = fMediaTrack->CurrentFrame();
679	if (position == currentPosition)
680		return B_OK;
681
682	status_t error = B_OK;
683	if (fHasKeyFrames) {
684		int64 wantedPosition = position;
685		error = fMediaTrack->FindKeyFrameForFrame(&position,
686			B_MEDIA_SEEK_CLOSEST_BACKWARD);
687		if (error == B_OK && currentPosition > position
688			&& currentPosition < wantedPosition) {
689			// The current position is before the wanted position,
690			// but later than the keyframe, so seeking is worse.
691			position = currentPosition;
692			return B_OK;
693		}
694		if (error == B_OK && position > wantedPosition) {
695			// We asked to seek backwards, but the extractor seeked
696			// forwards! Returning an error here will cause silence
697			// to be produced.
698			return B_ERROR;
699		}
700		if (error == B_OK)
701			error = fMediaTrack->SeekToFrame(&position, 0);
702		if (error != B_OK) {
703			position = fMediaTrack->CurrentFrame();
704//			if (fReportSeekError) {
705				printf("  seek to key frame backward: %" B_PRId64 " -> %"
706					B_PRId64 " (%" B_PRId64 ") - %s\n", wantedPosition,
707					position, fMediaTrack->CurrentFrame(), strerror(error));
708				fReportSeekError = false;
709//			}
710		} else {
711			fReportSeekError = true;
712		}
713	} else {
714		_FindKeyFrameBackward(position);
715		error = fMediaTrack->SeekToFrame(&position);
716	}
717	return error;
718}
719
720