vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / media / media-add-ons / AbstractFileInterfaceNode.cpp
blobf08c17492b4ff0e6c4a0ca69529330425056fa3e
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"
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>
27 AbstractFileInterfaceNode::~AbstractFileInterfaceNode(void)
29 CALLED();
31 // Stop the BMediaEventLooper thread
32 Quit();
33 if (fCurrentFile != 0)
34 delete fCurrentFile;
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()
49 CALLED();
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';
57 // initialize the parameters
58 if (defaultChunkSize <= 0) {
59 fInitCheckStatus = B_BAD_VALUE;
60 return;
63 fDefaultChunkSizeParam = defaultChunkSize;
64 fDefaultChunkSizeParamChangeTime = 0;
65 if (defaultBitRate <= 0) {
66 fInitCheckStatus = B_BAD_VALUE;
67 return;
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;
81 fDefaultBufferPeriodParam = int32(value);
82 fDefaultBufferPeriodParamChangeTime = 0;
84 fInitCheckStatus = B_OK;
88 status_t AbstractFileInterfaceNode::InitCheck(void) const
90 CALLED();
91 return fInitCheckStatus;
95 status_t AbstractFileInterfaceNode::GetConfigurationFor(
96 BMessage * into_message)
98 CALLED();
99 return B_OK;
103 // -------------------------------------------------------- //
104 // implementation of BMediaNode
105 // -------------------------------------------------------- //
106 BMediaAddOn * AbstractFileInterfaceNode::AddOn(
107 int32 * internal_id) const
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;
116 return fAddOn;
120 void AbstractFileInterfaceNode::Start(
121 bigtime_t performance_time)
123 PRINT("AbstractFileInterfaceNode::Start(pt=%lld)\n",performance_time);
124 BMediaEventLooper::Start(performance_time);
128 void AbstractFileInterfaceNode::Stop(
129 bigtime_t performance_time,
130 bool immediate)
132 PRINT("AbstractFileInterfaceNode::Stop(pt=%lld, im=%d)\n",
133 performance_time, immediate);
134 BMediaEventLooper::Stop(performance_time,immediate);
138 void AbstractFileInterfaceNode::Seek(
139 bigtime_t media_time,
140 bigtime_t performance_time)
142 PRINT("AbstractFileInterfaceNode::Seek(mt=%lld,pt=%lld)\n",
143 media_time,performance_time);
144 BMediaEventLooper::Seek(media_time,performance_time);
148 void AbstractFileInterfaceNode::SetRunMode(
149 run_mode mode)
151 PRINT("AbstractFileInterfaceNode::SetRunMode(%i)\n",mode);
152 BMediaEventLooper::SetRunMode(mode);
156 void AbstractFileInterfaceNode::TimeWarp(
157 bigtime_t at_real_time,
158 bigtime_t to_performance_time)
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);
166 void AbstractFileInterfaceNode::Preroll(void)
168 CALLED();
169 // XXX:Performance opportunity
170 BMediaNode::Preroll();
174 void AbstractFileInterfaceNode::SetTimeSource(
175 BTimeSource * time_source)
177 CALLED();
178 BMediaNode::SetTimeSource(time_source);
182 status_t AbstractFileInterfaceNode::HandleMessage(
183 int32 message,
184 const void * data,
185 size_t size)
187 CALLED();
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;
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;
204 BMediaNode::HandleBadMessage(message,data,size);
205 status = B_ERROR;
206 break;
208 return status;
212 status_t AbstractFileInterfaceNode::RequestCompleted(
213 const media_request_info & info)
215 CALLED();
216 return BMediaNode::RequestCompleted(info);
220 status_t AbstractFileInterfaceNode::DeleteHook(
221 BMediaNode * node)
223 CALLED();
224 return BMediaEventLooper::DeleteHook(node);
228 void AbstractFileInterfaceNode::NodeRegistered(void)
230 CALLED();
232 // set up our parameter web
233 SetParameterWeb(MakeParameterWeb());
235 // start the BMediaEventLooper thread
236 SetPriority(B_REAL_TIME_PRIORITY);
237 Run();
241 status_t AbstractFileInterfaceNode::GetNodeAttributes(
242 media_node_attribute * outAttributes,
243 size_t inMaxCount)
245 CALLED();
246 return BMediaNode::GetNodeAttributes(outAttributes,inMaxCount);
250 status_t AbstractFileInterfaceNode::AddTimer(
251 bigtime_t at_performance_time,
252 int32 cookie)
254 CALLED();
255 return BMediaEventLooper::AddTimer(at_performance_time,cookie);
259 // protected:
260 BParameterWeb * AbstractFileInterfaceNode::MakeParameterWeb(void)
262 CALLED();
264 BParameterWeb * web = new BParameterWeb();
265 BParameterGroup * mainGroup = web->MakeGroup("AbstractFileInterfaceNode Parameters");
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);
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);
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);
294 return web;
298 // -------------------------------------------------------- //
299 // implementation of BFileInterface
300 // -------------------------------------------------------- //
301 status_t AbstractFileInterfaceNode::GetNextFileFormat(
302 int32 * cookie,
303 media_file_format * out_format)
305 CALLED();
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;
313 // so next time they won't get the same format again
314 *cookie = 1;
315 GetFileFormat(out_format);
316 return B_OK;
320 void AbstractFileInterfaceNode::DisposeFileFormatCookie(
321 int32 cookie)
323 CALLED();
324 // nothing to do since our cookies are just integers
328 status_t AbstractFileInterfaceNode::GetDuration(
329 bigtime_t * out_time)
331 CALLED();
332 if (out_time == 0) {
333 PRINT("\t<- B_BAD_VALUE\n");
334 return B_BAD_VALUE;
337 if (fCurrentFile == 0) {
338 PRINT("\t<- B_NO_INIT\n");
339 return B_NO_INIT;
342 return fCurrentFile->GetSize(out_time);
346 status_t AbstractFileInterfaceNode::SniffRef(
347 const entry_ref & file,
348 char * out_mime_type, /* 256 bytes */
349 float * out_quality)
351 CALLED();
352 return StaticSniffRef(file,out_mime_type,out_quality);
356 status_t AbstractFileInterfaceNode::SetRef(
357 const entry_ref & file,
358 uint32 openMode,
359 bool create,
360 bigtime_t * out_time)
362 CALLED();
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)));
373 if (status != B_OK) {
374 PRINT("\t<- failed BFile initialization\n");
375 return status;
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);
385 status_t AbstractFileInterfaceNode::GetRef(
386 entry_ref * out_ref,
387 char * out_mime_type)
389 CALLED();
391 if (fCurrentFile == 0) {
392 PRINT("\t<- B_NO_INIT\n");
393 return B_NO_INIT; // the input_ref isn't valid yet either
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;
403 // provided for BAbstractFileInterfaceNodeAddOn
404 status_t AbstractFileInterfaceNode::StaticSniffRef(
405 const entry_ref & file,
406 char * out_mime_type, /* 256 bytes */
407 float * out_quality)
409 CALLED();
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;
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;
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;
433 status_t AbstractFileInterfaceNode::GetParameterValue(
434 int32 id,
435 bigtime_t * last_change,
436 void * value,
437 size_t * ioSize)
439 CALLED();
441 switch (id) {
442 case DEFAULT_CHUNK_SIZE_PARAM:
443 if (*ioSize < sizeof(size_t)) {
444 return B_ERROR; // not enough room
446 *last_change = fDefaultChunkSizeParamChangeTime;
447 *((size_t*)value) = fDefaultChunkSizeParam;
448 *ioSize = sizeof(size_t);
449 break;
451 case DEFAULT_BIT_RATE_PARAM:
452 if (*ioSize < sizeof(float)) {
453 return B_ERROR; // not enough room
455 *last_change = fDefaultBitRateParamChangeTime;
456 *((float*)value) = fDefaultBitRateParam;
457 *ioSize = sizeof(float);
458 break;
460 case DEFAULT_BUFFER_PERIOD_PARAM:
461 if (*ioSize < sizeof(int32)) {
462 return B_ERROR; // not enough room
464 *last_change = fDefaultBufferPeriodParamChangeTime;
465 *((int32*)value) = fDefaultBufferPeriodParam;
466 *ioSize = sizeof(int32);
467 break;
469 default:
470 PRINT("AbstractFileInterfaceNode::GetParameterValue unknown id (%ld)\n",id);
471 return B_ERROR;
474 return B_OK;
478 void AbstractFileInterfaceNode::SetParameterValue(
479 int32 id,
480 bigtime_t when,
481 const void * value,
482 size_t size)
484 PRINT("AbstractFileInterfaceNode::SetParameterValue(id=%ld,when=%lld,size=%ld)\n",id,when,int32(size));
486 switch (id) {
487 case DEFAULT_CHUNK_SIZE_PARAM:
488 case DEFAULT_BIT_RATE_PARAM:
489 case DEFAULT_BUFFER_PERIOD_PARAM:
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);
496 break;
498 default:
499 PRINT("AbstractFileInterfaceNode::SetParameterValue unknown id (%ld)\n",id);
500 break;
505 // the default implementation should call the add-on main()
506 status_t AbstractFileInterfaceNode::StartControlPanel(
507 BMessenger * out_messenger)
509 CALLED();
510 return BControllable::StartControlPanel(out_messenger);
514 // -------------------------------------------------------- //
515 // implementation for BMediaEventLooper
516 // -------------------------------------------------------- //
517 void AbstractFileInterfaceNode::HandleEvent(
518 const media_timed_event *event,
519 bigtime_t lateness,
520 bool realTimeEvent)
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);
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;
554 /* override to clean up custom events you have added to your queue */
555 void AbstractFileInterfaceNode::CleanUpEvent(
556 const media_timed_event *event)
558 BMediaEventLooper::CleanUpEvent(event);
562 /* called from Offline mode to determine the current time of the node */
563 /* update your internal information whenever it changes */
564 bigtime_t AbstractFileInterfaceNode::OfflineTime()
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 // }
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 */
581 void AbstractFileInterfaceNode::ControlLoop() {
582 BMediaEventLooper::ControlLoop();
586 // protected:
587 status_t AbstractFileInterfaceNode::HandleStart(
588 const media_timed_event *event,
589 bigtime_t lateness,
590 bool realTimeEvent)
592 CALLED();
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);
602 return B_OK;
606 status_t AbstractFileInterfaceNode::HandleSeek(
607 const media_timed_event *event,
608 bigtime_t lateness,
609 bool realTimeEvent)
611 PRINT("AbstractFileInterfaceNode::HandleSeek(t=%lld,d=%ld,bd=%lld)\n",event->event_time,event->data,event->bigdata);
613 if (fCurrentFile != 0)
614 return fCurrentFile->Seek(event->bigdata,SEEK_SET);
615 else
616 return B_ERROR;
620 status_t AbstractFileInterfaceNode::HandleWarp(
621 const media_timed_event *event,
622 bigtime_t lateness,
623 bool realTimeEvent)
625 CALLED();
626 return B_OK;
630 status_t AbstractFileInterfaceNode::HandleStop(
631 const media_timed_event *event,
632 bigtime_t lateness,
633 bool realTimeEvent)
635 CALLED();
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;
643 status_t AbstractFileInterfaceNode::HandleParameter(
644 const media_timed_event *event,
645 bigtime_t lateness,
646 bool realTimeEvent)
648 CALLED();
650 status_t status = B_OK;
652 bool chunkSizeUpdated = false, bitRateUpdated = false, bufferPeriodUpdated = false;
654 size_t dataSize = size_t(event->data);
655 int64 param = int64(event->bigdata);
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;
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);
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;
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;
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));
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));
770 fDefaultChunkSizeParamChangeTime = fDefaultBitRateParamChangeTime;
771 chunkSizeUpdated = true;
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;
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));
819 fDefaultChunkSizeParamChangeTime = fDefaultBufferPeriodParamChangeTime;
820 chunkSizeUpdated = true;
824 break;
825 default:
826 PRINT("AbstractFileInterfaceNode::HandleParameter called with unknown param id (%ld)\n",param);
827 status = B_ERROR;
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));
838 if (bitRateUpdated) {
839 PRINT("\tbit rate parameter updated\n");
840 BroadcastNewParameterValue(fDefaultBitRateParamChangeTime,
841 DEFAULT_BIT_RATE_PARAM,
842 &fDefaultBitRateParam,
843 sizeof(fDefaultBitRateParam));
845 if (bufferPeriodUpdated) {
846 PRINT("\tbuffer period parameter updated\n");
847 BroadcastNewParameterValue(fDefaultBufferPeriodParamChangeTime,
848 DEFAULT_BUFFER_PERIOD_PARAM,
849 &fDefaultBufferPeriodParam,
850 sizeof(fDefaultBufferPeriodParam));
852 return status;
856 // -------------------------------------------------------- //
857 // AbstractFileInterfaceNode specific functions
858 // -------------------------------------------------------- //
859 void AbstractFileInterfaceNode::GetFlavor(flavor_info * info, int32 id)
861 CALLED();
863 if (info == 0)
864 return;
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;
880 void AbstractFileInterfaceNode::GetFormat(media_format * outFormat)
882 CALLED();
884 if (outFormat == 0)
885 return;
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;
894 void AbstractFileInterfaceNode::GetFileFormat(media_file_format * outFileFormat)
896 CALLED();
898 if (outFileFormat == 0)
899 return;
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';
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.)
928 status_t AbstractFileInterfaceNode::AddRequirements(media_format * format)
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;
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;
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;
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;
950 return B_OK;
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.
956 status_t AbstractFileInterfaceNode::ResolveWildcards(media_format * format)
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;
966 if (format->u.multistream.max_chunk_size == media_multistream_format::wildcard.max_chunk_size) {
967 format->u.multistream.max_chunk_size = fDefaultChunkSizeParam;
969 if (format->u.multistream.avg_bit_rate == media_multistream_format::wildcard.avg_bit_rate) {
970 format->u.multistream.avg_bit_rate = fDefaultBitRateParam;
972 if (format->u.multistream.avg_chunk_size == media_multistream_format::wildcard.avg_chunk_size) {
973 format->u.multistream.avg_chunk_size = fDefaultChunkSizeParam;
975 return B_OK;
979 // -------------------------------------------------------- //
980 // stuffing
981 // -------------------------------------------------------- //
982 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_0(void *) { return B_ERROR; }
983 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_1(void *) { return B_ERROR; }
984 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_2(void *) { return B_ERROR; }
985 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_3(void *) { return B_ERROR; }
986 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_4(void *) { return B_ERROR; }
987 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_5(void *) { return B_ERROR; }
988 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_6(void *) { return B_ERROR; }
989 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_7(void *) { return B_ERROR; }
990 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_8(void *) { return B_ERROR; }
991 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_9(void *) { return B_ERROR; }
992 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_10(void *) { return B_ERROR; }
993 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_11(void *) { return B_ERROR; }
994 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_12(void *) { return B_ERROR; }
995 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_13(void *) { return B_ERROR; }
996 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_14(void *) { return B_ERROR; }
997 status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_15(void *) { return B_ERROR; }