void ESDSinkNode::Disconnect(const media_source& what, const media_destination& where) { CALLED(); node_output *channel = FindOutput(what); // is this our output? if (channel == NULL) { fprintf(stderr, "ESDSinkNode::Disconnect() returning (cause : B_MEDIA_BAD_SOURCE)\n"); return; } // Make sure that our connection is the one being disconnected if ((where == channel->fOutput.destination) && (what == channel->fOutput.source)) { channel->fOutput.destination = media_destination::null; channel->fOutput.format = channel->fPreferredFormat; delete channel->fBufferGroup; channel->fBufferGroup = NULL; } else { fprintf(stderr, "\tDisconnect() called with wrong source/destination (%ld/%ld), ours is (%ld/%ld)\n", what.id, where.id, channel->fOutput.source.id, channel->fOutput.destination.id); } }
status_t ESDSinkNode::FormatProposal(const media_source& output, media_format* format) { // FormatProposal() is the first stage in the BMediaRoster::Connect() process. We hand // out a suggested format, with wildcards for any variations we support. CALLED(); node_output *channel = FindOutput(output); // is this a proposal for our select output? if (channel == NULL) { fprintf(stderr, "ESDSinkNode::FormatProposal returning B_MEDIA_BAD_SOURCE\n"); return B_MEDIA_BAD_SOURCE; } // we only support floating-point raw audio, so we always return that, but we // supply an error code depending on whether we found the proposal acceptable. media_type requestedType = format->type; *format = channel->fPreferredFormat; if ((requestedType != B_MEDIA_UNKNOWN_TYPE) && (requestedType != B_MEDIA_RAW_AUDIO)) { fprintf(stderr, "ESDSinkNode::FormatProposal returning B_MEDIA_BAD_FORMAT\n"); return B_MEDIA_BAD_FORMAT; } else return B_OK; // raw audio or wildcard type, either is okay by us }
void ESDSinkNode::Connect(status_t error, const media_source& source, const media_destination& destination, const media_format& format, char* io_name) { CALLED(); node_output *channel = FindOutput(source); // is this our output? if (channel == NULL) { fprintf(stderr, "ESDSinkNode::Connect returning (cause : B_MEDIA_BAD_SOURCE)\n"); return; } // If something earlier failed, Connect() might still be called, but with a non-zero // error code. When that happens we simply unreserve the connection and do // nothing else. if (error) { channel->fOutput.destination = media_destination::null; channel->fOutput.format = channel->fPreferredFormat; return; } // Okay, the connection has been confirmed. Record the destination and format // that we agreed on, and report our connection name again. channel->fOutput.destination = destination; channel->fOutput.format = format; strncpy(io_name, channel->fOutput.name, B_MEDIA_NAME_LENGTH); // reset our buffer duration, etc. to avoid later calculations bigtime_t duration = channel->fOutput.format.u.raw_audio.buffer_size * 10000 / ( (channel->fOutput.format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK) * channel->fOutput.format.u.raw_audio.channel_count) / ((int32)(channel->fOutput.format.u.raw_audio.frame_rate / 100)); SetBufferDuration(duration); // Now that we're connected, we can determine our downstream latency. // Do so, then make sure we get our events early enough. media_node_id id; FindLatencyFor(channel->fOutput.destination, &fLatency, &id); PRINT(("\tdownstream latency = %Ld\n", fLatency)); fInternalLatency = BufferDuration(); PRINT(("\tbuffer-filling took %Ld usec on this machine\n", fInternalLatency)); //SetEventLatency(fLatency + fInternalLatency); // Set up the buffer group for our connection, as long as nobody handed us a // buffer group (via SetBufferGroup()) prior to this. That can happen, for example, // if the consumer calls SetOutputBuffersFor() on us from within its Connected() // method. if (!channel->fBufferGroup) AllocateBuffers(*channel); // we are sure the thread is started StartThread(); }
BMediaOutput* BMediaClient::FindOutput(const media_connection& output) const { CALLED(); if (!output.IsOutput()) return NULL; return FindOutput(output.Source()); }
status_t ESDSinkNode::PrepareToConnect(const media_source& what, const media_destination& where, media_format* format, media_source* out_source, char* out_name) { // PrepareToConnect() is the second stage of format negotiations that happens // inside BMediaRoster::Connect(). At this point, the consumer's AcceptFormat() // method has been called, and that node has potentially changed the proposed // format. It may also have left wildcards in the format. PrepareToConnect() // *must* fully specialize the format before returning! CALLED(); node_output *channel = FindOutput(what); // is this our output? if (channel == NULL) { fprintf(stderr, "ESDSinkNode::PrepareToConnect returning B_MEDIA_BAD_SOURCE\n"); return B_MEDIA_BAD_SOURCE; } // are we already connected? if (channel->fOutput.destination != media_destination::null) return B_MEDIA_ALREADY_CONNECTED; // the format may not yet be fully specialized (the consumer might have // passed back some wildcards). Finish specializing it now, and return an // error if we don't support the requested format. if (format->type != B_MEDIA_RAW_AUDIO) { fprintf(stderr, "\tnon-raw-audio format?!\n"); return B_MEDIA_BAD_FORMAT; } // !!! validate all other fields except for buffer_size here, because the consumer might have // supplied different values from AcceptFormat()? // check the buffer size, which may still be wildcarded if (format->u.raw_audio.buffer_size == media_raw_audio_format::wildcard.buffer_size) { format->u.raw_audio.buffer_size = 2048; // pick something comfortable to suggest fprintf(stderr, "\tno buffer size provided, suggesting %lu\n", format->u.raw_audio.buffer_size); } else { fprintf(stderr, "\tconsumer suggested buffer_size %lu\n", format->u.raw_audio.buffer_size); } // Now reserve the connection, and return information about it channel->fOutput.destination = where; channel->fOutput.format = *format; *out_source = channel->fOutput.source; strncpy(out_name, channel->fOutput.name, B_MEDIA_NAME_LENGTH); return B_OK; }
void ESDSinkNode::EnableOutput(const media_source& what, bool enabled, int32* _deprecated_) { CALLED(); // If I had more than one output, I'd have to walk my list of output records to see // which one matched the given source, and then enable/disable that one. But this // node only has one output, so I just make sure the given source matches, then set // the enable state accordingly. node_output *channel = FindOutput(what); if (channel != NULL) { channel->fOutputEnabled = enabled; } }
void ESDSinkNode::LateNoticeReceived(const media_source& what, bigtime_t how_much, bigtime_t performance_time) { CALLED(); node_output *channel = FindOutput(what); // is this our output? if (channel == NULL) { return; } // If we're late, we need to catch up. Respond in a manner appropriate to our // current run mode. if (RunMode() == B_RECORDING) { // A hardware capture node can't adjust; it simply emits buffers at // appropriate points. We (partially) simulate this by not adjusting // our behavior upon receiving late notices -- after all, the hardware // can't choose to capture "sooner".... } else if (RunMode() == B_INCREASE_LATENCY) { // We're late, and our run mode dictates that we try to produce buffers // earlier in order to catch up. This argues that the downstream nodes are // not properly reporting their latency, but there's not much we can do about // that at the moment, so we try to start producing buffers earlier to // compensate. fInternalLatency += how_much; SetEventLatency(fLatency + fInternalLatency); fprintf(stderr, "\tincreasing latency to %Ld\n", fLatency + fInternalLatency); } else { // The other run modes dictate various strategies for sacrificing data quality // in the interests of timely data delivery. The way *we* do this is to skip // a buffer, which catches us up in time by one buffer duration. /*size_t nSamples = fOutput.format.u.raw_audio.buffer_size / sizeof(float); mSamplesSent += nSamples;*/ fprintf(stderr, "\tskipping a buffer to try to catch up\n"); } }
status_t ESDSinkNode::SetBufferGroup(const media_source& for_source, BBufferGroup* newGroup) { CALLED(); node_output *channel = FindOutput(for_source); // is this our output? if (channel == NULL) { fprintf(stderr, "ESDSinkNode::SetBufferGroup returning B_MEDIA_BAD_SOURCE\n"); return B_MEDIA_BAD_SOURCE; } // Are we being passed the buffer group we're already using? if (newGroup == channel->fBufferGroup) return B_OK; // Ahh, someone wants us to use a different buffer group. At this point we delete // the one we are using and use the specified one instead. If the specified group is // NULL, we need to recreate one ourselves, and use *that*. Note that if we're // caching a BBuffer that we requested earlier, we have to Recycle() that buffer // *before* deleting the buffer group, otherwise we'll deadlock waiting for that // buffer to be recycled! delete channel->fBufferGroup; // waits for all buffers to recycle if (newGroup != NULL) { // we were given a valid group; just use that one from now on channel->fBufferGroup = newGroup; } else { // we were passed a NULL group pointer; that means we construct // our own buffer group to use from now on size_t size = channel->fOutput.format.u.raw_audio.buffer_size; int32 count = int32(fLatency / BufferDuration() + 1 + 1); channel->fBufferGroup = new BBufferGroup(size, count); } return B_OK; }