xref: /haiku/src/add-ons/media/media-add-ons/AbstractFileInterfaceNode.cpp (revision bcf291ed9b7e872a4b60160de829153188a8ea2f)
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 
~AbstractFileInterfaceNode(void)27 AbstractFileInterfaceNode::~AbstractFileInterfaceNode(void)
28 {
29 	CALLED();
30 
31 	// Stop the BMediaEventLooper thread
32 	Quit();
33 	if (fCurrentFile != 0)
34 		delete fCurrentFile;
35 }
36 
37 
AbstractFileInterfaceNode(size_t defaultChunkSize,float defaultBitRate,const flavor_info * info,BMessage * config,BMediaAddOn * addOn)38 AbstractFileInterfaceNode::AbstractFileInterfaceNode(
39 				size_t defaultChunkSize,
40 				float defaultBitRate,
41 				const flavor_info * info,
42 				BMessage * config,
43 				BMediaAddOn * addOn)
44 	: BMediaNode("AbstractFileInterfaceNode"),
45 	  BFileInterface(),
46 	  BControllable(),
47 	  BMediaEventLooper()
48 {
49 	CALLED();
50 
51 	// keep our creator around for AddOn calls later
52 	fAddOn = addOn;
53 	// null some fields
54 	fCurrentFile = 0;
55 	f_current_mime_type[0] = '\0';
56 
57 	// initialize the parameters
58 	if (defaultChunkSize <= 0) {
59 		fInitCheckStatus = B_BAD_VALUE;
60 		return;
61 	}
62 
63 	fDefaultChunkSizeParam = defaultChunkSize;
64 	fDefaultChunkSizeParamChangeTime = 0;
65 	if (defaultBitRate <= 0) {
66 		fInitCheckStatus = B_BAD_VALUE;
67 		return;
68 	}
69 
70 	fDefaultBitRateParam = defaultBitRate;
71 	fDefaultBitRateParamChangeTime = 0;
72 	// initialize parameter compute fields
73 	fLeastRecentlyUpdatedParameter = DEFAULT_BIT_RATE_PARAM;
74 	fLastUpdatedParameter = DEFAULT_BUFFER_PERIOD_PARAM;
75 	// From the chunk size and bit rate we compute the buffer period.
76 	int64 value = int64(8000000/1024*fDefaultChunkSizeParam/fDefaultBitRateParam);
77 	if ((value <= 0) || (value > INT_MAX)) {
78 		fInitCheckStatus = B_BAD_VALUE;
79 		return;
80 	}
81 	fDefaultBufferPeriodParam = int32(value);
82 	fDefaultBufferPeriodParamChangeTime = 0;
83 
84 	fInitCheckStatus = B_OK;
85 }
86 
87 
InitCheck(void) const88 status_t AbstractFileInterfaceNode::InitCheck(void) const
89 {
90 	CALLED();
91 	return fInitCheckStatus;
92 }
93 
94 
GetConfigurationFor(BMessage * into_message)95 status_t AbstractFileInterfaceNode::GetConfigurationFor(
96 				BMessage * into_message)
97 {
98 	CALLED();
99 	return B_OK;
100 }
101 
102 
103 // -------------------------------------------------------- //
104 // implementation of BMediaNode
105 // -------------------------------------------------------- //
AddOn(int32 * internal_id) const106 BMediaAddOn * AbstractFileInterfaceNode::AddOn(
107 				int32 * internal_id) const
108 {
109 	CALLED();
110 	// BeBook says this only gets called if we were in an add-on.
111 	if (fAddOn != 0) {
112 		// If we get a null pointer then we just won't write.
113 		if (internal_id != 0)
114 			internal_id = 0;
115 	}
116 	return fAddOn;
117 }
118 
119 
Start(bigtime_t performance_time)120 void AbstractFileInterfaceNode::Start(
121 				bigtime_t performance_time)
122 {
123 	PRINT("AbstractFileInterfaceNode::Start(pt=%lld)\n",performance_time);
124 	BMediaEventLooper::Start(performance_time);
125 }
126 
127 
Stop(bigtime_t performance_time,bool immediate)128 void AbstractFileInterfaceNode::Stop(
129 				bigtime_t performance_time,
130 				bool immediate)
131 {
132 	PRINT("AbstractFileInterfaceNode::Stop(pt=%lld, im=%d)\n",
133 		  performance_time, immediate);
134 	BMediaEventLooper::Stop(performance_time,immediate);
135 }
136 
137 
Seek(bigtime_t media_time,bigtime_t performance_time)138 void AbstractFileInterfaceNode::Seek(
139 				bigtime_t media_time,
140 				bigtime_t performance_time)
141 {
142 	PRINT("AbstractFileInterfaceNode::Seek(mt=%lld,pt=%lld)\n",
143 		  media_time,performance_time);
144 	BMediaEventLooper::Seek(media_time,performance_time);
145 }
146 
147 
SetRunMode(run_mode mode)148 void AbstractFileInterfaceNode::SetRunMode(
149 				run_mode mode)
150 {
151 	PRINT("AbstractFileInterfaceNode::SetRunMode(%i)\n",mode);
152 	BMediaEventLooper::SetRunMode(mode);
153 }
154 
155 
TimeWarp(bigtime_t at_real_time,bigtime_t to_performance_time)156 void AbstractFileInterfaceNode::TimeWarp(
157 				bigtime_t at_real_time,
158 				bigtime_t to_performance_time)
159 {
160 	PRINT("AbstractFileInterfaceNode::TimeWarp(rt=%lld,pt=%lld)\n",
161 		  at_real_time,to_performance_time);
162 	BMediaEventLooper::TimeWarp(at_real_time,to_performance_time);
163 }
164 
165 
Preroll(void)166 void AbstractFileInterfaceNode::Preroll(void)
167 {
168 	CALLED();
169 	// XXX:Performance opportunity
170 	BMediaNode::Preroll();
171 }
172 
173 
SetTimeSource(BTimeSource * time_source)174 void AbstractFileInterfaceNode::SetTimeSource(
175 				BTimeSource * time_source)
176 {
177 	CALLED();
178 	BMediaNode::SetTimeSource(time_source);
179 }
180 
181 
HandleMessage(int32 message,const void * data,size_t size)182 status_t AbstractFileInterfaceNode::HandleMessage(
183 				int32 message,
184 				const void * data,
185 				size_t size)
186 {
187 	CALLED();
188 
189 	status_t status = B_OK;
190 	switch (message) {
191 		// no special messages for now
192 		default:
193 			status = BFileInterface::HandleMessage(message,data,size);
194 			if (status == B_OK) {
195 				break;
196 			}
197 			status = BControllable::HandleMessage(message, data, size);
198 			if (status == B_OK)
199 				break;
200 			status = BMediaNode::HandleMessage(message,data,size);
201 			if (status == B_OK) {
202 				break;
203 			}
204 			BMediaNode::HandleBadMessage(message,data,size);
205 			status = B_ERROR;
206 			break;
207 	}
208 	return status;
209 }
210 
211 
RequestCompleted(const media_request_info & info)212 status_t AbstractFileInterfaceNode::RequestCompleted(
213 				const media_request_info & info)
214 {
215 	CALLED();
216 	return BMediaNode::RequestCompleted(info);
217 }
218 
219 
DeleteHook(BMediaNode * node)220 status_t AbstractFileInterfaceNode::DeleteHook(
221 				BMediaNode * node)
222 {
223 	CALLED();
224 	return BMediaEventLooper::DeleteHook(node);
225 }
226 
227 
NodeRegistered(void)228 void AbstractFileInterfaceNode::NodeRegistered(void)
229 {
230 	CALLED();
231 
232 	// set up our parameter web
233 	SetParameterWeb(MakeParameterWeb());
234 
235 	// start the BMediaEventLooper thread
236 	SetPriority(B_REAL_TIME_PRIORITY);
237 	Run();
238 }
239 
240 
GetNodeAttributes(media_node_attribute * outAttributes,size_t inMaxCount)241 status_t AbstractFileInterfaceNode::GetNodeAttributes(
242 				media_node_attribute * outAttributes,
243 				size_t inMaxCount)
244 {
245 	CALLED();
246 	return BMediaNode::GetNodeAttributes(outAttributes,inMaxCount);
247 }
248 
249 
AddTimer(bigtime_t at_performance_time,int32 cookie)250 status_t AbstractFileInterfaceNode::AddTimer(
251 					bigtime_t at_performance_time,
252 					int32 cookie)
253 {
254 	CALLED();
255 	return BMediaEventLooper::AddTimer(at_performance_time,cookie);
256 }
257 
258 
259 // protected:
MakeParameterWeb(void)260 BParameterWeb * AbstractFileInterfaceNode::MakeParameterWeb(void)
261 {
262 	CALLED();
263 
264 	BParameterWeb * web = new BParameterWeb();
265 	BParameterGroup * mainGroup = web->MakeGroup("AbstractFileInterfaceNode Parameters");
266 
267 	// these three are related:
268 	// DEFAULT_CHUNK_SIZE_PARAM =
269 	// DEFAULT_BIT_RATE_PARAM / 1024 * DEFAULT_BUFFER_PERIOD_PARAM * 1000
270 	BParameterGroup * chunkSizeGroup = mainGroup->MakeGroup("Chunk Size Group");
271 	BContinuousParameter * chunkSizeParameter
272 	   = chunkSizeGroup->MakeContinuousParameter(
273 	     DEFAULT_CHUNK_SIZE_PARAM, B_MEDIA_MULTISTREAM,
274 		 "Chunk Size", B_GAIN, "bytes", 1024, 32*1024, 512);
275 	chunkSizeParameter->SetResponse(BContinuousParameter::B_LINEAR,1,0);
276 	chunkSizeParameter->SetValue(&fDefaultChunkSizeParam,sizeof(fDefaultChunkSizeParam),0);
277 
278 	BParameterGroup * bitRateGroup = mainGroup->MakeGroup("Bit Rate Group");
279 	BContinuousParameter * bitRateParameter
280 	   = bitRateGroup->MakeContinuousParameter(
281 	     DEFAULT_BIT_RATE_PARAM, B_MEDIA_MULTISTREAM,
282 	     "Bit Rate", B_GAIN, "kbits/sec", 1, 320000, 1);
283 	bitRateParameter->SetResponse(BContinuousParameter::B_LINEAR,.001,0);
284 	bitRateParameter->SetValue(&fDefaultBitRateParam,sizeof(fDefaultBitRateParam),0);
285 
286 	BParameterGroup * bufferPeriodGroup = mainGroup->MakeGroup("Buffer Period Group");
287 	BContinuousParameter * bufferPeriodParameter
288 	   = bufferPeriodGroup->MakeContinuousParameter(
289 	     DEFAULT_BUFFER_PERIOD_PARAM, B_MEDIA_MULTISTREAM,
290 	     "Buffer Period", B_GAIN, "ms", 1, 10000, 1);
291 	bufferPeriodParameter->SetResponse(BContinuousParameter::B_LINEAR,1,0);
292 	bufferPeriodParameter->SetValue(&fDefaultBufferPeriodParam,sizeof(fDefaultBufferPeriodParam),0);
293 
294 	return web;
295 }
296 
297 
298 // -------------------------------------------------------- //
299 // implementation of BFileInterface
300 // -------------------------------------------------------- //
GetNextFileFormat(int32 * cookie,media_file_format * out_format)301 status_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 
DisposeFileFormatCookie(int32 cookie)320 void AbstractFileInterfaceNode::DisposeFileFormatCookie(
321 				int32 cookie)
322 {
323 	CALLED();
324 	// nothing to do since our cookies are just integers
325 }
326 
327 
GetDuration(bigtime_t * out_time)328 status_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 
SniffRef(const entry_ref & file,char * out_mime_type,float * out_quality)346 status_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 
SetRef(const entry_ref & file,uint32 openMode,bool create,bigtime_t * out_time)356 status_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 
GetRef(entry_ref * out_ref,char * out_mime_type)385 status_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
StaticSniffRef(const entry_ref & file,char * out_mime_type,float * out_quality)404 status_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 // -------------------------------------------------------- //
428 const int32 AbstractFileInterfaceNode::DEFAULT_CHUNK_SIZE_PARAM = 1;
429 const int32 AbstractFileInterfaceNode::DEFAULT_BIT_RATE_PARAM = 2;
430 const int32 AbstractFileInterfaceNode::DEFAULT_BUFFER_PERIOD_PARAM = 3;
431 
432 
GetParameterValue(int32 id,bigtime_t * last_change,void * value,size_t * ioSize)433 status_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 
SetParameterValue(int32 id,bigtime_t when,const void * value,size_t size)478 void 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()
StartControlPanel(BMessenger * out_messenger)506 status_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 // -------------------------------------------------------- //
HandleEvent(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)517 void 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 */
CleanUpEvent(const media_timed_event * event)555 void 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 */
OfflineTime()564 bigtime_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 */
ControlLoop()581 void AbstractFileInterfaceNode::ControlLoop() {
582 	BMediaEventLooper::ControlLoop();
583 }
584 
585 
586 // protected:
HandleStart(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)587 status_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 
HandleSeek(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)606 status_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 
HandleWarp(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)620 status_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 
HandleStop(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)630 status_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 
HandleParameter(const media_timed_event * event,bigtime_t lateness,bool realTimeEvent)643 status_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 // -------------------------------------------------------- //
GetFlavor(flavor_info * info,int32 id)859 void 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 
GetFormat(media_format * outFormat)880 void 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 
GetFileFormat(media_file_format * outFileFormat)894 void 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 //
AddRequirements(media_format * format)928 status_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.
ResolveWildcards(media_format * format)956 status_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 // -------------------------------------------------------- //
_Reserved_AbstractFileInterfaceNode_0(void *)982 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_0(void *) { return B_ERROR; }
_Reserved_AbstractFileInterfaceNode_1(void *)983 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_1(void *) { return B_ERROR; }
_Reserved_AbstractFileInterfaceNode_2(void *)984 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_2(void *) { return B_ERROR; }
_Reserved_AbstractFileInterfaceNode_3(void *)985 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_3(void *) { return B_ERROR; }
_Reserved_AbstractFileInterfaceNode_4(void *)986 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_4(void *) { return B_ERROR; }
_Reserved_AbstractFileInterfaceNode_5(void *)987 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_5(void *) { return B_ERROR; }
_Reserved_AbstractFileInterfaceNode_6(void *)988 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_6(void *) { return B_ERROR; }
_Reserved_AbstractFileInterfaceNode_7(void *)989 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_7(void *) { return B_ERROR; }
_Reserved_AbstractFileInterfaceNode_8(void *)990 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_8(void *) { return B_ERROR; }
_Reserved_AbstractFileInterfaceNode_9(void *)991 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_9(void *) { return B_ERROR; }
_Reserved_AbstractFileInterfaceNode_10(void *)992 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_10(void *) { return B_ERROR; }
_Reserved_AbstractFileInterfaceNode_11(void *)993 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_11(void *) { return B_ERROR; }
_Reserved_AbstractFileInterfaceNode_12(void *)994 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_12(void *) { return B_ERROR; }
_Reserved_AbstractFileInterfaceNode_13(void *)995 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_13(void *) { return B_ERROR; }
_Reserved_AbstractFileInterfaceNode_14(void *)996 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_14(void *) { return B_ERROR; }
_Reserved_AbstractFileInterfaceNode_15(void *)997 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_15(void *) { return B_ERROR; }
998