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