1// MediaReader.cpp
2//
3// Andrew Bachmann, 2002
4//
5// A MediaReader is a node that
6// implements FileInterface and BBufferProducer.
7// It reads any file and produces one output,
8// which is a multistream.
9//
10// see also MediaReaderAddOn.cpp
11#include "../AbstractFileInterfaceNode.h"
12#include "MediaReader.h"
13#include "../misc.h"
14#include "debug.h"
15
16#include <Buffer.h>
17#include <BufferGroup.h>
18#include <BufferProducer.h>
19#include <Controllable.h>
20#include <Entry.h>
21#include <Errors.h>
22#include <File.h>
23#include <FileInterface.h>
24#include <MediaAddOn.h>
25#include <MediaDefs.h>
26#include <MediaEventLooper.h>
27#include <MediaNode.h>
28#include <MediaRoster.h>
29#include <ParameterWeb.h>
30#include <TimeSource.h>
31
32
33#include <limits.h>
34#include <stdio.h>
35#include <string.h>
36
37
38MediaReader::~MediaReader(void)
39{
40	fprintf(stderr,"MediaReader::~MediaReader\n");
41	if (fBufferGroup != 0) {
42		BBufferGroup * group = fBufferGroup;
43		fBufferGroup = 0;
44		delete group;
45	}
46}
47
48
49MediaReader::MediaReader(
50				size_t defaultChunkSize,
51				float defaultBitRate,
52				const flavor_info * info,
53				BMessage * config,
54				BMediaAddOn * addOn)
55	: BMediaNode("MediaReader"),
56	  BBufferProducer(B_MEDIA_MULTISTREAM),
57	  AbstractFileInterfaceNode(defaultChunkSize, defaultBitRate, info, config, addOn)
58{
59	CALLED();
60
61	// null some fields
62	fBufferGroup = 0;
63	// start enabled
64	fOutputEnabled = true;
65	// don't overwrite available space, and be sure to terminate
66	strncpy(output.name,"MediaReader Output",B_MEDIA_NAME_LENGTH-1);
67	output.name[B_MEDIA_NAME_LENGTH-1] = '\0';
68	// initialize the output
69	output.node = media_node::null;     // until registration
70	output.source = media_source::null; // until registration
71	output.destination = media_destination::null;
72	GetFormat(&output.format);
73}
74
75
76// -------------------------------------------------------- //
77// implementation of BMediaNode
78// -------------------------------------------------------- //
79void MediaReader::Preroll(void)
80{
81	CALLED();
82	// XXX:Performance opportunity
83	BMediaNode::Preroll();
84}
85
86
87status_t MediaReader::HandleMessage(
88				int32 message,
89				const void * data,
90				size_t size)
91{
92	CALLED();
93
94	status_t status = B_OK;
95
96	switch (message) {
97		// no special messages for now
98		default:
99			status = BBufferProducer::HandleMessage(message,data,size);
100			if (status == B_OK) {
101				break;
102			}
103			status = AbstractFileInterfaceNode::HandleMessage(message,data,size);
104			break;
105	}
106
107	return status;
108}
109
110
111void MediaReader::NodeRegistered(void)
112{
113	CALLED();
114
115	// now we can do this
116	output.node = Node();
117	output.source.id = 0;
118	output.source.port = output.node.port; // same as ControlPort();
119
120	// creates the parameter web and starts the looper thread
121	AbstractFileInterfaceNode::NodeRegistered();
122}
123
124
125// -------------------------------------------------------- //
126// implementation of BFileInterface
127// -------------------------------------------------------- //
128status_t MediaReader::SetRef(
129				const entry_ref & file,
130				bool create,
131				bigtime_t * out_time)
132{
133	CALLED();
134
135	status_t status = AbstractFileInterfaceNode::SetRef(file,B_READ_ONLY,create,out_time);
136	if (status != B_OK) {
137		PRINT("AbstractFileInterfaceNode::SetRef returned an error\n");
138		return status;
139	}
140
141	if (output.destination == media_destination::null) {
142		// reset the format, and set the requirements imposed by this file
143		GetFormat(&output.format);
144		AddRequirements(&output.format);
145		return B_OK;
146	}
147
148	// if we are connected we may have to re-negotiate the connection
149	media_format format;
150	GetFormat(&format);
151	AddRequirements(&format);
152	if (format_is_acceptible(format,output.format)) {
153		fprintf(stderr,"  compatible format = no re-negotiation necessary\n");
154		return B_OK;
155	}
156	// first try the easy way : SORRY DEPRECATED into private :-(
157	// this code from MediaWriter would be different for MediaReader even if it worked...
158	//	int32 change_tag = NewChangeTag();
159	//	status = this->BBufferConsumer::RequestFormatChange(output.source,output.destination,&format,&change_tag);
160	//	if (status == B_OK) {
161	//		fprintf(stderr,"  format change successful\n");
162	//		return B_OK;
163	//	}
164
165	// okay, the hard way requires we get the MediaRoster
166	BMediaRoster * roster = BMediaRoster::Roster(&status);
167	if (roster == 0)
168		return B_MEDIA_SYSTEM_FAILURE;
169
170	if (status != B_OK)
171		return status;
172
173	// before disconnect one should always stop the nodes (bebook says)
174	// requires run_state cast since the return type on RunState() is
175	// wrong [int32]
176	run_state destinationRunState = run_state(RunState());
177	if (destinationRunState == BMediaEventLooper::B_STARTED)
178		Stop(0,true); // stop us right now
179
180	// should also stop the destination if it is running, but how?
181	/*	BMediaNode destinationNode = ??
182	run_state destinationRunState = destinationNode->??;
183	status = destinationNode->StopNode(??,0,true);
184	if (status != B_OK) {
185		return status;
186	}  */
187	// we should disconnect right now
188	media_destination outputDestination = output.destination;
189	status = roster->Disconnect(output.source.id,output.source,
190							    output.destination.id,output.destination);
191	if (status != B_OK)
192		return status;
193
194	// if that went okay, we'll try reconnecting
195	media_output connectOutput;
196	media_input connectInput;
197	status = roster->Connect(output.source,outputDestination,
198							 &format,&connectOutput,&connectInput);
199	if (status != B_OK)
200		return status;
201
202	// now restart if necessary
203	if (destinationRunState == BMediaEventLooper::B_STARTED) {
204		Start(0);
205	}
206	return status;
207}
208
209
210// -------------------------------------------------------- //
211// implemention of BBufferProducer
212// -------------------------------------------------------- //
213
214// They are asking us to make the first offering.
215// So, we get a fresh format and then add requirements based
216// on the current file. (if any)
217status_t MediaReader::FormatSuggestionRequested(
218				media_type type,
219				int32 quality,
220				media_format * format)
221{
222	CALLED();
223
224	if ((type != B_MEDIA_MULTISTREAM) && (type != B_MEDIA_UNKNOWN_TYPE)) {
225		PRINT("\t<- B_MEDIA_BAD_FORMAT\n");
226		return B_MEDIA_BAD_FORMAT;
227	}
228
229	GetFormat(format);
230	AddRequirements(format);
231	return B_OK;
232}
233
234
235// They made an offer to us.  We should make sure that the offer is
236// acceptable, and then we can add any requirements we have on top of
237// that.  We leave wildcards for anything that we don't care about.
238status_t MediaReader::FormatProposal(
239				const media_source & output_source,
240				media_format * format)
241{
242	CALLED();
243
244	if (output.source != output_source) {
245		PRINT("\t<- B_MEDIA_BAD_SOURCE\n");
246		return B_MEDIA_BAD_SOURCE; // we only have one output so that better be it
247	}
248	/*	media_format * myFormat = GetFormat();
249	fprintf(stderr,"proposed format: ");
250	print_media_format(format);
251	fprintf(stderr,"\n");
252	fprintf(stderr,"my format: ");
253	print_media_format(myFormat);
254	fprintf(stderr,"\n"); */
255	// Be's format_is_compatible doesn't work.
256	//	if (!format_is_compatible(*format,*myFormat)) {
257	media_format myFormat;
258	GetFormat(&myFormat);
259	if (!format_is_acceptible(*format,myFormat)) {
260		PRINT("\t<- B_MEDIA_BAD_FORMAT\n");
261		return B_MEDIA_BAD_FORMAT;
262	}
263	AddRequirements(format);
264	return B_OK;
265}
266
267
268// Presumably we have already agreed with them that this format is
269// okay.  But just in case, we check the offer. (and complain if it
270// is invalid)  Then as the last thing we do, we get rid of any
271// remaining wilcards.
272status_t MediaReader::FormatChangeRequested(
273				const media_source & source,
274				const media_destination & destination,
275				media_format * io_format,
276				int32 * _deprecated_)
277{
278	CALLED();
279
280	if (output.source != source) {
281		PRINT("\t<- B_MEDIA_BAD_SOURCE\n");
282		return B_MEDIA_BAD_SOURCE;
283	}
284	status_t status = FormatProposal(source,io_format);
285	if (status != B_OK) {
286		PRINT("\terror returned by FormatProposal\n");
287		GetFormat(io_format);
288		return status;
289	}
290
291	return ResolveWildcards(io_format);
292}
293
294
295status_t MediaReader::GetNextOutput(	/* cookie starts as 0 */
296				int32 * cookie,
297				media_output * out_output)
298{
299	CALLED();
300
301	if (*cookie != 0) {
302		PRINT("\t<- B_ERROR (no more outputs)\n");
303		return B_ERROR;
304	}
305
306	// so next time they won't get the same output again
307	*cookie = 1;
308	*out_output = output;
309	return B_OK;
310}
311
312
313status_t MediaReader::DisposeOutputCookie(
314				int32 cookie)
315{
316	CALLED();
317	// nothing to do since our cookies are just integers
318	return B_OK;
319}
320
321
322status_t MediaReader::SetBufferGroup(
323				const media_source & for_source,
324				BBufferGroup * group)
325{
326	CALLED();
327
328	if (output.source != for_source) {
329		PRINT("\t<- B_MEDIA_BAD_SOURCE\n");
330		return B_MEDIA_BAD_SOURCE; // we only have one output so that better be it
331	}
332	if (fBufferGroup != 0) {
333		if (fBufferGroup == group)
334			return B_OK; // time saver
335		delete fBufferGroup;
336	}
337	if (group != 0) {
338		fBufferGroup = group;
339	} else {
340		// let's take advantage of this opportunity to recalculate
341		// our downstream latency and ensure that it is up to date
342		media_node_id id;
343		FindLatencyFor(output.destination, &fDownstreamLatency, &id);
344		// buffer period gets initialized in Connect() because
345		// that is the first time we get the real values for
346		// chunk size and bit rate, which are used to compute buffer period
347		// note: you can still make a buffer group before connecting (why?)
348		//       but we don't make it, you make it yourself and pass it here.
349		//       not sure why anybody would want to do that since they need
350		//       a connection anyway...
351		if (fBufferPeriod <= 0) {
352			fprintf(stderr,"<- B_NO_INIT");
353			return B_NO_INIT;
354		}
355		int32 count = int32(fDownstreamLatency/fBufferPeriod)+2;
356		PRINT("\tdownstream latency = %lld, buffer period = %lld, buffer count = %ld\n",
357				fDownstreamLatency, fBufferPeriod, count);
358
359		// allocate the buffers
360		fBufferGroup = new BBufferGroup(output.format.u.multistream.max_chunk_size,count);
361		if (fBufferGroup == 0) {
362			PRINT("\t<- B_NO_MEMORY\n");
363			return B_NO_MEMORY;
364		}
365		status_t status = fBufferGroup->InitCheck();
366		if (status != B_OK) {
367			PRINT("\t<- fBufferGroup initialization failed\n");
368			return status;
369		}
370	}
371	return B_OK;
372}
373
374
375/* Format of clipping is (as int16-s): <from line> <npairs> <startclip> <endclip>. */
376/* Repeat for each line where the clipping is different from the previous line. */
377/* If <npairs> is negative, use the data from line -<npairs> (there are 0 pairs after */
378/* a negative <npairs>. Yes, we only support 32k*32k frame buffers for clipping. */
379/* Any non-0 field of 'display' means that that field changed, and if you don't support */
380/* that change, you should return an error and ignore the request. Note that the buffer */
381/* offset values do not have wildcards; 0 (or -1, or whatever) are real values and must */
382/* be adhered to. */
383status_t MediaReader::VideoClippingChanged(
384				const media_source & for_source,
385				int16 num_shorts,
386				int16 * clip_data,
387				const media_video_display_info & display,
388				int32 * _deprecated_)
389{
390	return BBufferProducer::VideoClippingChanged(for_source,num_shorts,clip_data,display,_deprecated_);
391}
392
393
394status_t MediaReader::GetLatency(
395				bigtime_t * out_latency)
396{
397	CALLED();
398
399	*out_latency = EventLatency() + SchedulingLatency();
400	return B_OK;
401}
402
403
404status_t MediaReader::PrepareToConnect(
405				const media_source & what,
406				const media_destination & where,
407				media_format * format,
408				media_source * out_source,
409				char * out_name)
410{
411	CALLED();
412
413	if (output.source != what) {
414		PRINT("\t<- B_MEDIA_BAD_SOURCE\n");
415		return B_MEDIA_BAD_SOURCE;
416	}
417	if (output.destination != media_destination::null) {
418		PRINT("\t<- B_MEDIA_ALREADY_CONNECTED\n");
419		return B_MEDIA_ALREADY_CONNECTED;
420	}
421
422	status_t status = FormatChangeRequested(output.source,where,format,0);
423	if (status != B_OK) {
424		PRINT("\t<- MediaReader::FormatChangeRequested failed\n");
425		return status;
426	}
427
428	// last check for wildcards and general validity
429	if (format->type != B_MEDIA_MULTISTREAM) {
430		PRINT("\t<- B_MEDIA_BAD_FORMAT\n");
431		return B_MEDIA_BAD_FORMAT;
432	}
433
434	*out_source = output.source;
435	output.destination = where;
436	strncpy(out_name,output.name,B_MEDIA_NAME_LENGTH-1);
437	out_name[B_MEDIA_NAME_LENGTH] = '\0';
438	return ResolveWildcards(format);
439}
440
441
442void MediaReader::Connect(
443				status_t error,
444				const media_source & source,
445				const media_destination & destination,
446				const media_format & format,
447				char * io_name)
448{
449	CALLED();
450
451	if (error != B_OK) {
452		PRINT("\t<- error already\n");
453		output.destination = media_destination::null;
454		GetFormat(&output.format);
455		return;
456	}
457	if (output.source != source) {
458		PRINT("\t<- B_MEDIA_BAD_SOURCE\n");
459		output.destination = media_destination::null;
460		GetFormat(&output.format);
461		return;
462	}
463
464	// record the agreed upon values
465	output.destination = destination;
466	output.format = format;
467	strncpy(io_name,output.name,B_MEDIA_NAME_LENGTH-1);
468	io_name[B_MEDIA_NAME_LENGTH-1] = '\0';
469
470	// determine our downstream latency
471	media_node_id id;
472	FindLatencyFor(output.destination, &fDownstreamLatency, &id);
473
474	// compute the buffer period (must be done before setbuffergroup)
475	fBufferPeriod = bigtime_t(1000u * 8000000u / 1024u
476	                     * output.format.u.multistream.max_chunk_size
477			             / output.format.u.multistream.max_bit_rate);
478
479	PRINT("\tmax chunk size = %ld, max bit rate = %f, buffer period = %lld\n",
480			output.format.u.multistream.max_chunk_size,
481			output.format.u.multistream.max_bit_rate,fBufferPeriod);
482
483	// setup the buffers if they aren't setup yet
484	if (fBufferGroup == 0) {
485		status_t status = SetBufferGroup(output.source,0);
486		if (status != B_OK) {
487			PRINT("\t<- SetBufferGroup failed\n");
488			output.destination = media_destination::null;
489			GetFormat(&output.format);
490			return;
491		}
492	}
493
494	SetBufferDuration(fBufferPeriod);
495
496	if (GetCurrentFile() != 0) {
497		bigtime_t start, end;
498		// buffer group buffer size
499		uint8 * data = new uint8[output.format.u.multistream.max_chunk_size];
500		BBuffer * buffer = 0;
501		ssize_t bytesRead = 0;
502		{ // timed section
503			start = TimeSource()->RealTime();
504			// first we try to use a real BBuffer
505			buffer = fBufferGroup->RequestBuffer(
506					output.format.u.multistream.max_chunk_size,fBufferPeriod);
507			if (buffer != 0) {
508				FillFileBuffer(buffer);
509			} else {
510				// didn't get a real BBuffer, try simulation by just a read from the disk
511				bytesRead = GetCurrentFile()->Read(
512						data, output.format.u.multistream.max_chunk_size);
513			}
514			end = TimeSource()->RealTime();
515		}
516		bytesRead = buffer->SizeUsed();
517		delete[] data;
518		if (buffer != 0) {
519			buffer->Recycle();
520		}
521		GetCurrentFile()->Seek(-bytesRead,SEEK_CUR); // put it back where we found it
522
523		fInternalLatency = end - start;
524
525		PRINT("\tinternal latency from disk read = %lld\n", fInternalLatency);
526	} else {
527		fInternalLatency = 100; // just guess
528		PRINT("\tinternal latency guessed = %lld\n", fInternalLatency);
529	}
530
531	SetEventLatency(fDownstreamLatency + fInternalLatency);
532
533	// XXX: do anything else?
534}
535
536
537void MediaReader::Disconnect(
538				const media_source & what,
539				const media_destination & where)
540{
541	CALLED();
542
543	if (output.destination != where) {
544		PRINT("\t<- B_MEDIA_BAD_DESTINATION\n");
545		return;
546	}
547	if (output.source != what) {
548		PRINT("\t<- B_MEDIA_BAD_SOURCE\n");
549		return;
550	}
551
552	output.destination = media_destination::null;
553	GetFormat(&output.format);
554	if (fBufferGroup != 0) {
555		BBufferGroup * group = fBufferGroup;
556		fBufferGroup = 0;
557		delete group;
558	}
559}
560
561
562void MediaReader::LateNoticeReceived(
563				const media_source & what,
564				bigtime_t how_much,
565				bigtime_t performance_time)
566{
567	CALLED();
568
569	if (what == output.source) {
570		switch (RunMode()) {
571			case B_OFFLINE:
572			    // nothing to do
573				break;
574			case B_RECORDING:
575			    // nothing to do
576				break;
577			case B_INCREASE_LATENCY:
578				fInternalLatency += how_much;
579				SetEventLatency(fDownstreamLatency + fInternalLatency);
580				break;
581			case B_DECREASE_PRECISION:
582				// XXX : shorten our buffer period
583				//       We could opt to just not wait but we should
584				//       probably gradually shorten the period so we
585				//       don't starve others.  Also, we need to make
586				//       sure we are catching up!  We may have some sort
587				//       of time goal for how long it takes us to
588				//       catch up, as well.
589				break;
590			case B_DROP_DATA:
591				// Okay you asked for it, we'll skip ahead in the file!
592				// We'll drop 1 buffer's worth
593				if (GetCurrentFile() == 0) {
594					PRINT("MediaReader::LateNoticeReceived called without"
595						  "an GetCurrentFile() (!)\n");
596				} else {
597					GetCurrentFile()->Seek(output.format.u.multistream.max_chunk_size,SEEK_CUR);
598				}
599				break;
600			default:
601				// huh?? there aren't any more run modes.
602				PRINT("MediaReader::LateNoticeReceived with unexpected run mode.\n");
603				break;
604		}
605	}
606}
607
608
609void MediaReader::EnableOutput(
610				const media_source & what,
611				bool enabled,
612				int32 * _deprecated_)
613{
614	CALLED();
615
616	if (output.source != what) {
617		PRINT("\t<- B_MEDIA_BAD_SOURCE\n");
618		return;
619	}
620
621	fOutputEnabled = enabled;
622}
623
624
625status_t MediaReader::SetPlayRate(
626				int32 numer,
627				int32 denom)
628{
629	return BBufferProducer::SetPlayRate(numer,denom); // XXX: do something intelligent later
630}
631
632
633void MediaReader::AdditionalBufferRequested(			//	used to be Reserved 0
634				const media_source & source,
635				media_buffer_id prev_buffer,
636				bigtime_t prev_time,
637				const media_seek_tag * prev_tag)
638{
639	CALLED();
640
641	if (output.source == source) {
642		BBuffer * buffer;
643		status_t status = GetFilledBuffer(&buffer);
644		if (status != B_OK) {
645			PRINT("MediaReader::AdditionalBufferRequested got an error from GetFilledBuffer.\n");
646			return; // don't send the buffer
647		}
648		SendBuffer(buffer, output.source, output.destination);
649	}
650}
651
652
653void MediaReader::LatencyChanged(
654				const media_source & source,
655				const media_destination & destination,
656				bigtime_t new_latency,
657				uint32 flags)
658{
659	CALLED();
660	if ((output.source == source) && (output.destination == destination)) {
661		fDownstreamLatency = new_latency;
662		SetEventLatency(fDownstreamLatency + fInternalLatency);
663	}
664	// we may have to recompute the number of buffers that we are using
665	// see SetBufferGroup
666}
667
668
669// -------------------------------------------------------- //
670// implementation for BMediaEventLooper
671// -------------------------------------------------------- //
672// protected:
673status_t MediaReader::HandleBuffer(
674				const media_timed_event *event,
675				bigtime_t lateness,
676				bool realTimeEvent)
677{
678	CALLED();
679
680	if (output.destination == media_destination::null)
681		return B_MEDIA_NOT_CONNECTED;
682
683	status_t status = B_OK;
684	BBuffer * buffer = fBufferGroup->RequestBuffer(output.format.u.multistream.max_chunk_size,fBufferPeriod);
685	if (buffer != 0) {
686	    status = FillFileBuffer(buffer);
687	    if (status != B_OK) {
688			PRINT("MediaReader::HandleEvent got an error from FillFileBuffer.\n");
689			buffer->Recycle();
690		} else {
691			if (fOutputEnabled) {
692				status = SendBuffer(buffer, output.source, output.destination);
693				if (status != B_OK) {
694					PRINT("MediaReader::HandleEvent got an error from SendBuffer.\n");
695					buffer->Recycle();
696				}
697			} else {
698				buffer->Recycle();
699			}
700		}
701	}
702	bigtime_t nextEventTime = event->event_time+fBufferPeriod;
703	media_timed_event nextBufferEvent(nextEventTime, BTimedEventQueue::B_HANDLE_BUFFER);
704	EventQueue()->AddEvent(nextBufferEvent);
705	return status;
706}
707
708
709status_t MediaReader::HandleDataStatus(
710						const media_timed_event *event,
711						bigtime_t lateness,
712						bool realTimeEvent)
713{
714	CALLED();
715	return SendDataStatus(event->data,output.destination,event->event_time);
716}
717
718
719// -------------------------------------------------------- //
720// MediaReader specific functions
721// -------------------------------------------------------- //
722// static:
723void MediaReader::GetFlavor(flavor_info * outInfo, int32 id)
724{
725	CALLED();
726
727	if (outInfo == 0)
728		return;
729
730	AbstractFileInterfaceNode::GetFlavor(outInfo,id);
731	outInfo->name = strdup("Media Reader");
732	outInfo->info = strdup(
733		"The Haiku Media Reader reads a file and produces a multistream.");
734	outInfo->kinds |= B_BUFFER_PRODUCER;
735	outInfo->out_format_count = 1; // 1 output
736	media_format * formats = new media_format[outInfo->out_format_count];
737	GetFormat(&formats[0]);
738	outInfo->out_formats = formats;
739	return;
740}
741
742
743void MediaReader::GetFormat(media_format * outFormat)
744{
745	CALLED();
746
747	AbstractFileInterfaceNode::GetFormat(outFormat);
748	return;
749}
750
751
752void MediaReader::GetFileFormat(media_file_format * outFileFormat)
753{
754	CALLED();
755
756	AbstractFileInterfaceNode::GetFileFormat(outFileFormat);
757	outFileFormat->capabilities |= media_file_format::B_READABLE;
758	return;
759}
760
761
762// protected:
763status_t MediaReader::GetFilledBuffer(
764				BBuffer ** outBuffer)
765{
766	CALLED();
767
768	BBuffer * buffer = fBufferGroup->RequestBuffer(output.format.u.multistream.max_chunk_size,-1);
769	if (buffer == 0) {
770		// XXX: add a new buffer and get it
771		PRINT("MediaReader::GetFilledBuffer needs a new buffer.\n");
772		return B_ERROR; // don't send the buffer
773	}
774
775	status_t status = FillFileBuffer(buffer);
776	*outBuffer = buffer;
777	return status;
778}
779
780
781status_t MediaReader::FillFileBuffer(
782				BBuffer * buffer)
783{
784	CALLED();
785
786	if (GetCurrentFile() == 0) {
787		PRINT("\t<- B_NO_INIT\n");
788		return B_NO_INIT;
789	}
790	PRINT("\t%ld buffer bytes used, %ld buffer bytes available\n",
791			buffer->SizeUsed(), buffer->SizeAvailable());
792	off_t position = GetCurrentFile()->Position();
793	ssize_t bytesRead = GetCurrentFile()->Read(buffer->Data(),buffer->SizeAvailable());
794	if (bytesRead < 0) {
795		PRINT("\t<- B_FILE_ERROR\n");
796		return B_FILE_ERROR; // some sort of file related error
797	}
798	PRINT("\t%ld file bytes read at position %ld.\n",
799			bytesRead, position);
800
801	buffer->SetSizeUsed(bytesRead);
802	media_header * header = buffer->Header();
803	header->type = B_MEDIA_MULTISTREAM;
804	header->size_used = bytesRead;
805	header->file_pos = position;
806	header->orig_size = bytesRead;
807	header->time_source = TimeSource()->ID();
808	header->start_time = TimeSource()->Now();
809	// nothing more to say?
810	return B_OK;
811}
812
813
814// -------------------------------------------------------- //
815// stuffing
816// -------------------------------------------------------- //
817status_t MediaReader::_Reserved_MediaReader_0(void *) { return B_ERROR; }
818status_t MediaReader::_Reserved_MediaReader_1(void *) { return B_ERROR; }
819status_t MediaReader::_Reserved_MediaReader_2(void *) { return B_ERROR; }
820status_t MediaReader::_Reserved_MediaReader_3(void *) { return B_ERROR; }
821status_t MediaReader::_Reserved_MediaReader_4(void *) { return B_ERROR; }
822status_t MediaReader::_Reserved_MediaReader_5(void *) { return B_ERROR; }
823status_t MediaReader::_Reserved_MediaReader_6(void *) { return B_ERROR; }
824status_t MediaReader::_Reserved_MediaReader_7(void *) { return B_ERROR; }
825status_t MediaReader::_Reserved_MediaReader_8(void *) { return B_ERROR; }
826status_t MediaReader::_Reserved_MediaReader_9(void *) { return B_ERROR; }
827status_t MediaReader::_Reserved_MediaReader_10(void *) { return B_ERROR; }
828status_t MediaReader::_Reserved_MediaReader_11(void *) { return B_ERROR; }
829status_t MediaReader::_Reserved_MediaReader_12(void *) { return B_ERROR; }
830status_t MediaReader::_Reserved_MediaReader_13(void *) { return B_ERROR; }
831status_t MediaReader::_Reserved_MediaReader_14(void *) { return B_ERROR; }
832status_t MediaReader::_Reserved_MediaReader_15(void *) { return B_ERROR; }
833