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
27AbstractFileInterfaceNode::~AbstractFileInterfaceNode(void)
28{
29	CALLED();
30
31	// Stop the BMediaEventLooper thread
32	Quit();
33	if (fCurrentFile != 0)
34		delete fCurrentFile;
35}
36
37
38AbstractFileInterfaceNode::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
88status_t AbstractFileInterfaceNode::InitCheck(void) const
89{
90	CALLED();
91	return fInitCheckStatus;
92}
93
94
95status_t AbstractFileInterfaceNode::GetConfigurationFor(
96				BMessage * into_message)
97{
98	CALLED();
99	return B_OK;
100}
101
102
103// -------------------------------------------------------- //
104// implementation of BMediaNode
105// -------------------------------------------------------- //
106BMediaAddOn * 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
120void 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
128void 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
138void 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
148void AbstractFileInterfaceNode::SetRunMode(
149				run_mode mode)
150{
151	PRINT("AbstractFileInterfaceNode::SetRunMode(%i)\n",mode);
152	BMediaEventLooper::SetRunMode(mode);
153}
154
155
156void 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
166void AbstractFileInterfaceNode::Preroll(void)
167{
168	CALLED();
169	// XXX:Performance opportunity
170	BMediaNode::Preroll();
171}
172
173
174void AbstractFileInterfaceNode::SetTimeSource(
175				BTimeSource * time_source)
176{
177	CALLED();
178	BMediaNode::SetTimeSource(time_source);
179}
180
181
182status_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
212status_t AbstractFileInterfaceNode::RequestCompleted(
213				const media_request_info & info)
214{
215	CALLED();
216	return BMediaNode::RequestCompleted(info);
217}
218
219
220status_t AbstractFileInterfaceNode::DeleteHook(
221				BMediaNode * node)
222{
223	CALLED();
224	return BMediaEventLooper::DeleteHook(node);
225}
226
227
228void 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
241status_t AbstractFileInterfaceNode::GetNodeAttributes(
242				media_node_attribute * outAttributes,
243				size_t inMaxCount)
244{
245	CALLED();
246	return BMediaNode::GetNodeAttributes(outAttributes,inMaxCount);
247}
248
249
250status_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:
260BParameterWeb * 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// -------------------------------------------------------- //
301status_t AbstractFileInterfaceNode::GetNextFileFormat(
302				int32 * cookie,
303				media_file_format * out_format)
304{
305	CALLED();
306
307	// it's valid but they already got our 1 file format
308	if (*cookie != 0) {
309		PRINT("\t<- B_ERROR\n");
310		return B_ERROR;
311	}
312
313	// so next time they won't get the same format again
314	*cookie = 1;
315	GetFileFormat(out_format);
316	return B_OK;
317}
318
319
320void AbstractFileInterfaceNode::DisposeFileFormatCookie(
321				int32 cookie)
322{
323	CALLED();
324	// nothing to do since our cookies are just integers
325}
326
327
328status_t AbstractFileInterfaceNode::GetDuration(
329				bigtime_t * out_time)
330{
331	CALLED();
332	if (out_time == 0) {
333		PRINT("\t<- B_BAD_VALUE\n");
334		return B_BAD_VALUE;
335	}
336
337	if (fCurrentFile == 0) {
338		PRINT("\t<- B_NO_INIT\n");
339		return B_NO_INIT;
340	}
341
342	return fCurrentFile->GetSize(out_time);
343}
344
345
346status_t AbstractFileInterfaceNode::SniffRef(
347				const entry_ref & file,
348				char * out_mime_type,	/* 256 bytes */
349				float * out_quality)
350{
351	CALLED();
352	return StaticSniffRef(file,out_mime_type,out_quality);
353}
354
355
356status_t AbstractFileInterfaceNode::SetRef(
357				const entry_ref & file,
358				uint32 openMode,
359				bool create,
360				bigtime_t * out_time)
361{
362	CALLED();
363
364	status_t status;
365	f_current_ref = file;
366	if (fCurrentFile == 0) {
367		fCurrentFile = new BFile(&f_current_ref,(openMode|(create?B_CREATE_FILE:0)));
368		status = fCurrentFile->InitCheck();
369	} else {
370		status = fCurrentFile->SetTo(&f_current_ref,(openMode|(create?B_CREATE_FILE:0)));
371	}
372
373	if (status != B_OK) {
374		PRINT("\t<- failed BFile initialization\n");
375		return status;
376	}
377
378	// cache the mime type for later
379	fCurrentFile->ReadAttr("BEOS:TYPE",0,0,f_current_mime_type,B_MIME_TYPE_LENGTH);
380	// compute the duration and return any error
381	return GetDuration(out_time);
382}
383
384
385status_t AbstractFileInterfaceNode::GetRef(
386				entry_ref * out_ref,
387				char * out_mime_type)
388{
389	CALLED();
390
391	if (fCurrentFile == 0) {
392		PRINT("\t<- B_NO_INIT\n");
393		return B_NO_INIT; // the input_ref isn't valid yet either
394	}
395
396	*out_ref = f_current_ref;
397	// they hopefully allocated enough space (no way to check :-/ )
398	strcpy(out_mime_type,f_current_mime_type);
399	return B_OK;
400}
401
402
403// provided for BAbstractFileInterfaceNodeAddOn
404status_t AbstractFileInterfaceNode::StaticSniffRef(
405				const entry_ref & file,
406				char * out_mime_type,	/* 256 bytes */
407				float * out_quality)
408{
409	CALLED();
410
411	BNode node(&file);
412	status_t initCheck = node.InitCheck();
413	if (initCheck != B_OK) {
414		PRINT("\t<- failed BNode::InitCheck()\n");
415		return initCheck;
416	}
417
418	// they hopefully allocated enough room
419	node.ReadAttr("BEOS:TYPE",0,0,out_mime_type,B_MIME_TYPE_LENGTH);
420	*out_quality = 1.0; // we handle all files perfectly!  we are so amazing!
421	return B_OK;
422}
423
424
425// -------------------------------------------------------- //
426// implementation for BControllable
427// -------------------------------------------------------- //
428const int32 AbstractFileInterfaceNode::DEFAULT_CHUNK_SIZE_PARAM = 1;
429const int32 AbstractFileInterfaceNode::DEFAULT_BIT_RATE_PARAM = 2;
430const int32 AbstractFileInterfaceNode::DEFAULT_BUFFER_PERIOD_PARAM = 3;
431
432
433status_t AbstractFileInterfaceNode::GetParameterValue(
434				int32 id,
435				bigtime_t * last_change,
436				void * value,
437				size_t * ioSize)
438{
439	CALLED();
440
441	switch (id) {
442		case DEFAULT_CHUNK_SIZE_PARAM:
443			if (*ioSize < sizeof(size_t)) {
444				return B_ERROR; // not enough room
445			}
446			*last_change = fDefaultChunkSizeParamChangeTime;
447			*((size_t*)value) = fDefaultChunkSizeParam;
448			*ioSize = sizeof(size_t);
449			break;
450
451		case DEFAULT_BIT_RATE_PARAM:
452			if (*ioSize < sizeof(float)) {
453				return B_ERROR; // not enough room
454			}
455			*last_change = fDefaultBitRateParamChangeTime;
456			*((float*)value) = fDefaultBitRateParam;
457			*ioSize = sizeof(float);
458			break;
459
460		case DEFAULT_BUFFER_PERIOD_PARAM:
461			if (*ioSize < sizeof(int32)) {
462				return B_ERROR; // not enough room
463			}
464			*last_change = fDefaultBufferPeriodParamChangeTime;
465			*((int32*)value) = fDefaultBufferPeriodParam;
466			*ioSize = sizeof(int32);
467			break;
468
469		default:
470			PRINT("AbstractFileInterfaceNode::GetParameterValue unknown id (%ld)\n",id);
471			return B_ERROR;
472	}
473
474	return B_OK;
475}
476
477
478void AbstractFileInterfaceNode::SetParameterValue(
479				int32 id,
480				bigtime_t when,
481				const void * value,
482				size_t size)
483{
484	PRINT("AbstractFileInterfaceNode::SetParameterValue(id=%ld,when=%lld,size=%ld)\n",id,when,int32(size));
485
486	switch (id) {
487		case DEFAULT_CHUNK_SIZE_PARAM:
488		case DEFAULT_BIT_RATE_PARAM:
489		case DEFAULT_BUFFER_PERIOD_PARAM:
490			{
491				media_timed_event event(when, BTimedEventQueue::B_PARAMETER,
492										NULL, BTimedEventQueue::B_NO_CLEANUP,
493										size, id, (char*) value, size);
494				EventQueue()->AddEvent(event);
495			}
496			break;
497
498		default:
499			PRINT("AbstractFileInterfaceNode::SetParameterValue unknown id (%ld)\n",id);
500			break;
501	}
502}
503
504
505// the default implementation should call the add-on main()
506status_t AbstractFileInterfaceNode::StartControlPanel(
507				BMessenger * out_messenger)
508{
509	CALLED();
510	return BControllable::StartControlPanel(out_messenger);
511}
512
513
514// -------------------------------------------------------- //
515// implementation for BMediaEventLooper
516// -------------------------------------------------------- //
517void AbstractFileInterfaceNode::HandleEvent(
518				const media_timed_event *event,
519				bigtime_t lateness,
520				bool realTimeEvent)
521{
522	CALLED();
523	switch (event->type) {
524		case BTimedEventQueue::B_START:
525			HandleStart(event,lateness,realTimeEvent);
526			break;
527		case BTimedEventQueue::B_SEEK:
528			HandleSeek(event,lateness,realTimeEvent);
529			break;
530		case BTimedEventQueue::B_WARP:
531			HandleWarp(event,lateness,realTimeEvent);
532			break;
533		case BTimedEventQueue::B_STOP:
534			HandleStop(event,lateness,realTimeEvent);
535			break;
536		case BTimedEventQueue::B_HANDLE_BUFFER:
537			if (RunState() == BMediaEventLooper::B_STARTED) {
538				HandleBuffer(event,lateness,realTimeEvent);
539			}
540			break;
541		case BTimedEventQueue::B_DATA_STATUS:
542			HandleDataStatus(event,lateness,realTimeEvent);
543			break;
544		case BTimedEventQueue::B_PARAMETER:
545			HandleParameter(event,lateness,realTimeEvent);
546			break;
547		default:
548			PRINT("  unknown event type: %ld\n",event->type);
549			break;
550	}
551}
552
553
554/* override to clean up custom events you have added to your queue */
555void AbstractFileInterfaceNode::CleanUpEvent(
556				const media_timed_event *event)
557{
558	BMediaEventLooper::CleanUpEvent(event);
559}
560
561
562/* called from Offline mode to determine the current time of the node */
563/* update your internal information whenever it changes */
564bigtime_t AbstractFileInterfaceNode::OfflineTime()
565{
566	CALLED();
567	return BMediaEventLooper::OfflineTime();
568	// XXX: do something else?
569	//	if (inputFile == 0) {
570	//		return 0;
571	//	} else {
572	//		return inputFile->Position();
573	//	}
574}
575
576
577/* override only if you know what you are doing! */
578/* otherwise much badness could occur */
579/* the actual control loop function: */
580/* 	waits for messages, Pops events off the queue and calls DispatchEvent */
581void AbstractFileInterfaceNode::ControlLoop() {
582	BMediaEventLooper::ControlLoop();
583}
584
585
586// protected:
587status_t AbstractFileInterfaceNode::HandleStart(
588						const media_timed_event *event,
589						bigtime_t lateness,
590						bool realTimeEvent)
591{
592	CALLED();
593
594	if (RunState() != B_STARTED) {
595		// XXX: Either use the following line or the lines that are not commented.
596		// There doesn't seem to be a practical difference that i can tell.
597		//		HandleBuffer(event,lateness,realTimeEvent);
598		media_timed_event firstBufferEvent(event->event_time, BTimedEventQueue::B_HANDLE_BUFFER);
599		HandleEvent(&firstBufferEvent, 0, false);
600		EventQueue()->AddEvent(firstBufferEvent);
601	}
602	return B_OK;
603}
604
605
606status_t AbstractFileInterfaceNode::HandleSeek(
607						const media_timed_event *event,
608						bigtime_t lateness,
609						bool realTimeEvent)
610{
611	PRINT("AbstractFileInterfaceNode::HandleSeek(t=%lld,d=%ld,bd=%lld)\n",event->event_time,event->data,event->bigdata);
612
613	if (fCurrentFile != 0)
614		return fCurrentFile->Seek(event->bigdata,SEEK_SET);
615	else
616		return B_ERROR;
617}
618
619
620status_t AbstractFileInterfaceNode::HandleWarp(
621						const media_timed_event *event,
622						bigtime_t lateness,
623						bool realTimeEvent)
624{
625	CALLED();
626	return B_OK;
627}
628
629
630status_t AbstractFileInterfaceNode::HandleStop(
631						const media_timed_event *event,
632						bigtime_t lateness,
633						bool realTimeEvent)
634{
635	CALLED();
636
637	// flush the queue so downstreamers don't get any more
638	EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER);
639	return B_OK;
640}
641
642
643status_t AbstractFileInterfaceNode::HandleParameter(
644				const media_timed_event *event,
645				bigtime_t lateness,
646				bool realTimeEvent)
647{
648	CALLED();
649
650	status_t status = B_OK;
651
652	bool chunkSizeUpdated = false, bitRateUpdated = false, bufferPeriodUpdated = false;
653
654	size_t dataSize = size_t(event->data);
655	int64 param = int64(event->bigdata);
656
657	switch (param) {
658		case DEFAULT_CHUNK_SIZE_PARAM:
659			PRINT("(DEFAULT_CHUNK_SIZE_PARAM,size=%ld",dataSize);
660			if (dataSize < sizeof(size_t)) {
661				PRINT("\ŧ<- B_BAD_VALUE: %lld\n",param);
662				status = B_BAD_VALUE;
663			} else {
664				size_t newDefaultChunkSize = *((size_t*)event->user_data);
665				PRINT(",%ld)\n", newDefaultChunkSize);
666				// ignore non positive chunk sizes
667				// XXX: we may decide later that a 0 chunk size means ship the whole file in one chunk (!)
668				if ((newDefaultChunkSize > 0) && (newDefaultChunkSize != fDefaultChunkSizeParam)) {
669					PRINT("\tgot a new chunk size, old chunk size was %ld\n",fDefaultChunkSizeParam);
670					fDefaultChunkSizeParam = newDefaultChunkSize;
671					fDefaultChunkSizeParamChangeTime = TimeSource()->Now();
672					chunkSizeUpdated = true;
673					if (fLeastRecentlyUpdatedParameter == DEFAULT_CHUNK_SIZE_PARAM) {
674						// Okay we were the least recently updated parameter,
675						// but we just got an update so we are no longer that.
676						// Let's figure out who the new least recently updated
677						// parameter is.  We are going to prefer to compute the
678						// bit rate since you usually don't want to muck with
679						// the buffer period.  However, if you just set the bitrate
680						// then we are stuck with making the buffer period the new
681						// parameter to be computed.
682						if (fLastUpdatedParameter == DEFAULT_BIT_RATE_PARAM)
683							fLeastRecentlyUpdatedParameter = DEFAULT_BUFFER_PERIOD_PARAM;
684						else
685							fLeastRecentlyUpdatedParameter = DEFAULT_BIT_RATE_PARAM;
686					}
687					// we just got an update, so we are the new lastUpdatedParameter
688					fLastUpdatedParameter = DEFAULT_CHUNK_SIZE_PARAM;
689					// now we have to compute the new value for the leastRecentlyUpdatedParameter
690					// we use the chunk size change time to preserve "simultaneity" information
691					if (fLeastRecentlyUpdatedParameter == DEFAULT_BUFFER_PERIOD_PARAM) {
692						int64 value = int64(8000000/1024*fDefaultChunkSizeParam/fDefaultBitRateParam);
693						if (value > INT_MAX) {
694							// clamp to INT_MAX
695							fDefaultBufferPeriodParam = INT_MAX;
696							// recompute chunk size
697							fDefaultChunkSizeParam = size_t(1024/8000000*fDefaultBitRateParam*fDefaultBufferPeriodParam);
698						} else {
699							fDefaultBufferPeriodParam = MAX(1,value);
700						}
701						fDefaultBufferPeriodParamChangeTime = fDefaultChunkSizeParamChangeTime;
702						bufferPeriodUpdated = true;
703					} else { // must have been bit rate
704						fDefaultBitRateParam = MAX(0.001,8000000/1024*fDefaultChunkSizeParam/fDefaultBufferPeriodParam);
705						fDefaultBitRateParamChangeTime = fDefaultChunkSizeParamChangeTime;
706						bitRateUpdated = true;
707					}
708				}
709			}
710			break;
711		case DEFAULT_BIT_RATE_PARAM:
712			PRINT("(DEFAULT_BIT_RATE_PARAM,size=%ld",dataSize);
713			if (dataSize < sizeof(float)) {
714				PRINT("\t<- B_BAD_VALUE:lld\n",param);
715				status = B_BAD_VALUE;
716			} else {
717				float newDefaultBitRate = *((float*)event->user_data);
718				PRINT(",%f)\n",newDefaultBitRate);
719				// ignore non positive bitrates
720				if ((newDefaultBitRate > 0) && (newDefaultBitRate != fDefaultBitRateParam)) {
721					PRINT("\tgot a new bit rate, old bit rate was %ld\n",fDefaultBitRateParam);
722					fDefaultBitRateParam = newDefaultBitRate;
723					fDefaultBitRateParamChangeTime = TimeSource()->Now();
724					bitRateUpdated = true;
725					if (fLeastRecentlyUpdatedParameter == DEFAULT_BIT_RATE_PARAM) {
726						// Okay we were the least recently updated parameter,
727						// but we just got an update so we are no longer that.
728						// Let's figure out who the new least recently updated
729						// parameter is.  We are going to prefer to compute the
730						// chunk size since you usually don't want to muck with
731						// the buffer period.  However, if you just set the chunk size
732						// then we are stuck with making the buffer period the new
733						// parameter to be computed.
734						if (fLastUpdatedParameter == DEFAULT_CHUNK_SIZE_PARAM) {
735							fLeastRecentlyUpdatedParameter = DEFAULT_BUFFER_PERIOD_PARAM;
736						} else {
737							fLeastRecentlyUpdatedParameter = DEFAULT_CHUNK_SIZE_PARAM;
738						}
739					}
740					// we just got an update, so we are the new lastUpdatedParameter
741					fLastUpdatedParameter = DEFAULT_BIT_RATE_PARAM;
742					// now we have to compute the new value for the leastRecentlyUpdatedParameter
743					// we use the bit rate change time to preserve "simultaneity" information
744					if (fLeastRecentlyUpdatedParameter == DEFAULT_BUFFER_PERIOD_PARAM) {
745						int64 value =
746								int64(8000000/1024*fDefaultChunkSizeParam/fDefaultBitRateParam);
747						if (value > INT_MAX) {
748							// clamp to INT_MAX
749							fDefaultBufferPeriodParam = INT_MAX;
750							// recompute bit rate
751							fDefaultBitRateParam = MAX(0.001,
752										8000000/1024*fDefaultChunkSizeParam/fDefaultBufferPeriodParam);
753						} else {
754							fDefaultBufferPeriodParam = MAX(1,int32(value));
755						}
756						fDefaultBufferPeriodParamChangeTime = fDefaultBitRateParamChangeTime;
757						bufferPeriodUpdated = true;
758					} else { // must have been chunk size
759						int64 value =
760								int64(1024/8000000*fDefaultBitRateParam*fDefaultBufferPeriodParam);
761						if (value > INT_MAX) {
762							// clamp to INT_MAX
763							fDefaultChunkSizeParam = INT_MAX;
764							// recompute bit rate
765							fDefaultBitRateParam =
766									MAX(0.001,8000000/1024*fDefaultChunkSizeParam/fDefaultBufferPeriodParam);
767						} else {
768							fDefaultChunkSizeParam = MAX(1,int32(value));
769						}
770						fDefaultChunkSizeParamChangeTime = fDefaultBitRateParamChangeTime;
771						chunkSizeUpdated = true;
772					}
773				}
774			}
775			break;
776		case DEFAULT_BUFFER_PERIOD_PARAM:
777			PRINT("(DEFAULT_BUFFER_PERIOD_PARAM,size=%ld",dataSize);
778			if (dataSize < sizeof(int32)) {
779				PRINT("\t<- B_BAD_VALUE:%ld\n",param);
780				status = B_BAD_VALUE;
781			} else {
782				int32 newBufferPeriod = *((int32*)event->user_data);
783				PRINT(",%ld)\n",newBufferPeriod);
784				// ignore non positive buffer period
785				if ((newBufferPeriod > 0) && (newBufferPeriod != fDefaultBufferPeriodParam)) {
786					PRINT("\tgot a new buffer period, old buffer period was %ld\n",
787						  fDefaultBufferPeriodParam);
788					fDefaultBufferPeriodParam = newBufferPeriod;
789					fDefaultBufferPeriodParamChangeTime = TimeSource()->Now();
790					bufferPeriodUpdated = true;
791					if (fLastUpdatedParameter == DEFAULT_BUFFER_PERIOD_PARAM) {
792						// prefer to update bit rate, unless you just set it
793						if (fLastUpdatedParameter == DEFAULT_BIT_RATE_PARAM) {
794							fLeastRecentlyUpdatedParameter = DEFAULT_CHUNK_SIZE_PARAM;
795						} else {
796							fLeastRecentlyUpdatedParameter = DEFAULT_BIT_RATE_PARAM;
797						}
798					}
799					// we just got an update, so we are the new lastUpdatedParameter
800					fLastUpdatedParameter = DEFAULT_BUFFER_PERIOD_PARAM;
801					// now we have to compute the new value for the leastRecentlyUpdatedParameter
802					// we use the buffer period change time to preserve "simultaneity" information
803					if (fLeastRecentlyUpdatedParameter == DEFAULT_BIT_RATE_PARAM) {
804						fDefaultBitRateParam =
805								MAX(0.001,8000000/1024*fDefaultChunkSizeParam/fDefaultBufferPeriodParam);
806						fDefaultBitRateParamChangeTime = fDefaultBufferPeriodParamChangeTime;
807						bitRateUpdated = true;
808					} else { // must have been chunk size
809						int64 value = int64(1024/8000000*fDefaultBitRateParam*fDefaultBufferPeriodParam);
810						if (value > INT_MAX) {
811							// clamp to INT_MAX
812							fDefaultChunkSizeParam = INT_MAX;
813							// recompute buffer period
814							fDefaultBufferPeriodParam =
815									size_t(8000000/1024*fDefaultChunkSizeParam/fDefaultBitRateParam);
816						} else {
817							fDefaultChunkSizeParam = MAX(1,int32(value));
818						}
819						fDefaultChunkSizeParamChangeTime = fDefaultBufferPeriodParamChangeTime;
820						chunkSizeUpdated = true;
821					}
822				}
823			}
824			break;
825		default:
826			PRINT("AbstractFileInterfaceNode::HandleParameter called with unknown param id (%ld)\n",param);
827			status = B_ERROR;
828	}
829	// send updates out for all the parameters that changed
830	// in every case this should be two updates. (if I have not made an error :-) )
831	if (chunkSizeUpdated) {
832		PRINT("\tchunk size parameter updated\n");
833		BroadcastNewParameterValue(fDefaultChunkSizeParamChangeTime,
834								   DEFAULT_CHUNK_SIZE_PARAM,
835								   &fDefaultChunkSizeParam,
836								   sizeof(fDefaultChunkSizeParam));
837	}
838	if (bitRateUpdated) {
839		PRINT("\tbit rate parameter updated\n");
840		BroadcastNewParameterValue(fDefaultBitRateParamChangeTime,
841								   DEFAULT_BIT_RATE_PARAM,
842								   &fDefaultBitRateParam,
843								   sizeof(fDefaultBitRateParam));
844	}
845	if (bufferPeriodUpdated) {
846		PRINT("\tbuffer period parameter updated\n");
847		BroadcastNewParameterValue(fDefaultBufferPeriodParamChangeTime,
848								   DEFAULT_BUFFER_PERIOD_PARAM,
849								   &fDefaultBufferPeriodParam,
850								   sizeof(fDefaultBufferPeriodParam));
851	}
852	return status;
853}
854
855
856// -------------------------------------------------------- //
857// AbstractFileInterfaceNode specific functions
858// -------------------------------------------------------- //
859void AbstractFileInterfaceNode::GetFlavor(flavor_info * info, int32 id)
860{
861	CALLED();
862
863	if (info == 0)
864		return;
865
866	info->name = strdup("AbstractFileInterfaceNode");
867	info->info = strdup("A AbstractFileInterfaceNode node handles a file.");
868	info->kinds = B_FILE_INTERFACE | B_CONTROLLABLE;
869	info->flavor_flags = B_FLAVOR_IS_LOCAL;
870	info->possible_count = INT_MAX;
871	info->in_format_count = 0; // no inputs
872	info->in_formats = 0;
873	info->out_format_count = 0; // no outputs
874	info->out_formats = 0;
875	info->internal_id = id;
876	return;
877}
878
879
880void AbstractFileInterfaceNode::GetFormat(media_format * outFormat)
881{
882	CALLED();
883
884	if (outFormat == 0)
885		return;
886
887	outFormat->type = B_MEDIA_MULTISTREAM;
888	outFormat->require_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
889	outFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
890	outFormat->u.multistream = media_multistream_format::wildcard;
891}
892
893
894void AbstractFileInterfaceNode::GetFileFormat(media_file_format * outFileFormat)
895{
896	CALLED();
897
898	if (outFileFormat == 0)
899		return;
900
901	outFileFormat->capabilities =
902			  media_file_format::B_PERFECTLY_SEEKABLE
903			| media_file_format::B_IMPERFECTLY_SEEKABLE
904			| media_file_format::B_KNOWS_ANYTHING;
905	/* I don't know what to initialize this to. (or if I should) */
906	// format.id =
907	outFileFormat->family = B_ANY_FORMAT_FAMILY;
908	outFileFormat->version = 100;
909	// see media_file_format in <MediaDefs.h> for limits
910	strncpy(outFileFormat->mime_type,"",63);
911	outFileFormat->mime_type[63]='\0';
912	strncpy(outFileFormat->pretty_name,"any media file format",63);
913	outFileFormat->pretty_name[63]='\0';
914	strncpy(outFileFormat->short_name,"any",31);
915	outFileFormat->short_name[31]='\0';
916	strncpy(outFileFormat->file_extension,"",7);
917	outFileFormat->file_extension[7]='\0';
918}
919
920
921// protected:
922// Here we make some guesses based on the file's mime type.
923// We don't have enough information to add any other requirements.
924// This function doesn't complain if you have already decided you want
925// the stream to be considered a different one. (so you can say that you
926// want to read that mpeg file as avi if you are so nutty.)
927//
928status_t AbstractFileInterfaceNode::AddRequirements(media_format * format)
929{
930	if (strcmp("video/x-msvideo",f_current_mime_type) == 0) {
931		if (format->u.multistream.format == media_multistream_format::wildcard.format) {
932			format->u.multistream.format = media_multistream_format::B_AVI;
933		}
934	} else
935	if (strcmp("video/mpeg",f_current_mime_type) == 0) {
936		if (format->u.multistream.format == media_multistream_format::wildcard.format) {
937			format->u.multistream.format = media_multistream_format::B_MPEG1;
938		}
939	} else
940	if (strcmp("video/quicktime",f_current_mime_type) == 0) {
941		if (format->u.multistream.format == media_multistream_format::wildcard.format) {
942			format->u.multistream.format = media_multistream_format::B_QUICKTIME;
943		}
944	} else
945	if (strcmp("audio/x-mpeg",f_current_mime_type) == 0) {
946		if (format->u.multistream.format == media_multistream_format::wildcard.format) {
947			format->u.multistream.format = media_multistream_format::B_MPEG1;
948		}
949	}
950	return B_OK;
951}
952
953
954// We need some sort of bit rate and chunk size, so if the other guy
955// didn't care, we'll use our own defaults.
956status_t AbstractFileInterfaceNode::ResolveWildcards(media_format * format)
957{
958	CALLED();
959	// There isn't an unknown format. hmph.
960	//	if (format->u.multistream.format == media_multistream_format::wildcard.format) {
961	//		format->u.multistream.format = media_multistream_format::B_UNKNOWN;
962	//	}
963	if (format->u.multistream.max_bit_rate == media_multistream_format::wildcard.max_bit_rate) {
964		format->u.multistream.max_bit_rate = fDefaultBitRateParam;
965	}
966	if (format->u.multistream.max_chunk_size == media_multistream_format::wildcard.max_chunk_size) {
967		format->u.multistream.max_chunk_size = fDefaultChunkSizeParam;
968	}
969	if (format->u.multistream.avg_bit_rate == media_multistream_format::wildcard.avg_bit_rate) {
970		format->u.multistream.avg_bit_rate = fDefaultBitRateParam;
971	}
972	if (format->u.multistream.avg_chunk_size == media_multistream_format::wildcard.avg_chunk_size) {
973		format->u.multistream.avg_chunk_size = fDefaultChunkSizeParam;
974	}
975	return B_OK;
976}
977
978
979// -------------------------------------------------------- //
980// stuffing
981// -------------------------------------------------------- //
982status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_0(void *) { return B_ERROR; }
983status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_1(void *) { return B_ERROR; }
984status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_2(void *) { return B_ERROR; }
985status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_3(void *) { return B_ERROR; }
986status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_4(void *) { return B_ERROR; }
987status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_5(void *) { return B_ERROR; }
988status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_6(void *) { return B_ERROR; }
989status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_7(void *) { return B_ERROR; }
990status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_8(void *) { return B_ERROR; }
991status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_9(void *) { return B_ERROR; }
992status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_10(void *) { return B_ERROR; }
993status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_11(void *) { return B_ERROR; }
994status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_12(void *) { return B_ERROR; }
995status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_13(void *) { return B_ERROR; }
996status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_14(void *) { return B_ERROR; }
997status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_15(void *) { return B_ERROR; }
998