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