// MediaOutputInfo.cpp // // Andrew Bachmann, 2002 // // A class to encapsulate and manipulate // all the information for a particular // output of a media node. #include #include #include #include #include #include "MediaOutputInfo.h" #include "misc.h" MediaOutputInfo::MediaOutputInfo(BBufferProducer * node, char * name) { producer = node; // null some fields bufferGroup = 0; bufferPeriod = 0; // start enabled outputEnabled = true; // don't overwrite available space, and be sure to terminate strncpy(output.name,name,B_MEDIA_NAME_LENGTH-1); output.name[B_MEDIA_NAME_LENGTH-1] = '\0'; // initialize the output output.node = media_node::null; output.source = media_source::null; output.destination = media_destination::null; } MediaOutputInfo::~MediaOutputInfo() { if (bufferGroup != 0) { BBufferGroup * group = bufferGroup; bufferGroup = 0; delete group; } } status_t MediaOutputInfo::SetBufferGroup(BBufferGroup * group) { if (bufferGroup != 0) { if (bufferGroup == group) { return B_OK; // time saver } delete bufferGroup; } bufferGroup = group; } // They made an offer to us. We should make sure that the offer is // acceptable, and then we can add any requirements we have on top of // that. We leave wildcards for anything that we don't care about. status_t MediaOutputInfo::FormatProposal(media_format * format) { if (format == 0) { fprintf(stderr,"<- B_BAD_VALUE\n"); return B_BAD_VALUE; // no crashing } // Be's format_is_compatible doesn't work, // so use our format_is_acceptible instead if (!format_is_acceptible(*format,generalFormat)) { fprintf(stderr,"<- B_MEDIA_BAD_FORMAT\n"); return B_MEDIA_BAD_FORMAT; } // XXX: test because we don't trust them! format->SpecializeTo(&wildcardedFormat); return B_OK; } // Presumably we have already agreed with them that this format is // okay. But just in case, we check the offer. (and complain if it // is invalid) Then as the last thing we do, we get rid of any // remaining wilcards. status_t MediaOutputInfo::FormatChangeRequested(const media_destination & destination, media_format * io_format) { if (io_format == 0) { fprintf(stderr,"<- B_BAD_VALUE\n"); return B_BAD_VALUE; // no crashing } status_t status = FormatProposal(io_format); if (status != B_OK) { fprintf(stderr,"<- MediaOutputInfo::FormatProposal failed\n"); *io_format = generalFormat; return status; } io_format->SpecializeTo(&fullySpecifiedFormat); return B_OK; } status_t MediaOutputInfo::PrepareToConnect(const media_destination & where, media_format * format, media_source * out_source, char * out_name) { if (output.destination != media_destination::null) { fprintf(stderr,"<- B_MEDIA_ALREADY_CONNECTED\n"); return B_MEDIA_ALREADY_CONNECTED; } status_t status = FormatChangeRequested(where,format); if (status != B_OK) { fprintf(stderr,"<- MediaOutputInfo::FormatChangeRequested failed\n"); return status; } *out_source = output.source; output.destination = where; strncpy(out_name,output.name,B_MEDIA_NAME_LENGTH-1); out_name[B_MEDIA_NAME_LENGTH] = '\0'; return B_OK; } status_t MediaOutputInfo::Connect(const media_destination & destination, const media_format & format, char * io_name, bigtime_t _downstreamLatency) { if (io_name == 0) { fprintf(stderr,"<- B_BAD_VALUE\n"); return B_BAD_VALUE; } output.destination = destination; output.format = format; strncpy(io_name,output.name,B_MEDIA_NAME_LENGTH-1); io_name[B_MEDIA_NAME_LENGTH-1] = '\0'; downstreamLatency = _downstreamLatency; // must be set before create buffer group status_t status = CreateBufferGroup(); // also initializes buffer period if (status != B_OK) { output.destination = media_destination::null; output.format = generalFormat; return status; } return B_OK; } status_t MediaOutputInfo::Disconnect() { output.destination = media_destination::null; output.format = generalFormat; if (bufferGroup != 0) { BBufferGroup * group = bufferGroup; bufferGroup = 0; delete group; } } status_t MediaOutputInfo::EnableOutput(bool enabled) { outputEnabled = enabled; return B_OK; } status_t MediaOutputInfo::AdditionalBufferRequested( media_buffer_id prev_buffer, bigtime_t prev_time, const media_seek_tag * prev_tag) { // XXX: implement me return B_OK; } // protected: status_t MediaOutputInfo::CreateBufferGroup() { bufferPeriod = ComputeBufferPeriod(); if (bufferGroup == 0) { int32 count = int32(downstreamLatency/bufferPeriod)+2; fprintf(stderr," downstream latency = %lld, buffer period = %lld, buffer count = %i\n", downstreamLatency,bufferPeriod,count); // allocate the buffers bufferGroup = new BBufferGroup(ComputeBufferSize(),count); if (bufferGroup == 0) { fprintf(stderr,"<- B_NO_MEMORY\n"); return B_NO_MEMORY; } status_t status = bufferGroup->InitCheck(); if (status != B_OK) { fprintf(stderr,"<- BufferGroup initialization failed\n"); BBufferGroup * group = bufferGroup; bufferGroup = 0; delete group; return status; } } return B_OK; } // public: uint32 MediaOutputInfo::ComputeBufferSize() { return ComputeBufferSize(output.format); } // returns result in # of bytes uint32 MediaOutputInfo::ComputeBufferSize(const media_format & format) { uint64 bufferSize = 1024; // default 1024 bytes switch (format.type) { case B_MEDIA_MULTISTREAM: bufferSize = format.u.multistream.max_chunk_size; break; case B_MEDIA_ENCODED_VIDEO: bufferSize = format.u.encoded_video.frame_size; break; case B_MEDIA_RAW_VIDEO: if (format.u.raw_video.interlace == 0) { // okay, you have no fields, you need no space, right? bufferSize = 0; } else { // this is the size of a *field*, not a frame bufferSize = format.u.raw_video.display.bytes_per_row * format.u.raw_video.display.line_count / format.u.raw_video.interlace; } break; case B_MEDIA_ENCODED_AUDIO: bufferSize = format.u.encoded_audio.frame_size; break; case B_MEDIA_RAW_AUDIO: bufferSize = format.u.raw_audio.buffer_size; break; default: break; } if (bufferSize > INT_MAX) { bufferSize = INT_MAX; } return int32(bufferSize); } bigtime_t MediaOutputInfo::ComputeBufferPeriod() { return ComputeBufferPeriod(output.format); } // returns result in # of microseconds bigtime_t MediaOutputInfo::ComputeBufferPeriod(const media_format & format) { bigtime_t bufferPeriod = 25*1000; // default 25 milliseconds switch (format.type) { case B_MEDIA_MULTISTREAM: // given a buffer size of 8192 bytes // and a bitrate of 1024 kilobits/millisecond (= 128 bytes/millisecond) // we need to produce a buffer every 64 milliseconds (= every 64000 microseconds) bufferPeriod = bigtime_t(1000.0 * 8.0 * ComputeBufferSize(format) / format.u.multistream.max_bit_rate); break; case B_MEDIA_ENCODED_VIDEO: bufferPeriod = bigtime_t(1000.0 * 8.0 * ComputeBufferSize(format) / format.u.encoded_video.max_bit_rate); break; case B_MEDIA_ENCODED_AUDIO: bufferPeriod = bigtime_t(1000.0 * 8.0 * ComputeBufferSize(format) / format.u.encoded_audio.bit_rate); break; case B_MEDIA_RAW_VIDEO: // Given a field rate of 50.00 fields per second, (PAL) // we need to generate a field/buffer // every 1/50 of a second = 20000 microseconds. bufferPeriod = bigtime_t(1000000.0 / format.u.raw_video.field_rate); break; case B_MEDIA_RAW_AUDIO: // Given a sample size of 4 bytes [B_AUDIO_INT] // and a channel count of 2 and a buffer_size // of 256 bytes and a frame_rate of 44100 Hertz (1/sec) // 1 frame = 1 sample/channel. // comes to ?? // this is a guess: bufferPeriod = bigtime_t(1000000.0 * ComputeBufferSize(format) / (format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK) / format.u.raw_audio.channel_count / format.u.raw_audio.frame_rate); break; default: break; } return bufferPeriod; }