xref: /haiku/src/add-ons/media/media-add-ons/AbstractFileInterfaceNode.cpp (revision cda5b8808fd0262f0fac472f6cfa809f846a83cf)
1 // AbstractFileInterfaceNode.cpp
2 //
3 // Andrew Bachmann, 2002
4 //
5 // The AbstractFileInterfaceNode class implements
6 // the common functionality between MediaReader
7 // and MediaWriter.
8 #include "AbstractFileInterfaceNode.h"
9 #include "debug.h"
10 
11 #include <Buffer.h>
12 #include <Controllable.h>
13 #include <Entry.h>
14 #include <Errors.h>
15 #include <File.h>
16 #include <FileInterface.h>
17 #include <MediaDefs.h>
18 #include <MediaEventLooper.h>
19 #include <MediaNode.h>
20 #include <TimeSource.h>
21 #include <ParameterWeb.h>
22 #include <limits.h>
23 #include <stdio.h>
24 #include <string.h>
25 
26 
27 AbstractFileInterfaceNode::~AbstractFileInterfaceNode(void)
28 {
29 	CALLED();
30 
31 	// Stop the BMediaEventLooper thread
32 	Quit();
33 	if (fCurrentFile != 0)
34 		delete fCurrentFile;
35 }
36 
37 
38 AbstractFileInterfaceNode::AbstractFileInterfaceNode(
39 				size_t defaultChunkSize,
40 				float defaultBitRate,
41 				const flavor_info * info,
42 				BMessage * config,
43 				BMediaAddOn * addOn)
44 	: BMediaNode("AbstractFileInterfaceNode"),
45 	  BFileInterface(),
46 	  BControllable(),
47 	  BMediaEventLooper()
48 {
49 	CALLED();
50 
51 	// keep our creator around for AddOn calls later
52 	fAddOn = addOn;
53 	// null some fields
54 	fCurrentFile = 0;
55 	f_current_mime_type[0] = '\0';
56 
57 	// initialize the parameters
58 	if (defaultChunkSize <= 0) {
59 		fInitCheckStatus = B_BAD_VALUE;
60 		return;
61 	}
62 
63 	fDefaultChunkSizeParam = defaultChunkSize;
64 	fDefaultChunkSizeParamChangeTime = 0;
65 	if (defaultBitRate <= 0) {
66 		fInitCheckStatus = B_BAD_VALUE;
67 		return;
68 	}
69 
70 	fDefaultBitRateParam = defaultBitRate;
71 	fDefaultBitRateParamChangeTime = 0;
72 	// initialize parameter compute fields
73 	fLeastRecentlyUpdatedParameter = DEFAULT_BIT_RATE_PARAM;
74 	fLastUpdatedParameter = DEFAULT_BUFFER_PERIOD_PARAM;
75 	// From the chunk size and bit rate we compute the buffer period.
76 	int64 value = int64(8000000/1024*fDefaultChunkSizeParam/fDefaultBitRateParam);
77 	if ((value <= 0) || (value > INT_MAX)) {
78 		fInitCheckStatus = B_BAD_VALUE;
79 		return;
80 	}
81 	fDefaultBufferPeriodParam = int32(value);
82 	fDefaultBufferPeriodParamChangeTime = 0;
83 
84 	fInitCheckStatus = B_OK;
85 }
86 
87 
88 status_t AbstractFileInterfaceNode::InitCheck(void) const
89 {
90 	CALLED();
91 	return fInitCheckStatus;
92 }
93 
94 
95 status_t AbstractFileInterfaceNode::GetConfigurationFor(
96 				BMessage * into_message)
97 {
98 	CALLED();
99 	return B_OK;
100 }
101 
102 
103 // -------------------------------------------------------- //
104 // implementation of BMediaNode
105 // -------------------------------------------------------- //
106 BMediaAddOn * AbstractFileInterfaceNode::AddOn(
107 				int32 * internal_id) const
108 {
109 	CALLED();
110 	// BeBook says this only gets called if we were in an add-on.
111 	if (fAddOn != 0) {
112 		// If we get a null pointer then we just won't write.
113 		if (internal_id != 0)
114 			internal_id = 0;
115 	}
116 	return fAddOn;
117 }
118 
119 
120 void AbstractFileInterfaceNode::Start(
121 				bigtime_t performance_time)
122 {
123 	PRINT("AbstractFileInterfaceNode::Start(pt=%lld)\n",performance_time);
124 	BMediaEventLooper::Start(performance_time);
125 }
126 
127 
128 void AbstractFileInterfaceNode::Stop(
129 				bigtime_t performance_time,
130 				bool immediate)
131 {
132 	PRINT("AbstractFileInterfaceNode::Stop(pt=%lld, im=%d)\n",
133 		  performance_time, immediate);
134 	BMediaEventLooper::Stop(performance_time,immediate);
135 }
136 
137 
138 void AbstractFileInterfaceNode::Seek(
139 				bigtime_t media_time,
140 				bigtime_t performance_time)
141 {
142 	PRINT("AbstractFileInterfaceNode::Seek(mt=%lld,pt=%lld)\n",
143 		  media_time,performance_time);
144 	BMediaEventLooper::Seek(media_time,performance_time);
145 }
146 
147 
148 void AbstractFileInterfaceNode::SetRunMode(
149 				run_mode mode)
150 {
151 	PRINT("AbstractFileInterfaceNode::SetRunMode(%i)\n",mode);
152 	BMediaEventLooper::SetRunMode(mode);
153 }
154 
155 
156 void AbstractFileInterfaceNode::TimeWarp(
157 				bigtime_t at_real_time,
158 				bigtime_t to_performance_time)
159 {
160 	PRINT("AbstractFileInterfaceNode::TimeWarp(rt=%lld,pt=%lld)\n",
161 		  at_real_time,to_performance_time);
162 	BMediaEventLooper::TimeWarp(at_real_time,to_performance_time);
163 }
164 
165 
166 void AbstractFileInterfaceNode::Preroll(void)
167 {
168 	CALLED();
169 	// XXX:Performance opportunity
170 	BMediaNode::Preroll();
171 }
172 
173 
174 void AbstractFileInterfaceNode::SetTimeSource(
175 				BTimeSource * time_source)
176 {
177 	CALLED();
178 	BMediaNode::SetTimeSource(time_source);
179 }
180 
181 
182 status_t AbstractFileInterfaceNode::HandleMessage(
183 				int32 message,
184 				const void * data,
185 				size_t size)
186 {
187 	CALLED();
188 
189 	status_t status = B_OK;
190 	switch (message) {
191 		// no special messages for now
192 		default:
193 			status = BFileInterface::HandleMessage(message,data,size);
194 			if (status == B_OK) {
195 				break;
196 			}
197 			status = BControllable::HandleMessage(message, data, size);
198 			if (status == B_OK)
199 				break;
200 			status = BMediaNode::HandleMessage(message,data,size);
201 			if (status == B_OK) {
202 				break;
203 			}
204 			BMediaNode::HandleBadMessage(message,data,size);
205 			status = B_ERROR;
206 			break;
207 	}
208 	return status;
209 }
210 
211 
212 status_t AbstractFileInterfaceNode::RequestCompleted(
213 				const media_request_info & info)
214 {
215 	CALLED();
216 	return BMediaNode::RequestCompleted(info);
217 }
218 
219 
220 status_t AbstractFileInterfaceNode::DeleteHook(
221 				BMediaNode * node)
222 {
223 	CALLED();
224 	return BMediaEventLooper::DeleteHook(node);
225 }
226 
227 
228 void AbstractFileInterfaceNode::NodeRegistered(void)
229 {
230 	CALLED();
231 
232 	// set up our parameter web
233 	SetParameterWeb(MakeParameterWeb());
234 
235 	// start the BMediaEventLooper thread
236 	SetPriority(B_REAL_TIME_PRIORITY);
237 	Run();
238 }
239 
240 
241 status_t AbstractFileInterfaceNode::GetNodeAttributes(
242 				media_node_attribute * outAttributes,
243 				size_t inMaxCount)
244 {
245 	CALLED();
246 	return BMediaNode::GetNodeAttributes(outAttributes,inMaxCount);
247 }
248 
249 
250 status_t AbstractFileInterfaceNode::AddTimer(
251 					bigtime_t at_performance_time,
252 					int32 cookie)
253 {
254 	CALLED();
255 	return BMediaEventLooper::AddTimer(at_performance_time,cookie);
256 }
257 
258 
259 // protected:
260 BParameterWeb * AbstractFileInterfaceNode::MakeParameterWeb(void)
261 {
262 	CALLED();
263 
264 	BParameterWeb * web = new BParameterWeb();
265 	BParameterGroup * mainGroup = web->MakeGroup("AbstractFileInterfaceNode Parameters");
266 
267 	// these three are related:
268 	// DEFAULT_CHUNK_SIZE_PARAM =
269 	// DEFAULT_BIT_RATE_PARAM / 1024 * DEFAULT_BUFFER_PERIOD_PARAM * 1000
270 	BParameterGroup * chunkSizeGroup = mainGroup->MakeGroup("Chunk Size Group");
271 	BContinuousParameter * chunkSizeParameter
272 	   = chunkSizeGroup->MakeContinuousParameter(
273 	     DEFAULT_CHUNK_SIZE_PARAM, B_MEDIA_MULTISTREAM,
274 		 "Chunk Size", B_GAIN, "bytes", 1024, 32*1024, 512);
275 	chunkSizeParameter->SetResponse(BContinuousParameter::B_LINEAR,1,0);
276 	chunkSizeParameter->SetValue(&fDefaultChunkSizeParam,sizeof(fDefaultChunkSizeParam),0);
277 
278 	BParameterGroup * bitRateGroup = mainGroup->MakeGroup("Bit Rate Group");
279 	BContinuousParameter * bitRateParameter
280 	   = bitRateGroup->MakeContinuousParameter(
281 	     DEFAULT_BIT_RATE_PARAM, B_MEDIA_MULTISTREAM,
282 	     "Bit Rate", B_GAIN, "kbits/sec", 1, 320000, 1);
283 	bitRateParameter->SetResponse(BContinuousParameter::B_LINEAR,.001,0);
284 	bitRateParameter->SetValue(&fDefaultBitRateParam,sizeof(fDefaultBitRateParam),0);
285 
286 	BParameterGroup * bufferPeriodGroup = mainGroup->MakeGroup("Buffer Period Group");
287 	BContinuousParameter * bufferPeriodParameter
288 	   = bufferPeriodGroup->MakeContinuousParameter(
289 	     DEFAULT_BUFFER_PERIOD_PARAM, B_MEDIA_MULTISTREAM,
290 	     "Buffer Period", B_GAIN, "ms", 1, 10000, 1);
291 	bufferPeriodParameter->SetResponse(BContinuousParameter::B_LINEAR,1,0);
292 	bufferPeriodParameter->SetValue(&fDefaultBufferPeriodParam,sizeof(fDefaultBufferPeriodParam),0);
293 
294 	return web;
295 }
296 
297 
298 // -------------------------------------------------------- //
299 // implementation of BFileInterface
300 // -------------------------------------------------------- //
301 status_t AbstractFileInterfaceNode::GetNextFileFormat(
302 				int32 * cookie,
303 				media_file_format * out_format)
304 {
305 	CALLED();
306 
307 	// avoid crashes
308 	if (out_format == 0) {
309 		// no place to write!
310 		PRINT("\t<- B_BAD_VALUE\n");
311 		return B_BAD_VALUE;
312 	}
313 
314 	if (cookie != 0) {
315 		// it's valid but they already got our 1 file format
316 		if (*cookie != 0) {
317 			PRINT("\t<- B_ERROR\n");
318 			return B_ERROR;
319 		}
320 		// so next time they won't get the same format again
321 		*cookie = 1;
322 	}
323 
324 	GetFileFormat(out_format);
325 	return B_OK;
326 }
327 
328 
329 void AbstractFileInterfaceNode::DisposeFileFormatCookie(
330 				int32 cookie)
331 {
332 	CALLED();
333 	// nothing to do since our cookies are just integers
334 }
335 
336 
337 status_t AbstractFileInterfaceNode::GetDuration(
338 				bigtime_t * out_time)
339 {
340 	CALLED();
341 	if (out_time == 0) {
342 		PRINT("\t<- B_BAD_VALUE\n");
343 		return B_BAD_VALUE;
344 	}
345 
346 	if (fCurrentFile == 0) {
347 		PRINT("\t<- B_NO_INIT\n");
348 		return B_NO_INIT;
349 	}
350 
351 	return fCurrentFile->GetSize(out_time);
352 }
353 
354 
355 status_t AbstractFileInterfaceNode::SniffRef(
356 				const entry_ref & file,
357 				char * out_mime_type,	/* 256 bytes */
358 				float * out_quality)
359 {
360 	CALLED();
361 	return StaticSniffRef(file,out_mime_type,out_quality);
362 }
363 
364 
365 status_t AbstractFileInterfaceNode::SetRef(
366 				const entry_ref & file,
367 				uint32 openMode,
368 				bool create,
369 				bigtime_t * out_time)
370 {
371 	CALLED();
372 
373 	if (out_time == 0) {
374 		PRINT("\t<- B_BAD_VALUE\n");
375 		return B_BAD_VALUE; // no crashes today thanks
376 	}
377 
378 	status_t status;
379 	f_current_ref = file;
380 	if (fCurrentFile == 0) {
381 		fCurrentFile = new BFile(&f_current_ref,(openMode|(create?B_CREATE_FILE:0)));
382 		status = fCurrentFile->InitCheck();
383 	} else {
384 		status = fCurrentFile->SetTo(&f_current_ref,(openMode|(create?B_CREATE_FILE:0)));
385 	}
386 
387 	if (status != B_OK) {
388 		PRINT("\t<- failed BFile initialization\n");
389 		return status;
390 	}
391 
392 	// cache the mime type for later
393 	fCurrentFile->ReadAttr("BEOS:TYPE",0,0,f_current_mime_type,B_MIME_TYPE_LENGTH);
394 	// compute the duration and return any error
395 	return GetDuration(out_time);
396 }
397 
398 
399 status_t AbstractFileInterfaceNode::GetRef(
400 				entry_ref * out_ref,
401 				char * out_mime_type)
402 {
403 	CALLED();
404 
405 	if ((out_ref == 0) || (out_mime_type == 0)) {
406 		PRINT("\t<- B_BAD_VALUE\n");
407 		return B_BAD_VALUE; // avoid crash
408 	}
409 
410 	if (fCurrentFile == 0) {
411 		PRINT("\t<- B_NO_INIT\n");
412 		return B_NO_INIT; // the input_ref isn't valid yet either
413 	}
414 
415 	*out_ref = f_current_ref;
416 	// they hopefully allocated enough space (no way to check :-/ )
417 	strcpy(out_mime_type,f_current_mime_type);
418 	return B_OK;
419 }
420 
421 
422 // provided for BAbstractFileInterfaceNodeAddOn
423 status_t AbstractFileInterfaceNode::StaticSniffRef(
424 				const entry_ref & file,
425 				char * out_mime_type,	/* 256 bytes */
426 				float * out_quality)
427 {
428 	CALLED();
429 
430 	if ((out_mime_type == 0) || (out_quality == 0)) {
431 		PRINT("\t<- B_BAD_VALUE\n");
432 		return B_BAD_VALUE; // avoid crash
433 	}
434 
435 	BNode node(&file);
436 	status_t initCheck = node.InitCheck();
437 	if (initCheck != B_OK) {
438 		PRINT("\t<- failed BNode::InitCheck()\n");
439 		return initCheck;
440 	}
441 
442 	// they hopefully allocated enough room
443 	node.ReadAttr("BEOS:TYPE",0,0,out_mime_type,B_MIME_TYPE_LENGTH);
444 	*out_quality = 1.0; // we handle all files perfectly!  we are so amazing!
445 	return B_OK;
446 }
447 
448 
449 // -------------------------------------------------------- //
450 // implementation for BControllable
451 // -------------------------------------------------------- //
452 const int32 AbstractFileInterfaceNode::DEFAULT_CHUNK_SIZE_PARAM = 1;
453 const int32 AbstractFileInterfaceNode::DEFAULT_BIT_RATE_PARAM = 2;
454 const int32 AbstractFileInterfaceNode::DEFAULT_BUFFER_PERIOD_PARAM = 3;
455 
456 
457 status_t AbstractFileInterfaceNode::GetParameterValue(
458 				int32 id,
459 				bigtime_t * last_change,
460 				void * value,
461 				size_t * ioSize)
462 {
463 	CALLED();
464 
465 	if ((last_change == 0) || (value == 0) || (ioSize == 0))
466 		return B_BAD_VALUE; // no crashing
467 
468 	switch (id) {
469 		case DEFAULT_CHUNK_SIZE_PARAM:
470 			if (*ioSize < sizeof(size_t)) {
471 				return B_ERROR; // not enough room
472 			}
473 			*last_change = fDefaultChunkSizeParamChangeTime;
474 			*((size_t*)value) = fDefaultChunkSizeParam;
475 			*ioSize = sizeof(size_t);
476 			break;
477 
478 		case DEFAULT_BIT_RATE_PARAM:
479 			if (*ioSize < sizeof(float)) {
480 				return B_ERROR; // not enough room
481 			}
482 			*last_change = fDefaultBitRateParamChangeTime;
483 			*((float*)value) = fDefaultBitRateParam;
484 			*ioSize = sizeof(float);
485 			break;
486 
487 		case DEFAULT_BUFFER_PERIOD_PARAM:
488 			if (*ioSize < sizeof(int32)) {
489 				return B_ERROR; // not enough room
490 			}
491 			*last_change = fDefaultBufferPeriodParamChangeTime;
492 			*((int32*)value) = fDefaultBufferPeriodParam;
493 			*ioSize = sizeof(int32);
494 			break;
495 
496 		default:
497 			PRINT("AbstractFileInterfaceNode::GetParameterValue unknown id (%ld)\n",id);
498 			return B_ERROR;
499 	}
500 
501 	return B_OK;
502 }
503 
504 
505 void AbstractFileInterfaceNode::SetParameterValue(
506 				int32 id,
507 				bigtime_t when,
508 				const void * value,
509 				size_t size)
510 {
511 	PRINT("AbstractFileInterfaceNode::SetParameterValue(id=%ld,when=%lld,size=%ld)\n",id,when,int32(size));
512 
513 	switch (id) {
514 		case DEFAULT_CHUNK_SIZE_PARAM:
515 		case DEFAULT_BIT_RATE_PARAM:
516 		case DEFAULT_BUFFER_PERIOD_PARAM:
517 			{
518 				media_timed_event event(when, BTimedEventQueue::B_PARAMETER,
519 										NULL, BTimedEventQueue::B_NO_CLEANUP,
520 										size, id, (char*) value, size);
521 				EventQueue()->AddEvent(event);
522 			}
523 			break;
524 
525 		default:
526 			PRINT("AbstractFileInterfaceNode::SetParameterValue unknown id (%ld)\n",id);
527 			break;
528 	}
529 }
530 
531 
532 // the default implementation should call the add-on main()
533 status_t AbstractFileInterfaceNode::StartControlPanel(
534 				BMessenger * out_messenger)
535 {
536 	CALLED();
537 	return BControllable::StartControlPanel(out_messenger);
538 }
539 
540 
541 // -------------------------------------------------------- //
542 // implementation for BMediaEventLooper
543 // -------------------------------------------------------- //
544 void AbstractFileInterfaceNode::HandleEvent(
545 				const media_timed_event *event,
546 				bigtime_t lateness,
547 				bool realTimeEvent)
548 {
549 	CALLED();
550 	switch (event->type) {
551 		case BTimedEventQueue::B_START:
552 			HandleStart(event,lateness,realTimeEvent);
553 			break;
554 		case BTimedEventQueue::B_SEEK:
555 			HandleSeek(event,lateness,realTimeEvent);
556 			break;
557 		case BTimedEventQueue::B_WARP:
558 			HandleWarp(event,lateness,realTimeEvent);
559 			break;
560 		case BTimedEventQueue::B_STOP:
561 			HandleStop(event,lateness,realTimeEvent);
562 			break;
563 		case BTimedEventQueue::B_HANDLE_BUFFER:
564 			if (RunState() == BMediaEventLooper::B_STARTED) {
565 				HandleBuffer(event,lateness,realTimeEvent);
566 			}
567 			break;
568 		case BTimedEventQueue::B_DATA_STATUS:
569 			HandleDataStatus(event,lateness,realTimeEvent);
570 			break;
571 		case BTimedEventQueue::B_PARAMETER:
572 			HandleParameter(event,lateness,realTimeEvent);
573 			break;
574 		default:
575 			PRINT("  unknown event type: %ld\n",event->type);
576 			break;
577 	}
578 }
579 
580 
581 /* override to clean up custom events you have added to your queue */
582 void AbstractFileInterfaceNode::CleanUpEvent(
583 				const media_timed_event *event)
584 {
585 	BMediaEventLooper::CleanUpEvent(event);
586 }
587 
588 
589 /* called from Offline mode to determine the current time of the node */
590 /* update your internal information whenever it changes */
591 bigtime_t AbstractFileInterfaceNode::OfflineTime()
592 {
593 	CALLED();
594 	return BMediaEventLooper::OfflineTime();
595 	// XXX: do something else?
596 	//	if (inputFile == 0) {
597 	//		return 0;
598 	//	} else {
599 	//		return inputFile->Position();
600 	//	}
601 }
602 
603 
604 /* override only if you know what you are doing! */
605 /* otherwise much badness could occur */
606 /* the actual control loop function: */
607 /* 	waits for messages, Pops events off the queue and calls DispatchEvent */
608 void AbstractFileInterfaceNode::ControlLoop() {
609 	BMediaEventLooper::ControlLoop();
610 }
611 
612 
613 // protected:
614 status_t AbstractFileInterfaceNode::HandleStart(
615 						const media_timed_event *event,
616 						bigtime_t lateness,
617 						bool realTimeEvent)
618 {
619 	CALLED();
620 
621 	if (RunState() != B_STARTED) {
622 		// XXX: Either use the following line or the lines that are not commented.
623 		// There doesn't seem to be a practical difference that i can tell.
624 		//		HandleBuffer(event,lateness,realTimeEvent);
625 		media_timed_event firstBufferEvent(event->event_time, BTimedEventQueue::B_HANDLE_BUFFER);
626 		HandleEvent(&firstBufferEvent, 0, false);
627 		EventQueue()->AddEvent(firstBufferEvent);
628 	}
629 	return B_OK;
630 }
631 
632 
633 status_t AbstractFileInterfaceNode::HandleSeek(
634 						const media_timed_event *event,
635 						bigtime_t lateness,
636 						bool realTimeEvent)
637 {
638 	PRINT("AbstractFileInterfaceNode::HandleSeek(t=%lld,d=%ld,bd=%lld)\n",event->event_time,event->data,event->bigdata);
639 
640 	if (fCurrentFile != 0)
641 		return fCurrentFile->Seek(event->bigdata,SEEK_SET);
642 	else
643 		return B_ERROR;
644 }
645 
646 
647 status_t AbstractFileInterfaceNode::HandleWarp(
648 						const media_timed_event *event,
649 						bigtime_t lateness,
650 						bool realTimeEvent)
651 {
652 	CALLED();
653 	return B_OK;
654 }
655 
656 
657 status_t AbstractFileInterfaceNode::HandleStop(
658 						const media_timed_event *event,
659 						bigtime_t lateness,
660 						bool realTimeEvent)
661 {
662 	CALLED();
663 
664 	// flush the queue so downstreamers don't get any more
665 	EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER);
666 	return B_OK;
667 }
668 
669 
670 status_t AbstractFileInterfaceNode::HandleParameter(
671 				const media_timed_event *event,
672 				bigtime_t lateness,
673 				bool realTimeEvent)
674 {
675 	CALLED();
676 
677 	status_t status = B_OK;
678 
679 	bool chunkSizeUpdated = false, bitRateUpdated = false, bufferPeriodUpdated = false;
680 
681 	size_t dataSize = size_t(event->data);
682 	int64 param = int64(event->bigdata);
683 
684 	switch (param) {
685 		case DEFAULT_CHUNK_SIZE_PARAM:
686 			PRINT("(DEFAULT_CHUNK_SIZE_PARAM,size=%ld",dataSize);
687 			if (dataSize < sizeof(size_t)) {
688 				PRINT("\ŧ<- B_BAD_VALUE: %lld\n",param);
689 				status = B_BAD_VALUE;
690 			} else {
691 				size_t newDefaultChunkSize = *((size_t*)event->user_data);
692 				PRINT(",%ld)\n", newDefaultChunkSize);
693 				// ignore non positive chunk sizes
694 				// XXX: we may decide later that a 0 chunk size means ship the whole file in one chunk (!)
695 				if ((newDefaultChunkSize > 0) && (newDefaultChunkSize != fDefaultChunkSizeParam)) {
696 					PRINT("\tgot a new chunk size, old chunk size was %ld\n",fDefaultChunkSizeParam);
697 					fDefaultChunkSizeParam = newDefaultChunkSize;
698 					fDefaultChunkSizeParamChangeTime = TimeSource()->Now();
699 					chunkSizeUpdated = true;
700 					if (fLeastRecentlyUpdatedParameter == DEFAULT_CHUNK_SIZE_PARAM) {
701 						// Okay we were the least recently updated parameter,
702 						// but we just got an update so we are no longer that.
703 						// Let's figure out who the new least recently updated
704 						// parameter is.  We are going to prefer to compute the
705 						// bit rate since you usually don't want to muck with
706 						// the buffer period.  However, if you just set the bitrate
707 						// then we are stuck with making the buffer period the new
708 						// parameter to be computed.
709 						if (fLastUpdatedParameter == DEFAULT_BIT_RATE_PARAM)
710 							fLeastRecentlyUpdatedParameter = DEFAULT_BUFFER_PERIOD_PARAM;
711 						else
712 							fLeastRecentlyUpdatedParameter = DEFAULT_BIT_RATE_PARAM;
713 					}
714 					// we just got an update, so we are the new lastUpdatedParameter
715 					fLastUpdatedParameter = DEFAULT_CHUNK_SIZE_PARAM;
716 					// now we have to compute the new value for the leastRecentlyUpdatedParameter
717 					// we use the chunk size change time to preserve "simultaneity" information
718 					if (fLeastRecentlyUpdatedParameter == DEFAULT_BUFFER_PERIOD_PARAM) {
719 						int64 value = int64(8000000/1024*fDefaultChunkSizeParam/fDefaultBitRateParam);
720 						if (value > INT_MAX) {
721 							// clamp to INT_MAX
722 							fDefaultBufferPeriodParam = INT_MAX;
723 							// recompute chunk size
724 							fDefaultChunkSizeParam = size_t(1024/8000000*fDefaultBitRateParam*fDefaultBufferPeriodParam);
725 						} else {
726 							fDefaultBufferPeriodParam = MAX(1,value);
727 						}
728 						fDefaultBufferPeriodParamChangeTime = fDefaultChunkSizeParamChangeTime;
729 						bufferPeriodUpdated = true;
730 					} else { // must have been bit rate
731 						fDefaultBitRateParam = MAX(0.001,8000000/1024*fDefaultChunkSizeParam/fDefaultBufferPeriodParam);
732 						fDefaultBitRateParamChangeTime = fDefaultChunkSizeParamChangeTime;
733 						bitRateUpdated = true;
734 					}
735 				}
736 			}
737 			break;
738 		case DEFAULT_BIT_RATE_PARAM:
739 			PRINT("(DEFAULT_BIT_RATE_PARAM,size=%ld",dataSize);
740 			if (dataSize < sizeof(float)) {
741 				PRINT("\t<- B_BAD_VALUE:lld\n",param);
742 				status = B_BAD_VALUE;
743 			} else {
744 				float newDefaultBitRate = *((float*)event->user_data);
745 				PRINT(",%f)\n",newDefaultBitRate);
746 				// ignore non positive bitrates
747 				if ((newDefaultBitRate > 0) && (newDefaultBitRate != fDefaultBitRateParam)) {
748 					PRINT("\tgot a new bit rate, old bit rate was %ld\n",fDefaultBitRateParam);
749 					fDefaultBitRateParam = newDefaultBitRate;
750 					fDefaultBitRateParamChangeTime = TimeSource()->Now();
751 					bitRateUpdated = true;
752 					if (fLeastRecentlyUpdatedParameter == DEFAULT_BIT_RATE_PARAM) {
753 						// Okay we were the least recently updated parameter,
754 						// but we just got an update so we are no longer that.
755 						// Let's figure out who the new least recently updated
756 						// parameter is.  We are going to prefer to compute the
757 						// chunk size since you usually don't want to muck with
758 						// the buffer period.  However, if you just set the chunk size
759 						// then we are stuck with making the buffer period the new
760 						// parameter to be computed.
761 						if (fLastUpdatedParameter == DEFAULT_CHUNK_SIZE_PARAM) {
762 							fLeastRecentlyUpdatedParameter = DEFAULT_BUFFER_PERIOD_PARAM;
763 						} else {
764 							fLeastRecentlyUpdatedParameter = DEFAULT_CHUNK_SIZE_PARAM;
765 						}
766 					}
767 					// we just got an update, so we are the new lastUpdatedParameter
768 					fLastUpdatedParameter = DEFAULT_BIT_RATE_PARAM;
769 					// now we have to compute the new value for the leastRecentlyUpdatedParameter
770 					// we use the bit rate change time to preserve "simultaneity" information
771 					if (fLeastRecentlyUpdatedParameter == DEFAULT_BUFFER_PERIOD_PARAM) {
772 						int64 value =
773 								int64(8000000/1024*fDefaultChunkSizeParam/fDefaultBitRateParam);
774 						if (value > INT_MAX) {
775 							// clamp to INT_MAX
776 							fDefaultBufferPeriodParam = INT_MAX;
777 							// recompute bit rate
778 							fDefaultBitRateParam = MAX(0.001,
779 										8000000/1024*fDefaultChunkSizeParam/fDefaultBufferPeriodParam);
780 						} else {
781 							fDefaultBufferPeriodParam = MAX(1,int32(value));
782 						}
783 						fDefaultBufferPeriodParamChangeTime = fDefaultBitRateParamChangeTime;
784 						bufferPeriodUpdated = true;
785 					} else { // must have been chunk size
786 						int64 value =
787 								int64(1024/8000000*fDefaultBitRateParam*fDefaultBufferPeriodParam);
788 						if (value > INT_MAX) {
789 							// clamp to INT_MAX
790 							fDefaultChunkSizeParam = INT_MAX;
791 							// recompute bit rate
792 							fDefaultBitRateParam =
793 									MAX(0.001,8000000/1024*fDefaultChunkSizeParam/fDefaultBufferPeriodParam);
794 						} else {
795 							fDefaultChunkSizeParam = MAX(1,int32(value));
796 						}
797 						fDefaultChunkSizeParamChangeTime = fDefaultBitRateParamChangeTime;
798 						chunkSizeUpdated = true;
799 					}
800 				}
801 			}
802 			break;
803 		case DEFAULT_BUFFER_PERIOD_PARAM:
804 			PRINT("(DEFAULT_BUFFER_PERIOD_PARAM,size=%ld",dataSize);
805 			if (dataSize < sizeof(int32)) {
806 				PRINT("\t<- B_BAD_VALUE:%ld\n",param);
807 				status = B_BAD_VALUE;
808 			} else {
809 				int32 newBufferPeriod = *((int32*)event->user_data);
810 				PRINT(",%ld)\n",newBufferPeriod);
811 				// ignore non positive buffer period
812 				if ((newBufferPeriod > 0) && (newBufferPeriod != fDefaultBufferPeriodParam)) {
813 					PRINT("\tgot a new buffer period, old buffer period was %ld\n",
814 						  fDefaultBufferPeriodParam);
815 					fDefaultBufferPeriodParam = newBufferPeriod;
816 					fDefaultBufferPeriodParamChangeTime = TimeSource()->Now();
817 					bufferPeriodUpdated = true;
818 					if (fLastUpdatedParameter == DEFAULT_BUFFER_PERIOD_PARAM) {
819 						// prefer to update bit rate, unless you just set it
820 						if (fLastUpdatedParameter == DEFAULT_BIT_RATE_PARAM) {
821 							fLeastRecentlyUpdatedParameter = DEFAULT_CHUNK_SIZE_PARAM;
822 						} else {
823 							fLeastRecentlyUpdatedParameter = DEFAULT_BIT_RATE_PARAM;
824 						}
825 					}
826 					// we just got an update, so we are the new lastUpdatedParameter
827 					fLastUpdatedParameter = DEFAULT_BUFFER_PERIOD_PARAM;
828 					// now we have to compute the new value for the leastRecentlyUpdatedParameter
829 					// we use the buffer period change time to preserve "simultaneity" information
830 					if (fLeastRecentlyUpdatedParameter == DEFAULT_BIT_RATE_PARAM) {
831 						fDefaultBitRateParam =
832 								MAX(0.001,8000000/1024*fDefaultChunkSizeParam/fDefaultBufferPeriodParam);
833 						fDefaultBitRateParamChangeTime = fDefaultBufferPeriodParamChangeTime;
834 						bitRateUpdated = true;
835 					} else { // must have been chunk size
836 						int64 value = int64(1024/8000000*fDefaultBitRateParam*fDefaultBufferPeriodParam);
837 						if (value > INT_MAX) {
838 							// clamp to INT_MAX
839 							fDefaultChunkSizeParam = INT_MAX;
840 							// recompute buffer period
841 							fDefaultBufferPeriodParam =
842 									size_t(8000000/1024*fDefaultChunkSizeParam/fDefaultBitRateParam);
843 						} else {
844 							fDefaultChunkSizeParam = MAX(1,int32(value));
845 						}
846 						fDefaultChunkSizeParamChangeTime = fDefaultBufferPeriodParamChangeTime;
847 						chunkSizeUpdated = true;
848 					}
849 				}
850 			}
851 			break;
852 		default:
853 			PRINT("AbstractFileInterfaceNode::HandleParameter called with unknown param id (%ld)\n",param);
854 			status = B_ERROR;
855 	}
856 	// send updates out for all the parameters that changed
857 	// in every case this should be two updates. (if I have not made an error :-) )
858 	if (chunkSizeUpdated) {
859 		PRINT("\tchunk size parameter updated\n");
860 		BroadcastNewParameterValue(fDefaultChunkSizeParamChangeTime,
861 								   DEFAULT_CHUNK_SIZE_PARAM,
862 								   &fDefaultChunkSizeParam,
863 								   sizeof(fDefaultChunkSizeParam));
864 	}
865 	if (bitRateUpdated) {
866 		PRINT("\tbit rate parameter updated\n");
867 		BroadcastNewParameterValue(fDefaultBitRateParamChangeTime,
868 								   DEFAULT_BIT_RATE_PARAM,
869 								   &fDefaultBitRateParam,
870 								   sizeof(fDefaultBitRateParam));
871 	}
872 	if (bufferPeriodUpdated) {
873 		PRINT("\tbuffer period parameter updated\n");
874 		BroadcastNewParameterValue(fDefaultBufferPeriodParamChangeTime,
875 								   DEFAULT_BUFFER_PERIOD_PARAM,
876 								   &fDefaultBufferPeriodParam,
877 								   sizeof(fDefaultBufferPeriodParam));
878 	}
879 	return status;
880 }
881 
882 
883 // -------------------------------------------------------- //
884 // AbstractFileInterfaceNode specific functions
885 // -------------------------------------------------------- //
886 void AbstractFileInterfaceNode::GetFlavor(flavor_info * info, int32 id)
887 {
888 	CALLED();
889 
890 	if (info == 0)
891 		return;
892 
893 	info->name = strdup("AbstractFileInterfaceNode");
894 	info->info = strdup("A AbstractFileInterfaceNode node handles a file.");
895 	info->kinds = B_FILE_INTERFACE | B_CONTROLLABLE;
896 	info->flavor_flags = B_FLAVOR_IS_LOCAL;
897 	info->possible_count = INT_MAX;
898 	info->in_format_count = 0; // no inputs
899 	info->in_formats = 0;
900 	info->out_format_count = 0; // no outputs
901 	info->out_formats = 0;
902 	info->internal_id = id;
903 	return;
904 }
905 
906 
907 void AbstractFileInterfaceNode::GetFormat(media_format * outFormat)
908 {
909 	CALLED();
910 
911 	if (outFormat == 0)
912 		return;
913 
914 	outFormat->type = B_MEDIA_MULTISTREAM;
915 	outFormat->require_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
916 	outFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
917 	outFormat->u.multistream = media_multistream_format::wildcard;
918 }
919 
920 
921 void AbstractFileInterfaceNode::GetFileFormat(media_file_format * outFileFormat)
922 {
923 	CALLED();
924 
925 	if (outFileFormat == 0)
926 		return;
927 
928 	outFileFormat->capabilities =
929 			  media_file_format::B_PERFECTLY_SEEKABLE
930 			| media_file_format::B_IMPERFECTLY_SEEKABLE
931 			| media_file_format::B_KNOWS_ANYTHING;
932 	/* I don't know what to initialize this to. (or if I should) */
933 	// format.id =
934 	outFileFormat->family = B_ANY_FORMAT_FAMILY;
935 	outFileFormat->version = 100;
936 	// see media_file_format in <MediaDefs.h> for limits
937 	strncpy(outFileFormat->mime_type,"",63);
938 	outFileFormat->mime_type[63]='\0';
939 	strncpy(outFileFormat->pretty_name,"any media file format",63);
940 	outFileFormat->pretty_name[63]='\0';
941 	strncpy(outFileFormat->short_name,"any",31);
942 	outFileFormat->short_name[31]='\0';
943 	strncpy(outFileFormat->file_extension,"",7);
944 	outFileFormat->file_extension[7]='\0';
945 }
946 
947 
948 // protected:
949 // Here we make some guesses based on the file's mime type.
950 // We don't have enough information to add any other requirements.
951 // This function doesn't complain if you have already decided you want
952 // the stream to be considered a different one. (so you can say that you
953 // want to read that mpeg file as avi if you are so nutty.)
954 //
955 status_t AbstractFileInterfaceNode::AddRequirements(media_format * format)
956 {
957 	if (strcmp("video/x-msvideo",f_current_mime_type) == 0) {
958 		if (format->u.multistream.format == media_multistream_format::wildcard.format) {
959 			format->u.multistream.format = media_multistream_format::B_AVI;
960 		}
961 	} else
962 	if (strcmp("video/mpeg",f_current_mime_type) == 0) {
963 		if (format->u.multistream.format == media_multistream_format::wildcard.format) {
964 			format->u.multistream.format = media_multistream_format::B_MPEG1;
965 		}
966 	} else
967 	if (strcmp("video/quicktime",f_current_mime_type) == 0) {
968 		if (format->u.multistream.format == media_multistream_format::wildcard.format) {
969 			format->u.multistream.format = media_multistream_format::B_QUICKTIME;
970 		}
971 	} else
972 	if (strcmp("audio/x-mpeg",f_current_mime_type) == 0) {
973 		if (format->u.multistream.format == media_multistream_format::wildcard.format) {
974 			format->u.multistream.format = media_multistream_format::B_MPEG1;
975 		}
976 	}
977 	return B_OK;
978 }
979 
980 
981 // We need some sort of bit rate and chunk size, so if the other guy
982 // didn't care, we'll use our own defaults.
983 status_t AbstractFileInterfaceNode::ResolveWildcards(media_format * format)
984 {
985 	CALLED();
986 	// There isn't an unknown format. hmph.
987 	//	if (format->u.multistream.format == media_multistream_format::wildcard.format) {
988 	//		format->u.multistream.format = media_multistream_format::B_UNKNOWN;
989 	//	}
990 	if (format->u.multistream.max_bit_rate == media_multistream_format::wildcard.max_bit_rate) {
991 		format->u.multistream.max_bit_rate = fDefaultBitRateParam;
992 	}
993 	if (format->u.multistream.max_chunk_size == media_multistream_format::wildcard.max_chunk_size) {
994 		format->u.multistream.max_chunk_size = fDefaultChunkSizeParam;
995 	}
996 	if (format->u.multistream.avg_bit_rate == media_multistream_format::wildcard.avg_bit_rate) {
997 		format->u.multistream.avg_bit_rate = fDefaultBitRateParam;
998 	}
999 	if (format->u.multistream.avg_chunk_size == media_multistream_format::wildcard.avg_chunk_size) {
1000 		format->u.multistream.avg_chunk_size = fDefaultChunkSizeParam;
1001 	}
1002 	return B_OK;
1003 }
1004 
1005 
1006 // -------------------------------------------------------- //
1007 // stuffing
1008 // -------------------------------------------------------- //
1009 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_0(void *) { return B_ERROR; }
1010 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_1(void *) { return B_ERROR; }
1011 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_2(void *) { return B_ERROR; }
1012 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_3(void *) { return B_ERROR; }
1013 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_4(void *) { return B_ERROR; }
1014 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_5(void *) { return B_ERROR; }
1015 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_6(void *) { return B_ERROR; }
1016 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_7(void *) { return B_ERROR; }
1017 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_8(void *) { return B_ERROR; }
1018 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_9(void *) { return B_ERROR; }
1019 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_10(void *) { return B_ERROR; }
1020 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_11(void *) { return B_ERROR; }
1021 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_12(void *) { return B_ERROR; }
1022 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_13(void *) { return B_ERROR; }
1023 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_14(void *) { return B_ERROR; }
1024 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_15(void *) { return B_ERROR; }
1025