📄 soundconsumer.cxx
字号:
//// (c) Yuri Kiryanov, openh323@kiryanov.com// for www.Openh323.org by Equivalence//// Portions: 1998-1999, Be Incorporated//#include <stdio.h>#include <OS.h>#include <scheduler.h>#include <Buffer.h>#include <TimeSource.h>#include "SoundPrivate.h"#include "SoundConsumer.h"template<class C> class array_delete { C * & m_ptr;public: // auto_ptr<> uses delete, not delete[], so we have to write our own. // I like hanging on to a reference, because if we manually delete the // array and set the pointer to NULL (or otherwise change the pointer) // it will still work. Others like the more elaborate implementation // of auto_ptr<>. Your Mileage May Vary. array_delete(C * & ptr) : m_ptr(ptr) {} ~array_delete() { delete[] m_ptr; }};// If we don't mind the format changing to another format while// running, we can define this to 1. Look for the symbol down in the source.#define ACCEPT_ANY_FORMAT_CHANGE 0// Compiling with NDEBUG means "release" -- it also turns off assert() and// other such debugging aids. By contrast, DEBUG turns on extra debugging// in some programs.#if !NDEBUG#define FPRINTF fprintf#else#define FPRINTF (void)#endif// Comment out the FPRINTF part of these lines to reduce verbiage.// Enabling MESSAGE will kill performance on slower machines, because it// prints for each message received (including each buffer).#define NODE //FPRINTF#define MESSAGE //FPRINTFSoundConsumer::SoundConsumer( const char * name, SoundProcessFunc recordFunc, SoundNotifyFunc notifyFunc, void * cookie) : BMediaNode(name ? name : "SoundConsumer"), BBufferConsumer(B_MEDIA_RAW_AUDIO){ NODE(stderr, "SoundConsumer::SoundConsumer(%p, %p, %p, %p)\n", name, recordFunc, notifyFunc, cookie); if (!name) name = "SoundConsumer"; // Set up the hook functions. m_recordHook = recordFunc; m_notifyHook = notifyFunc; m_cookie = cookie; // Create the port that we publish as our Control Port. char pname[32]; sprintf(pname, "%.20s Control", name); m_port = create_port(10, pname); // Initialize our single media_input. Make sure it knows // the Control Port associated with the destination, and // the index of the destination (since we only have one, // that's trivial). m_input.destination.port = m_port; m_input.destination.id = 1; sprintf(m_input.name, "%.20s Input", name); // Set up the timing variables that we'll be using. m_trTimeout = 0LL; m_tpSeekAt = 0; m_tmSeekTo = 0; m_delta = 0; m_seeking = false; // Create, and run, the thread that we use to service // the Control Port. sprintf(pname, "%.20s Service", name); m_thread = spawn_thread(ThreadEntry, pname, 110, this); resume_thread(m_thread);}SoundConsumer::~SoundConsumer(){ NODE(stderr, "SoundConsumer::~SoundConsumer()\n"); // Signal to our thread that it's time to go home. write_port(m_port, MSG_QUIT_NOW, 0, 0); status_t s; while (wait_for_thread(m_thread, &s) == B_INTERRUPTED) NODE(stderr, "wait_for_thread() B_INTERRUPTED\n"); delete_port(m_port);}status_tSoundConsumer::SetHooks( SoundProcessFunc recordFunc, SoundNotifyFunc notifyFunc, void * cookie){ // SetHooks needs to be synchronized with the service thread, else we may // call the wrong hook function with the wrong cookie, which would be bad. // Rather than do locking, which is expensive, we streamline the process // by sending our service thread a request to change the hooks, and waiting // for the acknowledge. status_t err = B_OK; set_hooks_q cmd; cmd.process = recordFunc; cmd.notify = notifyFunc; cmd.cookie = cookie; // If we're not in the service thread, we need to round-trip a message. if (find_thread(0) != m_thread) { cmd.reply = create_port(1, "SetHooks reply"); // Send the private message to our service thread. err = write_port(ControlPort(), MSG_CHANGE_HOOKS, &cmd, sizeof(cmd)); if (err >= 0) { int32 code; // Wait for acknowledge from the service thread. err = read_port_etc(cmd.reply, &code, 0, 0, B_TIMEOUT, 6000000LL); if (err > 0) err = 0; NODE(stderr, "SoundConsumer::SetHooks read reply: %#010lx\n", err); } // Clean up. delete_port(cmd.reply); } else { // Within the service thread, it's OK to just go ahead and do the change. DoHookChange(&cmd); } return err;}//////////////////////////////////////////////////////////////////////////////////// BMediaNode-derived methods//////////////////////////////////////////////////////////////////////////////////port_id SoundConsumer::ControlPort() const{ return m_port;}BMediaAddOn* SoundConsumer::AddOn( int32 * internal_id) const{ // This object is instantiated inside an application. // Therefore, it has no add-on. if (internal_id) *internal_id = 0; return 0;}void SoundConsumer::Start( bigtime_t performance_time){ // Since we are a consumer and just blindly accept buffers that are // thrown at us, we don't need to do anything special in Start()/Stop(). // If we were (also) a producer, we'd have to be more elaborate. // The only thing we do is immediately perform any queued Seek based on // the start time, which is the right thing to do (seeing as we were // Seek()-ed when we weren't started). if (m_seeking) { m_delta = performance_time - m_tmSeekTo; m_seeking = false; } if (m_notifyHook) { (*m_notifyHook)(m_cookie, B_WILL_START, performance_time); } else { Notify(B_WILL_START, performance_time); }}void SoundConsumer::Stop( bigtime_t performance_time, bool immediate){ // Since we are a consumer and just blindly accept buffers that are // thrown at us, we don't need to do anything special in Start()/Stop(). // If we were (also) a producer, we'd have to be more elaborate. // Note that this is not strictly in conformance with The Rules, // but since this is not an add-on Node for use with any application; // it's a Node over which we have complete control, we can live with // treating buffers received before the start time or after the stop // time as any other buffer. if (m_notifyHook) { (*m_notifyHook)(m_cookie, B_WILL_STOP, performance_time, immediate); } else { Notify(B_WILL_STOP, performance_time, immediate); }}void SoundConsumer::Seek( bigtime_t media_time, bigtime_t performance_time){ // Seek() on a consumer just serves to offset the time stamp // of received buffers passed to our Record hook function. // In the hook function, you may wish to save those time stamps // to disk or otherwise store them. You may also want to // synchronize this node's media time with an upstream // producer's media time to make this offset meaningful. if (m_notifyHook) { (*m_notifyHook)(m_cookie, B_WILL_SEEK, performance_time, media_time); } else { Notify(B_WILL_SEEK, performance_time, media_time); } m_tpSeekAt = performance_time; m_tmSeekTo = media_time; m_seeking = true;}void SoundConsumer::SetRunMode( run_mode mode){ if (mode == BMediaNode::B_OFFLINE) { // BMediaNode::B_OFFLINE means we don't need to run in // real time. So, we shouldn't run as a real time // thread. int32 new_prio = suggest_thread_priority(B_OFFLINE_PROCESSING); set_thread_priority(m_thread, new_prio); } else { // We're running in real time, so we'd better have // a big enough thread priority to handle it! // Here's where those magic scheduler values // come from: // // * In the worst case, we process one buffer per // reschedule (we get rescheduled when we go to // look for a message on our Control Port), so // in order to keep up with the incoming buffers, // the duration of one buffer becomes our // scheduling period. If we don't know anything // about the buffers, we pick a reasonable // default. // * We're a simple consumer, so we don't have to // be too picky about the jitter. Half a period // of jitter means that we'd have to get two // consecutive worst-case reschedules before // we'd fall behind. // * The amount of time we spend processing is // our ProcessingLatency(). bigtime_t period = 10000; if (buffer_duration(m_input.format.u.raw_audio) > 0) { period = buffer_duration(m_input.format.u.raw_audio); } // assuming we're running for 500 us or less per buffer int32 new_prio = suggest_thread_priority(B_AUDIO_RECORDING, period, period/2, ProcessingLatency()); set_thread_priority(m_thread, new_prio); }}void SoundConsumer::TimeWarp( bigtime_t at_real_time, bigtime_t to_performance_time){ // Since buffers will come pre-time-stamped, we only need to look // at them, so we can ignore the time warp as a consumer. if (m_notifyHook) { (*m_notifyHook)(m_cookie, B_WILL_TIMEWARP, at_real_time, to_performance_time); } else { Notify(B_WILL_TIMEWARP, at_real_time, to_performance_time); }}void SoundConsumer::Preroll(){ // There is nothing for us to do in Preroll()}void SoundConsumer::SetTimeSource( BTimeSource * /* time_source */){ // We don't need to do anything special to take note of the // fact that the time source changed, because we get our timing // information from the buffers we receive.}status_t SoundConsumer::HandleMessage( int32 message, const void * data, size_t size){ // Check with each of our superclasses to see if they // understand the message. If none of them do, call // BMediaNode::HandleBadMessage(). if ((BBufferConsumer::HandleMessage(message, data, size) < 0) && (BMediaNode::HandleMessage(message, data, size) < 0)) { HandleBadMessage(message, data, size); return B_ERROR; } return B_OK;}//////////////////////////////////////////////////////////////////////////////////// BBufferConsumer-derived methods//////////////////////////////////////////////////////////////////////////////////status_t SoundConsumer::AcceptFormat( const media_destination & dest, media_format * format){ // We only accept formats aimed at our single input. if (dest != m_input.destination) { return B_MEDIA_BAD_DESTINATION; } // If no format is specified, we say we want raw audio. if (format->type <= 0) { format->type = B_MEDIA_RAW_AUDIO; format->u.raw_audio = media_raw_audio_format::wildcard; } // If a non-raw-audio format is specified, we tell the world what // we want, and that the specified format was unacceptable to us. else if (format->type != B_MEDIA_RAW_AUDIO) { format->type = B_MEDIA_RAW_AUDIO; format->u.raw_audio = media_raw_audio_format::wildcard; return B_MEDIA_BAD_FORMAT;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -