xref: /haiku/src/add-ons/media/media-add-ons/reader/MediaReader.cpp (revision 30456a328a4f7e8c71893bb14a50c488009daa53)
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 
~MediaReader(void)38 MediaReader::~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 
MediaReader(size_t defaultChunkSize,float defaultBitRate,const flavor_info * info,BMessage * config,BMediaAddOn * addOn)49 MediaReader::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 // -------------------------------------------------------- //
Preroll(void)79 void MediaReader::Preroll(void)
80 {
81 	CALLED();
82 	// XXX:Performance opportunity
83 	BMediaNode::Preroll();
84 }
85 
86 
HandleMessage(int32 message,const void * data,size_t size)87 status_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 
NodeRegistered(void)111 void 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 // -------------------------------------------------------- //
SetRef(const entry_ref & file,bool create,bigtime_t * out_time)128 status_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)
FormatSuggestionRequested(media_type type,int32 quality,media_format * format)217 status_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.
FormatProposal(const media_source & output_source,media_format * format)238 status_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.
FormatChangeRequested(const media_source & source,const media_destination & destination,media_format * io_format,int32 * _deprecated_)272 status_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 
GetNextOutput(int32 * cookie,media_output * out_output)295 status_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 
DisposeOutputCookie(int32 cookie)313 status_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 
SetBufferGroup(const media_source & for_source,BBufferGroup * group)322 status_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. */
VideoClippingChanged(const media_source & for_source,int16 num_shorts,int16 * clip_data,const media_video_display_info & display,int32 * _deprecated_)383 status_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 
GetLatency(bigtime_t * out_latency)394 status_t MediaReader::GetLatency(
395 				bigtime_t * out_latency)
396 {
397 	CALLED();
398 
399 	*out_latency = EventLatency() + SchedulingLatency();
400 	return B_OK;
401 }
402 
403 
PrepareToConnect(const media_source & what,const media_destination & where,media_format * format,media_source * out_source,char * out_name)404 status_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 
Connect(status_t error,const media_source & source,const media_destination & destination,const media_format & format,char * io_name)442 void 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 
Disconnect(const media_source & what,const media_destination & where)537 void 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 
LateNoticeReceived(const media_source & what,bigtime_t how_much,bigtime_t performance_time)562 void 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 
EnableOutput(const media_source & what,bool enabled,int32 * _deprecated_)609 void 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 
SetPlayRate(int32 numer,int32 denom)625 status_t MediaReader::SetPlayRate(
626 				int32 numer,
627 				int32 denom)
628 {
629 	return BBufferProducer::SetPlayRate(numer,denom); // XXX: do something intelligent later
630 }
631 
632 
AdditionalBufferRequested(const media_source & source,media_buffer_id prev_buffer,bigtime_t prev_time,const media_seek_tag * prev_tag)633 void 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 
LatencyChanged(const media_source & source,const media_destination & destination,bigtime_t new_latency,uint32 flags)653 void 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:
HandleBuffer(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)673 status_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 
HandleDataStatus(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)709 status_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:
GetFlavor(flavor_info * outInfo,int32 id)723 void 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 
GetFormat(media_format * outFormat)743 void MediaReader::GetFormat(media_format * outFormat)
744 {
745 	CALLED();
746 
747 	AbstractFileInterfaceNode::GetFormat(outFormat);
748 	return;
749 }
750 
751 
GetFileFormat(media_file_format * outFileFormat)752 void 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:
GetFilledBuffer(BBuffer ** outBuffer)763 status_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 
FillFileBuffer(BBuffer * buffer)781 status_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 // -------------------------------------------------------- //
_Reserved_MediaReader_0(void *)817 status_t MediaReader::_Reserved_MediaReader_0(void *) { return B_ERROR; }
_Reserved_MediaReader_1(void *)818 status_t MediaReader::_Reserved_MediaReader_1(void *) { return B_ERROR; }
_Reserved_MediaReader_2(void *)819 status_t MediaReader::_Reserved_MediaReader_2(void *) { return B_ERROR; }
_Reserved_MediaReader_3(void *)820 status_t MediaReader::_Reserved_MediaReader_3(void *) { return B_ERROR; }
_Reserved_MediaReader_4(void *)821 status_t MediaReader::_Reserved_MediaReader_4(void *) { return B_ERROR; }
_Reserved_MediaReader_5(void *)822 status_t MediaReader::_Reserved_MediaReader_5(void *) { return B_ERROR; }
_Reserved_MediaReader_6(void *)823 status_t MediaReader::_Reserved_MediaReader_6(void *) { return B_ERROR; }
_Reserved_MediaReader_7(void *)824 status_t MediaReader::_Reserved_MediaReader_7(void *) { return B_ERROR; }
_Reserved_MediaReader_8(void *)825 status_t MediaReader::_Reserved_MediaReader_8(void *) { return B_ERROR; }
_Reserved_MediaReader_9(void *)826 status_t MediaReader::_Reserved_MediaReader_9(void *) { return B_ERROR; }
_Reserved_MediaReader_10(void *)827 status_t MediaReader::_Reserved_MediaReader_10(void *) { return B_ERROR; }
_Reserved_MediaReader_11(void *)828 status_t MediaReader::_Reserved_MediaReader_11(void *) { return B_ERROR; }
_Reserved_MediaReader_12(void *)829 status_t MediaReader::_Reserved_MediaReader_12(void *) { return B_ERROR; }
_Reserved_MediaReader_13(void *)830 status_t MediaReader::_Reserved_MediaReader_13(void *) { return B_ERROR; }
_Reserved_MediaReader_14(void *)831 status_t MediaReader::_Reserved_MediaReader_14(void *) { return B_ERROR; }
_Reserved_MediaReader_15(void *)832 status_t MediaReader::_Reserved_MediaReader_15(void *) { return B_ERROR; }
833