📄 soundconsumer.cxx
字号:
}#if !ACCEPT_ANY_FORMAT_CHANGE // If we're already connected, and this format doesn't go with the // format in effect, we dont' accept this new format. if (!format_is_compatible(*format, m_input.format)) { *format = m_input.format; return B_MEDIA_BAD_FORMAT; }#endif // I guess we're OK by now, because we have no particular needs as // far as frame rate, sample format, etc go. return B_OK;}status_t SoundConsumer::GetNextInput( int32 * cookie, media_input * out_input){ NODE(stderr, "SoundConsumer: GetNextInput()\n"); // The "next" is kind of misleading, since it's also used for // getting the first (and only) input. if (!*cookie) { if (m_input.source == media_source::null) { // If there's no current connection, make sure we return a // reasonable format telling the world we accept any raw audio. m_input.format.type = B_MEDIA_RAW_AUDIO; m_input.format.u.raw_audio = media_raw_audio_format::wildcard; m_input.node = Node(); m_input.destination.port = ControlPort(); m_input.destination.id = 1; } *out_input = m_input; *cookie = 1; return B_OK; } // There's only one input. return B_BAD_INDEX;}void SoundConsumer::DisposeInputCookie( int32 /* cookie */){ // We didn't allocate any memory or set any state in GetNextInput() // so this function is a no-op.}void SoundConsumer::BufferReceived( BBuffer * buffer){ NODE(stderr, "SoundConsumer::BufferReceived()\n"); // Whee, a buffer! Update the seek info, if necessary. if (m_seeking && buffer->Header()->start_time >= m_tpSeekAt) { m_delta = m_tpSeekAt - m_tmSeekTo; m_seeking = false; } // If there is a record hook, let the interested party have at it! if (m_recordHook) { (*m_recordHook)(m_cookie, buffer->Header()->start_time-m_delta, buffer->Data(), buffer->Header()->size_used, m_input.format.u.raw_audio); } else { Record(buffer->Header()->start_time-m_delta, buffer->Data(), buffer->Header()->size_used, m_input.format.u.raw_audio); } // Buffers should ALWAYS be recycled, else whomever is producing them // will starve. buffer->Recycle();}void SoundConsumer::ProducerDataStatus( const media_destination & for_whom, int32 status, bigtime_t at_media_time){ if (for_whom == m_input.destination) { // Tell whomever is interested that the upstream producer will or won't // send more data in the immediate future. if (m_notifyHook) { (*m_notifyHook)(m_cookie, B_PRODUCER_DATA_STATUS, status, at_media_time); } else { Notify(B_PRODUCER_DATA_STATUS, status, at_media_time); } }}status_t SoundConsumer::GetLatencyFor( const media_destination & for_whom, bigtime_t * out_latency, media_node_id * out_timesource){ // We only accept requests for the one-and-only input of our Node. if (for_whom != m_input.destination) { return B_MEDIA_BAD_DESTINATION; } // Tell the world about our latency information (overridable by user). *out_latency = TotalLatency(); *out_timesource = TimeSource()->Node().node; return B_OK;}status_t SoundConsumer::Connected( const media_source & producer, const media_destination & where, const media_format & with_format, media_input * out_input){ NODE(stderr, "SoundConsumer::Connected()\n"); // Only accept connection requests when we're not already connected. if (m_input.source != media_source::null) { return B_MEDIA_BAD_DESTINATION; } // Only accept connection requests on the one-and-only available input. if (where != m_input.destination) { return B_MEDIA_BAD_DESTINATION; } // Other than that, we accept pretty much anything. The format has been // pre-cleared through AcceptFormat(), and we accept any format anyway. m_input.source = producer; m_input.format = with_format; // Tell whomever is interested that there's now a connection. if (m_notifyHook) { (*m_notifyHook)(m_cookie, B_CONNECTED, m_input.name); } else { Notify(B_CONNECTED, m_input.name); } // This is the most important line -- return our connection information // to the world so it can use it! *out_input = m_input; return B_OK;}void SoundConsumer::Disconnected( const media_source & producer, const media_destination & where){ // We can't disconnect something which isn't us. if (where != m_input.destination) { return; } // We can't disconnect from someone who isn't connected to us. if (producer != m_input.source) { return; } // Tell the interested party that it's time to leave. if (m_notifyHook) { (*m_notifyHook)(m_cookie, B_DISCONNECTED); } else { Notify(B_DISCONNECTED); } // Mark ourselves as not-connected. m_input.source = media_source::null;}status_t SoundConsumer::FormatChanged( const media_source & producer, const media_destination & consumer, int32 from_change_count, const media_format & format){ NODE(stderr, "SoundConsumer::Connected()\n"); // The up-stream guy feels like changing the format. If we can accept // arbitrary format changes, we just say "OK". If, however, we're recording // to a file, that's not such a good idea; we only accept format changes // that are compatible with the format we're already using. You set this // behaviour at compile time by defining ACCEPT_ANY_FORMAT_CHANGE to 1 or 0. status_t err = B_OK;#if ACCEPT_ANY_FORMAT_CHANGE media_format fmt(format); err = AcceptFormat(m_input.destination, &fmt);#else if (m_input.source != media_source::null) { err = format_is_compatible(format, m_input.format) ? B_OK : B_MEDIA_BAD_FORMAT; }#endif if (err >= B_OK) { m_input.format = format; if (m_notifyHook) { (*m_notifyHook)(m_cookie, B_FORMAT_CHANGED, &m_input.format.u.raw_audio); } else { Notify(B_FORMAT_CHANGED, &m_input.format.u.raw_audio); } } return err;}voidSoundConsumer::DoHookChange( void * msg){ // Tell the old guy we're changing the hooks ... if (m_notifyHook) { (*m_notifyHook)(m_cookie, B_HOOKS_CHANGED); } else { Notify(B_HOOKS_CHANGED); } // ... and then do it. set_hooks_q * ptr = (set_hooks_q *)msg; m_recordHook = ptr->process; m_notifyHook = ptr->notify; m_cookie = ptr->cookie;}status_tSoundConsumer::ThreadEntry( void * obj){ ((SoundConsumer *)obj)->ServiceThread(); return 0;}voidSoundConsumer::ServiceThread(){ // The Big Bad ServiceThread receives messages aimed at this // Node and dispatches them (typically to HandleMessage()). // If we were a Producer, we might have to do finicky timing and // queued Start()/Stop() processing in here. But we ain't. // A media kit message will never be bigger than B_MEDIA_MESSAGE_SIZE. // Avoid wasing stack space by dynamically allocating at start. char * msg = new char[B_MEDIA_MESSAGE_SIZE]; // Make sure we clean up this data when we exit the function. array_delete<char> msg_delete(msg); int bad = 0; while (true) { // Call read_port_etc() with a timeout derived from a virtual function, // to allow clients to do special processing if necessary. bigtime_t timeout = Timeout(); int32 code = 0; status_t err = read_port_etc(m_port, &code, msg, B_MEDIA_MESSAGE_SIZE, B_TIMEOUT, timeout); MESSAGE(stderr, "SoundConsumer::ServiceThread() port %ld message %#010lx\n", m_port, code); // If we received a message, err will be the size of the message (including 0). if (err >= 0) { // Real messages reset the timeout time. m_trTimeout = 0; bad = 0; // Check for our private stop message. if (code == MSG_QUIT_NOW) { if (m_notifyHook) { (*m_notifyHook)(m_cookie, B_NODE_DIES, 0); } else { Notify(B_NODE_DIES, 0); } break; } // Else check for our private change-hooks message. else if (code == MSG_CHANGE_HOOKS) { DoHookChange(msg); // Write acknowledge to waiting thread. write_port(((set_hooks_q *)msg)->reply, 0, 0, 0); } // Else it has to be a regular media kit message; go ahead and // dispatch it. else { HandleMessage(code, msg, err); } } // Timing out means that there was no buffer. Tell the interested party. else if (err == B_TIMED_OUT) { if (m_notifyHook) { (*m_notifyHook)(m_cookie, B_OP_TIMED_OUT, timeout); } else { Notify(B_OP_TIMED_OUT, timeout); } } // Other errors are bad. else { FPRINTF(stderr, "SoundConsumer: error %#010lx\n", err); bad++; // If we receive three bad reads with no good messages inbetween, // things are probably not going to improve (like the port disappeared // or something) so we call it a day. if (bad > 3) { if (m_notifyHook) { (*m_notifyHook)(m_cookie, B_NODE_DIES, bad, err, code, msg); } else { Notify(B_NODE_DIES, bad, err, code, msg); } break; } } }}bigtime_tSoundConsumer::Timeout(){ // Timeout() is called for each call to read_port_etc() in the service // thread to figure out a reasonable time-out value. The default behaviour // we've picked is to exponentially back off from one second and upwards. // While it's true that 44 back-offs will run us out of precision in a // bigtime_t, the time to actually reach 44 consecutive back-offs is longer // than the expected market longevity of just about any piece of real estate. // Is that the sound of an impending year-fifteen-million software problem? :-) m_trTimeout = (m_trTimeout < 1000000) ? 1000000 : m_trTimeout*2; return m_trTimeout;}bigtime_tSoundConsumer::ProcessingLatency(){ // We're saying it takes us 500 us to process each buffer. If all we do is // copy the data, it probably takes much less than that, but it doesn't // hurt to be slightly conservative. return 500LL;}bigtime_tSoundConsumer::TotalLatency(){ // Had we been a producer that passes buffers on, we'd have to // include downstream latency in this value. But we are not. return ProcessingLatency();}voidSoundConsumer::Record( bigtime_t /* time */, const void * /* data */, size_t /* size */, const media_raw_audio_format & /* format */){ // If there is no record hook installed, we instead call this function // for received buffers.}voidSoundConsumer::Notify( int32 /* cause */, ...){ // If there is no notification hook installed, we instead call this function // for giving notification of various events.}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -