// AbstractFileInterfaceNode.cpp // // Andrew Bachmann, 2002 // // The AbstractFileInterfaceNode class implements // the common functionality between MediaReader // and MediaWriter. #include "AbstractFileInterfaceNode.h" #include "debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include AbstractFileInterfaceNode::~AbstractFileInterfaceNode(void) { CALLED(); // Stop the BMediaEventLooper thread Quit(); if (fCurrentFile != 0) delete fCurrentFile; } AbstractFileInterfaceNode::AbstractFileInterfaceNode( size_t defaultChunkSize, float defaultBitRate, const flavor_info * info, BMessage * config, BMediaAddOn * addOn) : BMediaNode("AbstractFileInterfaceNode"), BFileInterface(), BControllable(), BMediaEventLooper() { CALLED(); // keep our creator around for AddOn calls later fAddOn = addOn; // null some fields fCurrentFile = 0; f_current_mime_type[0] = '\0'; // initialize the parameters if (defaultChunkSize <= 0) { fInitCheckStatus = B_BAD_VALUE; return; } fDefaultChunkSizeParam = defaultChunkSize; fDefaultChunkSizeParamChangeTime = 0; if (defaultBitRate <= 0) { fInitCheckStatus = B_BAD_VALUE; return; } fDefaultBitRateParam = defaultBitRate; fDefaultBitRateParamChangeTime = 0; // initialize parameter compute fields fLeastRecentlyUpdatedParameter = DEFAULT_BIT_RATE_PARAM; fLastUpdatedParameter = DEFAULT_BUFFER_PERIOD_PARAM; // From the chunk size and bit rate we compute the buffer period. int64 value = int64(8000000/1024*fDefaultChunkSizeParam/fDefaultBitRateParam); if ((value <= 0) || (value > INT_MAX)) { fInitCheckStatus = B_BAD_VALUE; return; } fDefaultBufferPeriodParam = int32(value); fDefaultBufferPeriodParamChangeTime = 0; fInitCheckStatus = B_OK; } status_t AbstractFileInterfaceNode::InitCheck(void) const { CALLED(); return fInitCheckStatus; } status_t AbstractFileInterfaceNode::GetConfigurationFor( BMessage * into_message) { CALLED(); return B_OK; } // -------------------------------------------------------- // // implementation of BMediaNode // -------------------------------------------------------- // BMediaAddOn * AbstractFileInterfaceNode::AddOn( int32 * internal_id) const { CALLED(); // BeBook says this only gets called if we were in an add-on. if (fAddOn != 0) { // If we get a null pointer then we just won't write. if (internal_id != 0) internal_id = 0; } return fAddOn; } void AbstractFileInterfaceNode::Start( bigtime_t performance_time) { PRINT("AbstractFileInterfaceNode::Start(pt=%lld)\n",performance_time); BMediaEventLooper::Start(performance_time); } void AbstractFileInterfaceNode::Stop( bigtime_t performance_time, bool immediate) { PRINT("AbstractFileInterfaceNode::Stop(pt=%lld, im=%d)\n", performance_time, immediate); BMediaEventLooper::Stop(performance_time,immediate); } void AbstractFileInterfaceNode::Seek( bigtime_t media_time, bigtime_t performance_time) { PRINT("AbstractFileInterfaceNode::Seek(mt=%lld,pt=%lld)\n", media_time,performance_time); BMediaEventLooper::Seek(media_time,performance_time); } void AbstractFileInterfaceNode::SetRunMode( run_mode mode) { PRINT("AbstractFileInterfaceNode::SetRunMode(%i)\n",mode); BMediaEventLooper::SetRunMode(mode); } void AbstractFileInterfaceNode::TimeWarp( bigtime_t at_real_time, bigtime_t to_performance_time) { PRINT("AbstractFileInterfaceNode::TimeWarp(rt=%lld,pt=%lld)\n", at_real_time,to_performance_time); BMediaEventLooper::TimeWarp(at_real_time,to_performance_time); } void AbstractFileInterfaceNode::Preroll(void) { CALLED(); // XXX:Performance opportunity BMediaNode::Preroll(); } void AbstractFileInterfaceNode::SetTimeSource( BTimeSource * time_source) { CALLED(); BMediaNode::SetTimeSource(time_source); } status_t AbstractFileInterfaceNode::HandleMessage( int32 message, const void * data, size_t size) { CALLED(); status_t status = B_OK; switch (message) { // no special messages for now default: status = BFileInterface::HandleMessage(message,data,size); if (status == B_OK) { break; } status = BControllable::HandleMessage(message, data, size); if (status == B_OK) break; status = BMediaNode::HandleMessage(message,data,size); if (status == B_OK) { break; } BMediaNode::HandleBadMessage(message,data,size); status = B_ERROR; break; } return status; } status_t AbstractFileInterfaceNode::RequestCompleted( const media_request_info & info) { CALLED(); return BMediaNode::RequestCompleted(info); } status_t AbstractFileInterfaceNode::DeleteHook( BMediaNode * node) { CALLED(); return BMediaEventLooper::DeleteHook(node); } void AbstractFileInterfaceNode::NodeRegistered(void) { CALLED(); // set up our parameter web SetParameterWeb(MakeParameterWeb()); // start the BMediaEventLooper thread SetPriority(B_REAL_TIME_PRIORITY); Run(); } status_t AbstractFileInterfaceNode::GetNodeAttributes( media_node_attribute * outAttributes, size_t inMaxCount) { CALLED(); return BMediaNode::GetNodeAttributes(outAttributes,inMaxCount); } status_t AbstractFileInterfaceNode::AddTimer( bigtime_t at_performance_time, int32 cookie) { CALLED(); return BMediaEventLooper::AddTimer(at_performance_time,cookie); } // protected: BParameterWeb * AbstractFileInterfaceNode::MakeParameterWeb(void) { CALLED(); BParameterWeb * web = new BParameterWeb(); BParameterGroup * mainGroup = web->MakeGroup("AbstractFileInterfaceNode Parameters"); // these three are related: // DEFAULT_CHUNK_SIZE_PARAM = // DEFAULT_BIT_RATE_PARAM / 1024 * DEFAULT_BUFFER_PERIOD_PARAM * 1000 BParameterGroup * chunkSizeGroup = mainGroup->MakeGroup("Chunk Size Group"); BContinuousParameter * chunkSizeParameter = chunkSizeGroup->MakeContinuousParameter( DEFAULT_CHUNK_SIZE_PARAM, B_MEDIA_MULTISTREAM, "Chunk Size", B_GAIN, "bytes", 1024, 32*1024, 512); chunkSizeParameter->SetResponse(BContinuousParameter::B_LINEAR,1,0); chunkSizeParameter->SetValue(&fDefaultChunkSizeParam,sizeof(fDefaultChunkSizeParam),0); BParameterGroup * bitRateGroup = mainGroup->MakeGroup("Bit Rate Group"); BContinuousParameter * bitRateParameter = bitRateGroup->MakeContinuousParameter( DEFAULT_BIT_RATE_PARAM, B_MEDIA_MULTISTREAM, "Bit Rate", B_GAIN, "kbits/sec", 1, 320000, 1); bitRateParameter->SetResponse(BContinuousParameter::B_LINEAR,.001,0); bitRateParameter->SetValue(&fDefaultBitRateParam,sizeof(fDefaultBitRateParam),0); BParameterGroup * bufferPeriodGroup = mainGroup->MakeGroup("Buffer Period Group"); BContinuousParameter * bufferPeriodParameter = bufferPeriodGroup->MakeContinuousParameter( DEFAULT_BUFFER_PERIOD_PARAM, B_MEDIA_MULTISTREAM, "Buffer Period", B_GAIN, "ms", 1, 10000, 1); bufferPeriodParameter->SetResponse(BContinuousParameter::B_LINEAR,1,0); bufferPeriodParameter->SetValue(&fDefaultBufferPeriodParam,sizeof(fDefaultBufferPeriodParam),0); return web; } // -------------------------------------------------------- // // implementation of BFileInterface // -------------------------------------------------------- // status_t AbstractFileInterfaceNode::GetNextFileFormat( int32 * cookie, media_file_format * out_format) { CALLED(); // it's valid but they already got our 1 file format if (*cookie != 0) { PRINT("\t<- B_ERROR\n"); return B_ERROR; } // so next time they won't get the same format again *cookie = 1; GetFileFormat(out_format); return B_OK; } void AbstractFileInterfaceNode::DisposeFileFormatCookie( int32 cookie) { CALLED(); // nothing to do since our cookies are just integers } status_t AbstractFileInterfaceNode::GetDuration( bigtime_t * out_time) { CALLED(); if (out_time == 0) { PRINT("\t<- B_BAD_VALUE\n"); return B_BAD_VALUE; } if (fCurrentFile == 0) { PRINT("\t<- B_NO_INIT\n"); return B_NO_INIT; } return fCurrentFile->GetSize(out_time); } status_t AbstractFileInterfaceNode::SniffRef( const entry_ref & file, char * out_mime_type, /* 256 bytes */ float * out_quality) { CALLED(); return StaticSniffRef(file,out_mime_type,out_quality); } status_t AbstractFileInterfaceNode::SetRef( const entry_ref & file, uint32 openMode, bool create, bigtime_t * out_time) { CALLED(); status_t status; f_current_ref = file; if (fCurrentFile == 0) { fCurrentFile = new BFile(&f_current_ref,(openMode|(create?B_CREATE_FILE:0))); status = fCurrentFile->InitCheck(); } else { status = fCurrentFile->SetTo(&f_current_ref,(openMode|(create?B_CREATE_FILE:0))); } if (status != B_OK) { PRINT("\t<- failed BFile initialization\n"); return status; } // cache the mime type for later fCurrentFile->ReadAttr("BEOS:TYPE",0,0,f_current_mime_type,B_MIME_TYPE_LENGTH); // compute the duration and return any error return GetDuration(out_time); } status_t AbstractFileInterfaceNode::GetRef( entry_ref * out_ref, char * out_mime_type) { CALLED(); if (fCurrentFile == 0) { PRINT("\t<- B_NO_INIT\n"); return B_NO_INIT; // the input_ref isn't valid yet either } *out_ref = f_current_ref; // they hopefully allocated enough space (no way to check :-/ ) strcpy(out_mime_type,f_current_mime_type); return B_OK; } // provided for BAbstractFileInterfaceNodeAddOn status_t AbstractFileInterfaceNode::StaticSniffRef( const entry_ref & file, char * out_mime_type, /* 256 bytes */ float * out_quality) { CALLED(); BNode node(&file); status_t initCheck = node.InitCheck(); if (initCheck != B_OK) { PRINT("\t<- failed BNode::InitCheck()\n"); return initCheck; } // they hopefully allocated enough room node.ReadAttr("BEOS:TYPE",0,0,out_mime_type,B_MIME_TYPE_LENGTH); *out_quality = 1.0; // we handle all files perfectly! we are so amazing! return B_OK; } // -------------------------------------------------------- // // implementation for BControllable // -------------------------------------------------------- // const int32 AbstractFileInterfaceNode::DEFAULT_CHUNK_SIZE_PARAM = 1; const int32 AbstractFileInterfaceNode::DEFAULT_BIT_RATE_PARAM = 2; const int32 AbstractFileInterfaceNode::DEFAULT_BUFFER_PERIOD_PARAM = 3; status_t AbstractFileInterfaceNode::GetParameterValue( int32 id, bigtime_t * last_change, void * value, size_t * ioSize) { CALLED(); switch (id) { case DEFAULT_CHUNK_SIZE_PARAM: if (*ioSize < sizeof(size_t)) { return B_ERROR; // not enough room } *last_change = fDefaultChunkSizeParamChangeTime; *((size_t*)value) = fDefaultChunkSizeParam; *ioSize = sizeof(size_t); break; case DEFAULT_BIT_RATE_PARAM: if (*ioSize < sizeof(float)) { return B_ERROR; // not enough room } *last_change = fDefaultBitRateParamChangeTime; *((float*)value) = fDefaultBitRateParam; *ioSize = sizeof(float); break; case DEFAULT_BUFFER_PERIOD_PARAM: if (*ioSize < sizeof(int32)) { return B_ERROR; // not enough room } *last_change = fDefaultBufferPeriodParamChangeTime; *((int32*)value) = fDefaultBufferPeriodParam; *ioSize = sizeof(int32); break; default: PRINT("AbstractFileInterfaceNode::GetParameterValue unknown id (%ld)\n",id); return B_ERROR; } return B_OK; } void AbstractFileInterfaceNode::SetParameterValue( int32 id, bigtime_t when, const void * value, size_t size) { PRINT("AbstractFileInterfaceNode::SetParameterValue(id=%ld,when=%lld,size=%ld)\n",id,when,int32(size)); switch (id) { case DEFAULT_CHUNK_SIZE_PARAM: case DEFAULT_BIT_RATE_PARAM: case DEFAULT_BUFFER_PERIOD_PARAM: { media_timed_event event(when, BTimedEventQueue::B_PARAMETER, NULL, BTimedEventQueue::B_NO_CLEANUP, size, id, (char*) value, size); EventQueue()->AddEvent(event); } break; default: PRINT("AbstractFileInterfaceNode::SetParameterValue unknown id (%ld)\n",id); break; } } // the default implementation should call the add-on main() status_t AbstractFileInterfaceNode::StartControlPanel( BMessenger * out_messenger) { CALLED(); return BControllable::StartControlPanel(out_messenger); } // -------------------------------------------------------- // // implementation for BMediaEventLooper // -------------------------------------------------------- // void AbstractFileInterfaceNode::HandleEvent( const media_timed_event *event, bigtime_t lateness, bool realTimeEvent) { CALLED(); switch (event->type) { case BTimedEventQueue::B_START: HandleStart(event,lateness,realTimeEvent); break; case BTimedEventQueue::B_SEEK: HandleSeek(event,lateness,realTimeEvent); break; case BTimedEventQueue::B_WARP: HandleWarp(event,lateness,realTimeEvent); break; case BTimedEventQueue::B_STOP: HandleStop(event,lateness,realTimeEvent); break; case BTimedEventQueue::B_HANDLE_BUFFER: if (RunState() == BMediaEventLooper::B_STARTED) { HandleBuffer(event,lateness,realTimeEvent); } break; case BTimedEventQueue::B_DATA_STATUS: HandleDataStatus(event,lateness,realTimeEvent); break; case BTimedEventQueue::B_PARAMETER: HandleParameter(event,lateness,realTimeEvent); break; default: PRINT(" unknown event type: %ld\n",event->type); break; } } /* override to clean up custom events you have added to your queue */ void AbstractFileInterfaceNode::CleanUpEvent( const media_timed_event *event) { BMediaEventLooper::CleanUpEvent(event); } /* called from Offline mode to determine the current time of the node */ /* update your internal information whenever it changes */ bigtime_t AbstractFileInterfaceNode::OfflineTime() { CALLED(); return BMediaEventLooper::OfflineTime(); // XXX: do something else? // if (inputFile == 0) { // return 0; // } else { // return inputFile->Position(); // } } /* override only if you know what you are doing! */ /* otherwise much badness could occur */ /* the actual control loop function: */ /* waits for messages, Pops events off the queue and calls DispatchEvent */ void AbstractFileInterfaceNode::ControlLoop() { BMediaEventLooper::ControlLoop(); } // protected: status_t AbstractFileInterfaceNode::HandleStart( const media_timed_event *event, bigtime_t lateness, bool realTimeEvent) { CALLED(); if (RunState() != B_STARTED) { // XXX: Either use the following line or the lines that are not commented. // There doesn't seem to be a practical difference that i can tell. // HandleBuffer(event,lateness,realTimeEvent); media_timed_event firstBufferEvent(event->event_time, BTimedEventQueue::B_HANDLE_BUFFER); HandleEvent(&firstBufferEvent, 0, false); EventQueue()->AddEvent(firstBufferEvent); } return B_OK; } status_t AbstractFileInterfaceNode::HandleSeek( const media_timed_event *event, bigtime_t lateness, bool realTimeEvent) { PRINT("AbstractFileInterfaceNode::HandleSeek(t=%lld,d=%ld,bd=%lld)\n",event->event_time,event->data,event->bigdata); if (fCurrentFile != 0) return fCurrentFile->Seek(event->bigdata,SEEK_SET); else return B_ERROR; } status_t AbstractFileInterfaceNode::HandleWarp( const media_timed_event *event, bigtime_t lateness, bool realTimeEvent) { CALLED(); return B_OK; } status_t AbstractFileInterfaceNode::HandleStop( const media_timed_event *event, bigtime_t lateness, bool realTimeEvent) { CALLED(); // flush the queue so downstreamers don't get any more EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER); return B_OK; } status_t AbstractFileInterfaceNode::HandleParameter( const media_timed_event *event, bigtime_t lateness, bool realTimeEvent) { CALLED(); status_t status = B_OK; bool chunkSizeUpdated = false, bitRateUpdated = false, bufferPeriodUpdated = false; size_t dataSize = size_t(event->data); int64 param = int64(event->bigdata); switch (param) { case DEFAULT_CHUNK_SIZE_PARAM: PRINT("(DEFAULT_CHUNK_SIZE_PARAM,size=%ld",dataSize); if (dataSize < sizeof(size_t)) { PRINT("\ลง<- B_BAD_VALUE: %lld\n",param); status = B_BAD_VALUE; } else { size_t newDefaultChunkSize = *((size_t*)event->user_data); PRINT(",%ld)\n", newDefaultChunkSize); // ignore non positive chunk sizes // XXX: we may decide later that a 0 chunk size means ship the whole file in one chunk (!) if ((newDefaultChunkSize > 0) && (newDefaultChunkSize != fDefaultChunkSizeParam)) { PRINT("\tgot a new chunk size, old chunk size was %ld\n",fDefaultChunkSizeParam); fDefaultChunkSizeParam = newDefaultChunkSize; fDefaultChunkSizeParamChangeTime = TimeSource()->Now(); chunkSizeUpdated = true; if (fLeastRecentlyUpdatedParameter == DEFAULT_CHUNK_SIZE_PARAM) { // Okay we were the least recently updated parameter, // but we just got an update so we are no longer that. // Let's figure out who the new least recently updated // parameter is. We are going to prefer to compute the // bit rate since you usually don't want to muck with // the buffer period. However, if you just set the bitrate // then we are stuck with making the buffer period the new // parameter to be computed. if (fLastUpdatedParameter == DEFAULT_BIT_RATE_PARAM) fLeastRecentlyUpdatedParameter = DEFAULT_BUFFER_PERIOD_PARAM; else fLeastRecentlyUpdatedParameter = DEFAULT_BIT_RATE_PARAM; } // we just got an update, so we are the new lastUpdatedParameter fLastUpdatedParameter = DEFAULT_CHUNK_SIZE_PARAM; // now we have to compute the new value for the leastRecentlyUpdatedParameter // we use the chunk size change time to preserve "simultaneity" information if (fLeastRecentlyUpdatedParameter == DEFAULT_BUFFER_PERIOD_PARAM) { int64 value = int64(8000000/1024*fDefaultChunkSizeParam/fDefaultBitRateParam); if (value > INT_MAX) { // clamp to INT_MAX fDefaultBufferPeriodParam = INT_MAX; // recompute chunk size fDefaultChunkSizeParam = size_t(1024/8000000*fDefaultBitRateParam*fDefaultBufferPeriodParam); } else { fDefaultBufferPeriodParam = MAX(1,value); } fDefaultBufferPeriodParamChangeTime = fDefaultChunkSizeParamChangeTime; bufferPeriodUpdated = true; } else { // must have been bit rate fDefaultBitRateParam = MAX(0.001,8000000/1024*fDefaultChunkSizeParam/fDefaultBufferPeriodParam); fDefaultBitRateParamChangeTime = fDefaultChunkSizeParamChangeTime; bitRateUpdated = true; } } } break; case DEFAULT_BIT_RATE_PARAM: PRINT("(DEFAULT_BIT_RATE_PARAM,size=%ld",dataSize); if (dataSize < sizeof(float)) { PRINT("\t<- B_BAD_VALUE:lld\n",param); status = B_BAD_VALUE; } else { float newDefaultBitRate = *((float*)event->user_data); PRINT(",%f)\n",newDefaultBitRate); // ignore non positive bitrates if ((newDefaultBitRate > 0) && (newDefaultBitRate != fDefaultBitRateParam)) { PRINT("\tgot a new bit rate, old bit rate was %ld\n",fDefaultBitRateParam); fDefaultBitRateParam = newDefaultBitRate; fDefaultBitRateParamChangeTime = TimeSource()->Now(); bitRateUpdated = true; if (fLeastRecentlyUpdatedParameter == DEFAULT_BIT_RATE_PARAM) { // Okay we were the least recently updated parameter, // but we just got an update so we are no longer that. // Let's figure out who the new least recently updated // parameter is. We are going to prefer to compute the // chunk size since you usually don't want to muck with // the buffer period. However, if you just set the chunk size // then we are stuck with making the buffer period the new // parameter to be computed. if (fLastUpdatedParameter == DEFAULT_CHUNK_SIZE_PARAM) { fLeastRecentlyUpdatedParameter = DEFAULT_BUFFER_PERIOD_PARAM; } else { fLeastRecentlyUpdatedParameter = DEFAULT_CHUNK_SIZE_PARAM; } } // we just got an update, so we are the new lastUpdatedParameter fLastUpdatedParameter = DEFAULT_BIT_RATE_PARAM; // now we have to compute the new value for the leastRecentlyUpdatedParameter // we use the bit rate change time to preserve "simultaneity" information if (fLeastRecentlyUpdatedParameter == DEFAULT_BUFFER_PERIOD_PARAM) { int64 value = int64(8000000/1024*fDefaultChunkSizeParam/fDefaultBitRateParam); if (value > INT_MAX) { // clamp to INT_MAX fDefaultBufferPeriodParam = INT_MAX; // recompute bit rate fDefaultBitRateParam = MAX(0.001, 8000000/1024*fDefaultChunkSizeParam/fDefaultBufferPeriodParam); } else { fDefaultBufferPeriodParam = MAX(1,int32(value)); } fDefaultBufferPeriodParamChangeTime = fDefaultBitRateParamChangeTime; bufferPeriodUpdated = true; } else { // must have been chunk size int64 value = int64(1024/8000000*fDefaultBitRateParam*fDefaultBufferPeriodParam); if (value > INT_MAX) { // clamp to INT_MAX fDefaultChunkSizeParam = INT_MAX; // recompute bit rate fDefaultBitRateParam = MAX(0.001,8000000/1024*fDefaultChunkSizeParam/fDefaultBufferPeriodParam); } else { fDefaultChunkSizeParam = MAX(1,int32(value)); } fDefaultChunkSizeParamChangeTime = fDefaultBitRateParamChangeTime; chunkSizeUpdated = true; } } } break; case DEFAULT_BUFFER_PERIOD_PARAM: PRINT("(DEFAULT_BUFFER_PERIOD_PARAM,size=%ld",dataSize); if (dataSize < sizeof(int32)) { PRINT("\t<- B_BAD_VALUE:%ld\n",param); status = B_BAD_VALUE; } else { int32 newBufferPeriod = *((int32*)event->user_data); PRINT(",%ld)\n",newBufferPeriod); // ignore non positive buffer period if ((newBufferPeriod > 0) && (newBufferPeriod != fDefaultBufferPeriodParam)) { PRINT("\tgot a new buffer period, old buffer period was %ld\n", fDefaultBufferPeriodParam); fDefaultBufferPeriodParam = newBufferPeriod; fDefaultBufferPeriodParamChangeTime = TimeSource()->Now(); bufferPeriodUpdated = true; if (fLastUpdatedParameter == DEFAULT_BUFFER_PERIOD_PARAM) { // prefer to update bit rate, unless you just set it if (fLastUpdatedParameter == DEFAULT_BIT_RATE_PARAM) { fLeastRecentlyUpdatedParameter = DEFAULT_CHUNK_SIZE_PARAM; } else { fLeastRecentlyUpdatedParameter = DEFAULT_BIT_RATE_PARAM; } } // we just got an update, so we are the new lastUpdatedParameter fLastUpdatedParameter = DEFAULT_BUFFER_PERIOD_PARAM; // now we have to compute the new value for the leastRecentlyUpdatedParameter // we use the buffer period change time to preserve "simultaneity" information if (fLeastRecentlyUpdatedParameter == DEFAULT_BIT_RATE_PARAM) { fDefaultBitRateParam = MAX(0.001,8000000/1024*fDefaultChunkSizeParam/fDefaultBufferPeriodParam); fDefaultBitRateParamChangeTime = fDefaultBufferPeriodParamChangeTime; bitRateUpdated = true; } else { // must have been chunk size int64 value = int64(1024/8000000*fDefaultBitRateParam*fDefaultBufferPeriodParam); if (value > INT_MAX) { // clamp to INT_MAX fDefaultChunkSizeParam = INT_MAX; // recompute buffer period fDefaultBufferPeriodParam = size_t(8000000/1024*fDefaultChunkSizeParam/fDefaultBitRateParam); } else { fDefaultChunkSizeParam = MAX(1,int32(value)); } fDefaultChunkSizeParamChangeTime = fDefaultBufferPeriodParamChangeTime; chunkSizeUpdated = true; } } } break; default: PRINT("AbstractFileInterfaceNode::HandleParameter called with unknown param id (%ld)\n",param); status = B_ERROR; } // send updates out for all the parameters that changed // in every case this should be two updates. (if I have not made an error :-) ) if (chunkSizeUpdated) { PRINT("\tchunk size parameter updated\n"); BroadcastNewParameterValue(fDefaultChunkSizeParamChangeTime, DEFAULT_CHUNK_SIZE_PARAM, &fDefaultChunkSizeParam, sizeof(fDefaultChunkSizeParam)); } if (bitRateUpdated) { PRINT("\tbit rate parameter updated\n"); BroadcastNewParameterValue(fDefaultBitRateParamChangeTime, DEFAULT_BIT_RATE_PARAM, &fDefaultBitRateParam, sizeof(fDefaultBitRateParam)); } if (bufferPeriodUpdated) { PRINT("\tbuffer period parameter updated\n"); BroadcastNewParameterValue(fDefaultBufferPeriodParamChangeTime, DEFAULT_BUFFER_PERIOD_PARAM, &fDefaultBufferPeriodParam, sizeof(fDefaultBufferPeriodParam)); } return status; } // -------------------------------------------------------- // // AbstractFileInterfaceNode specific functions // -------------------------------------------------------- // void AbstractFileInterfaceNode::GetFlavor(flavor_info * info, int32 id) { CALLED(); if (info == 0) return; info->name = strdup("AbstractFileInterfaceNode"); info->info = strdup("A AbstractFileInterfaceNode node handles a file."); info->kinds = B_FILE_INTERFACE | B_CONTROLLABLE; info->flavor_flags = B_FLAVOR_IS_LOCAL; info->possible_count = INT_MAX; info->in_format_count = 0; // no inputs info->in_formats = 0; info->out_format_count = 0; // no outputs info->out_formats = 0; info->internal_id = id; return; } void AbstractFileInterfaceNode::GetFormat(media_format * outFormat) { CALLED(); if (outFormat == 0) return; outFormat->type = B_MEDIA_MULTISTREAM; outFormat->require_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS; outFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS; outFormat->u.multistream = media_multistream_format::wildcard; } void AbstractFileInterfaceNode::GetFileFormat(media_file_format * outFileFormat) { CALLED(); if (outFileFormat == 0) return; outFileFormat->capabilities = media_file_format::B_PERFECTLY_SEEKABLE | media_file_format::B_IMPERFECTLY_SEEKABLE | media_file_format::B_KNOWS_ANYTHING; /* I don't know what to initialize this to. (or if I should) */ // format.id = outFileFormat->family = B_ANY_FORMAT_FAMILY; outFileFormat->version = 100; // see media_file_format in for limits strncpy(outFileFormat->mime_type,"",63); outFileFormat->mime_type[63]='\0'; strncpy(outFileFormat->pretty_name,"any media file format",63); outFileFormat->pretty_name[63]='\0'; strncpy(outFileFormat->short_name,"any",31); outFileFormat->short_name[31]='\0'; strncpy(outFileFormat->file_extension,"",7); outFileFormat->file_extension[7]='\0'; } // protected: // Here we make some guesses based on the file's mime type. // We don't have enough information to add any other requirements. // This function doesn't complain if you have already decided you want // the stream to be considered a different one. (so you can say that you // want to read that mpeg file as avi if you are so nutty.) // status_t AbstractFileInterfaceNode::AddRequirements(media_format * format) { if (strcmp("video/x-msvideo",f_current_mime_type) == 0) { if (format->u.multistream.format == media_multistream_format::wildcard.format) { format->u.multistream.format = media_multistream_format::B_AVI; } } else if (strcmp("video/mpeg",f_current_mime_type) == 0) { if (format->u.multistream.format == media_multistream_format::wildcard.format) { format->u.multistream.format = media_multistream_format::B_MPEG1; } } else if (strcmp("video/quicktime",f_current_mime_type) == 0) { if (format->u.multistream.format == media_multistream_format::wildcard.format) { format->u.multistream.format = media_multistream_format::B_QUICKTIME; } } else if (strcmp("audio/x-mpeg",f_current_mime_type) == 0) { if (format->u.multistream.format == media_multistream_format::wildcard.format) { format->u.multistream.format = media_multistream_format::B_MPEG1; } } return B_OK; } // We need some sort of bit rate and chunk size, so if the other guy // didn't care, we'll use our own defaults. status_t AbstractFileInterfaceNode::ResolveWildcards(media_format * format) { CALLED(); // There isn't an unknown format. hmph. // if (format->u.multistream.format == media_multistream_format::wildcard.format) { // format->u.multistream.format = media_multistream_format::B_UNKNOWN; // } if (format->u.multistream.max_bit_rate == media_multistream_format::wildcard.max_bit_rate) { format->u.multistream.max_bit_rate = fDefaultBitRateParam; } if (format->u.multistream.max_chunk_size == media_multistream_format::wildcard.max_chunk_size) { format->u.multistream.max_chunk_size = fDefaultChunkSizeParam; } if (format->u.multistream.avg_bit_rate == media_multistream_format::wildcard.avg_bit_rate) { format->u.multistream.avg_bit_rate = fDefaultBitRateParam; } if (format->u.multistream.avg_chunk_size == media_multistream_format::wildcard.avg_chunk_size) { format->u.multistream.avg_chunk_size = fDefaultChunkSizeParam; } return B_OK; } // -------------------------------------------------------- // // stuffing // -------------------------------------------------------- // status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_0(void *) { return B_ERROR; } status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_1(void *) { return B_ERROR; } status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_2(void *) { return B_ERROR; } status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_3(void *) { return B_ERROR; } status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_4(void *) { return B_ERROR; } status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_5(void *) { return B_ERROR; } status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_6(void *) { return B_ERROR; } status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_7(void *) { return B_ERROR; } status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_8(void *) { return B_ERROR; } status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_9(void *) { return B_ERROR; } status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_10(void *) { return B_ERROR; } status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_11(void *) { return B_ERROR; } status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_12(void *) { return B_ERROR; } status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_13(void *) { return B_ERROR; } status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_14(void *) { return B_ERROR; } status_t AbstractFileInterfaceNode::_Reserved_AbstractFileInterfaceNode_15(void *) { return B_ERROR; }