Ticket #8911: 0001-Replace-SoundCounsumer-with-modified-BMediaRecorder.patch

File 0001-Replace-SoundCounsumer-with-modified-BMediaRecorder.patch, 59.9 KB (added by Barrett, 5 years ago)
  • new file headers/private/media/MediaRecorder.h

    From 337b20d3672e656bc612334466e07dc0ee287883 Mon Sep 17 00:00:00 2001
    From: Barrett <b.vitruvio@gmail.com>
    Date: Sun, 4 May 2014 21:44:00 +0200
    Subject: [PATCH] Replace SoundCounsumer with modified BMediaRecorder.
    
    ---
     headers/private/media/MediaRecorder.h     | 109 +++++
     headers/private/media/MediaRecorderNode.h | 119 +++++
     headers/private/media/SoundConsumer.h     | 151 -------
     src/apps/soundrecorder/RecorderWindow.cpp | 180 ++++----
     src/apps/soundrecorder/RecorderWindow.h   |  14 +-
     src/kits/media/Jamfile                    |   3 +-
     src/kits/media/MediaRecorder.cpp          | 473 ++++++++++++++++++++
     src/kits/media/MediaRecorderNode.cpp      | 324 ++++++++++++++
     src/kits/media/SoundConsumer.cpp          | 700 ------------------------------
     9 files changed, 1106 insertions(+), 967 deletions(-)
     create mode 100644 headers/private/media/MediaRecorder.h
     create mode 100644 headers/private/media/MediaRecorderNode.h
     delete mode 100644 headers/private/media/SoundConsumer.h
     create mode 100644 src/kits/media/MediaRecorder.cpp
     create mode 100644 src/kits/media/MediaRecorderNode.cpp
     delete mode 100644 src/kits/media/SoundConsumer.cpp
    
    diff --git a/headers/private/media/MediaRecorder.h b/headers/private/media/MediaRecorder.h
    new file mode 100644
    index 0000000..8b6d54e
    - +  
     1/*
     2 * Copyright 2014, Haiku, Inc. All rights reserved.
     3 * Distributed under the terms of the MIT License.
     4 */
     5#ifndef _MEDIA_RECORDER_H
     6#define _MEDIA_RECORDER_H
     7
     8
     9#include <MediaNode.h>
     10#include <TimeSource.h>
     11
     12#include "MediaRecorderNode.h"
     13#include "SoundUtils.h"
     14
     15
     16namespace BPrivate { namespace media {
     17
     18class BMediaRecorder {
     19public:
     20                                BMediaRecorder(const char* name,
     21                                    media_type type
     22                                        = B_MEDIA_UNKNOWN_TYPE);
     23
     24    virtual                     ~BMediaRecorder();
     25
     26            status_t            InitCheck() const;
     27
     28    typedef void                (*ProcessFunc)(void* cookie,
     29                                    bigtime_t timestamp, void* data,
     30                                    size_t datasize,
     31                                    const media_format& format);
     32
     33    typedef void                (*NotifyFunc)(void* cookie,
     34                                    int32 code, ...);
     35
     36            status_t            SetHooks(ProcessFunc recordFunc = NULL,
     37                                    NotifyFunc notifyFunc = NULL,
     38                                    void* cookie = NULL);
     39
     40            void                SetAcceptedFormat(
     41                                    const media_format& format);
     42
     43    virtual status_t            Start(bool force = false);
     44    virtual status_t            Stop(bool force = false);
     45
     46    virtual status_t            Connect(const media_format& format,
     47                                    uint32 flags = 0);
     48
     49    virtual status_t            Connect(const dormant_node_info& dormantInfo,
     50                                    const media_format* format = NULL,
     51                                    uint32 flags = 0);
     52
     53    virtual status_t            Connect(const media_node& node,
     54                                    const media_output* useOutput = NULL,
     55                                    const media_format* format = NULL,
     56                                    uint32 flags = 0);
     57
     58    virtual status_t            Disconnect();
     59
     60            bool                IsConnected() const;
     61            bool                IsRunning() const;
     62
     63            const media_format& Format() const;
     64
     65            const media_output& MediaOutput() const;
     66            const media_input&  MediaInput() const;
     67
     68protected:
     69
     70    virtual void                BufferReceived(void* buffer,
     71                                    size_t size,
     72                                    const media_header& header);
     73private:
     74
     75        status_t                _Connect(
     76                                    const media_format* format,
     77                                    uint32 flags,
     78                                    const dormant_node_info* dormantNode,
     79                                    const media_node* mediaNode,
     80                                    const media_output* output);
     81
     82        status_t                fInitErr;
     83
     84        bool                    fConnected;
     85        bool                    fRunning;
     86
     87        BTimeSource*            fTimeSource;
     88
     89        ProcessFunc             fRecordHook;
     90        NotifyFunc              fNotifyHook;
     91
     92        media_node              fOutputNode;
     93        media_output            fOutput;
     94
     95        BMediaRecorderNode*     fNode;
     96        media_input             fInput;
     97
     98        void*                   fBufferCookie;
     99
     100        friend class            BMediaRecorderNode;
     101};
     102
     103}
     104
     105}
     106
     107using namespace BPrivate::media;
     108
     109#endif  //  _MEDIA_RECORDER_H
  • new file headers/private/media/MediaRecorderNode.h

    diff --git a/headers/private/media/MediaRecorderNode.h b/headers/private/media/MediaRecorderNode.h
    new file mode 100644
    index 0000000..b1e421e
    - +  
     1/*
     2 * Copyright 1999, Be Incorporated
     3 * Copyright 2014, Dario Casalinuovo
     4 * All Rights Reserved.
     5 * This file may be used under the terms of the Be Sample Code License.
     6 */
     7#ifndef _MEDIA_RECORDER_NODE_H
     8#define _MEDIA_RECORDER_NODE_H
     9
     10
     11#include <BufferConsumer.h>
     12#include <MediaEventLooper.h>
     13#include <String.h>
     14
     15
     16namespace BPrivate { namespace media {
     17
     18class BMediaRecorder;
     19
     20class BMediaRecorderNode : public BMediaEventLooper,
     21    public BBufferConsumer {
     22public:
     23                            BMediaRecorderNode(const char* name,
     24                                BMediaRecorder* recorder,
     25                                media_type type
     26                                    = B_MEDIA_UNKNOWN_TYPE);
     27
     28            //  TODO these are not thread safe; we should fix that...
     29            void            SetAcceptedFormat(const media_format& format);
     30
     31            status_t        GetInput(media_input* outInput);
     32
     33            void            SetDataEnabled(bool enabled);
     34
     35protected:
     36
     37    virtual BMediaAddOn*    AddOn(int32* id) const;
     38
     39    virtual void            NodeRegistered();
     40
     41    virtual void            SetRunMode(run_mode mode);
     42
     43    virtual void            HandleEvent(const media_timed_event* event,
     44                                bigtime_t lateness,
     45                                bool realTimeEvent);
     46
     47    virtual void            Start(bigtime_t performanceTime);
     48
     49    virtual void            Stop(bigtime_t performanceTime,
     50                                bool immediate);
     51
     52    virtual void            Seek(bigtime_t mediaTime,
     53                                bigtime_t performanceTime);
     54
     55    virtual void            TimeWarp(bigtime_t realTime,
     56                                bigtime_t performanceTime);
     57
     58    virtual status_t        HandleMessage(int32 message,
     59                                const void* data,
     60                                size_t size);
     61
     62            // Someone, probably the producer, is asking you about
     63            // this format. Give your honest opinion, possibly
     64            // modifying *format. Do not ask upstream producer about
     65            //  the format, since he's synchronously waiting for your
     66            // reply.
     67
     68    virtual status_t        AcceptFormat(const media_destination& dest,
     69                                media_format* format);
     70
     71    virtual status_t        GetNextInput(int32* cookie,
     72                                media_input* outInput);
     73
     74    virtual void            DisposeInputCookie(int32 cookie);
     75
     76    virtual void            BufferReceived(BBuffer* buffer);
     77
     78    virtual void            ProducerDataStatus(
     79                                const media_destination& forWhom,
     80                                int32 status,
     81                                bigtime_t performanceTime);
     82
     83    virtual status_t        GetLatencyFor(
     84                                const media_destination& forWhom,
     85                                bigtime_t*  outLatency,
     86                                media_node_id*  outTimesource);
     87
     88    virtual status_t        Connected(
     89                                const media_source& producer,
     90                                const media_destination& where,
     91                                const media_format& withFormat,
     92                                media_input* outInput);
     93
     94    virtual void            Disconnected(
     95                                const media_source& producer,
     96                                const media_destination& where);
     97
     98    virtual status_t        FormatChanged(
     99                                const media_source& producer,
     100                                const media_destination& consumer,
     101                                int32 tag,
     102                                const media_format& format);
     103
     104protected:
     105
     106    virtual                 ~BMediaRecorderNode();
     107
     108            BMediaRecorder* fRecorder;
     109            media_format    fOKFormat;
     110            media_input     fInput;
     111            BString         fName;
     112};
     113
     114}
     115}
     116
     117using namespace BPrivate::media;
     118
     119#endif  //  _MEDIA_RECORDER_NODE_H
  • deleted file headers/private/media/SoundConsumer.h

    diff --git a/headers/private/media/SoundConsumer.h b/headers/private/media/SoundConsumer.h
    deleted file mode 100644
    index c51dcc3..0000000
    + -  
    1 /*******************************************************************************
    2 /
    3 /   File:           SoundConsumer.h
    4 /
    5 /   Description:    Record sound from some sound-producing Node.
    6 /
    7 /   Copyright 1998, Be Incorporated, All Rights Reserved
    8 /
    9 *******************************************************************************/
    10 #ifndef SOUND_CONSUMER_H
    11 #define SOUND_CONSUMER_H
    12 
    13 //  To use this Consumer:
    14 //  1. Create Record and Notify hooks, or subclass SoundConsumer
    15 //     if you'd rather use the inheritance hierarchy.
    16 //     * The Record function should do whatever you want to do
    17 //       when you receive a buffer.
    18 //     * The Notify function should handle whatever events
    19 //       you wish to handle (defined in SoundUtil.h).
    20 //  2: Create an instance of SoundConsumer, giving it the
    21 //     appropriate hook functions. Or, create an instance of an
    22 //     appropriate subclass if you've made one.
    23 //  3: Register your new Consumer with the MediaRoster.
    24 //  4: Connect your Consumer to some Producer.
    25 //  5: Start or Stop the Consumer if your hook functions
    26 //     implement behavior for these kinds of events.
    27 //     Seek the Consumer to set the offset of the timestamps that
    28 //     your Record function will see.
    29 //  6: When you're done, disconnect the Consumer, then delete it.
    30 
    31 #include <BufferConsumer.h>
    32 #include "SoundUtils.h"
    33 
    34 namespace BPrivate {
    35 
    36 
    37 class SoundConsumer : public BBufferConsumer {
    38 public:
    39                                 SoundConsumer(const char* name,
    40                                     SoundProcessFunc recordFunc = NULL,
    41                                     SoundNotifyFunc notifyFunc = NULL,
    42                                     void* cookie = NULL);
    43     virtual                     ~SoundConsumer();
    44 
    45     //This function is OK to call from any thread.
    46             status_t            SetHooks(SoundProcessFunc recordFunc = NULL,
    47                                     SoundNotifyFunc notifyFunc = NULL,
    48                                     void* cookie = NULL);
    49 
    50     //  The MediaNode interface
    51 public:
    52     virtual port_id             ControlPort() const;
    53     virtual BMediaAddOn*        AddOn(int32* internalID) const;
    54         // Who instantiated you -- or NULL for app class
    55 
    56 
    57 protected:
    58     virtual void                Start(bigtime_t performanceTime);
    59     virtual void                Stop(bigtime_t performanceTime, bool immediate);
    60     virtual void                Seek(bigtime_t mediaTime,
    61                                     bigtime_t performanceTime);
    62     virtual void                SetRunMode(run_mode mode);
    63     virtual void                TimeWarp(bigtime_t atRealTime,
    64                                     bigtime_t to_performanceTime);
    65     virtual void                Preroll();
    66     virtual void                SetTimeSource(BTimeSource* timeSource);
    67     virtual status_t            HandleMessage(int32 message, const void* data,
    68                                     size_t size);
    69 
    70     //  The BufferConsumer interface
    71     virtual status_t            AcceptFormat(const media_destination& dest,
    72                                     media_format* format);
    73     virtual status_t            GetNextInput(int32* cookie,
    74                                     media_input* _input);
    75     virtual void                DisposeInputCookie(int32 cookie);
    76     virtual void                BufferReceived(BBuffer* buffer);
    77     virtual void                ProducerDataStatus(
    78                                     const media_destination& forWhom,
    79                                     int32 status, bigtime_t atMediaTime);
    80     virtual status_t            GetLatencyFor(const media_destination& forWhom,
    81                                     bigtime_t* _latency,
    82                                     media_node_id* _timesource);
    83     virtual status_t            Connected(const media_source& producer,
    84                                     const media_destination& where,
    85                                     const media_format& format,
    86                                     media_input* _input);
    87     virtual void                Disconnected(const media_source& producer,
    88                                     const media_destination& where);
    89     virtual status_t            FormatChanged(const media_source& producer,
    90                                     const media_destination& consumer,
    91                                     int32 fromChangeCount,
    92                                     const media_format& format);
    93 
    94 protected:
    95     //  Functions called when no hooks are installed.
    96     //  OK to override instead of installing hooks.
    97     virtual void                Record(bigtime_t time, const void* data,
    98                                     size_t size,
    99                                     const media_raw_audio_format& format);
    100     virtual void                Notify(int32 cause, ...);
    101 
    102 private:
    103             SoundProcessFunc    m_recordHook;
    104             SoundNotifyFunc     m_notifyHook;
    105             void*               m_cookie;
    106             media_input         m_input;
    107             thread_id           m_thread;
    108             port_id             m_port;
    109 
    110     // The times we need to deal with
    111     // My notation for times: tr = real time,
    112     // tp = performance time, tm = media time.
    113             bigtime_t           m_trTimeout;
    114                 // how long to wait on the input port
    115             bigtime_t           m_tpSeekAt;
    116                 // when we Seek
    117             bigtime_t           m_tmSeekTo;
    118                 // target time for Seek
    119 
    120     // The transformation from media to peformance time.
    121     // d = p - m, so m + d = p.
    122     // Media time is generally governed by the Seek
    123     // function. In our node, we simply use media time as
    124     // the time that we report to the record hook function.
    125     // If we were a producer node, we might use media time
    126     // to track where we were in playing a certain piece
    127     // of media. But we aren't.
    128             bigtime_t           m_delta;
    129        
    130     // State variables
    131             bool                m_seeking;
    132                 // a Seek is pending
    133 
    134     //  Functions to calculate timing values. OK to override.
    135     //  ProcessingLatency is the time it takes to process a buffer;
    136     //  TotalLatency is returned to producer; Timeout is passed
    137     //  to call to read_port_etc() in service thread loop.
    138     virtual bigtime_t           Timeout();
    139     virtual bigtime_t           ProcessingLatency();
    140     virtual bigtime_t           TotalLatency();
    141     //  The actual thread doing the work
    142     static  status_t            ThreadEntry(void* cookie);
    143             void                ServiceThread();
    144             void                DoHookChange(void* message);
    145 };
    146 
    147 
    148 }
    149 
    150 
    151 #endif  // SOUND_CONSUMER_H
  • src/apps/soundrecorder/RecorderWindow.cpp

    diff --git a/src/apps/soundrecorder/RecorderWindow.cpp b/src/apps/soundrecorder/RecorderWindow.cpp
    index f2fb731..dad953a 100644
    a b  
    11/*
    22 * Copyright 2005, Jérôme Duval. All rights reserved.
     3 * Copyright 2014, Dario Casalinuovo. All rights reserved.
    34 * Distributed under the terms of the MIT License.
    45 *
    56 * Inspired by SoundCapture from Be newsletter (Media Kit Basics:
     
    4243#include <TimeSource.h>
    4344#include <NodeInfo.h>
    4445
     46#include "SoundUtils.h"
    4547#include "RecorderWindow.h"
    46 #include "SoundConsumer.h"
    4748#include "FileUtils.h"
    4849
    4950#if ! NDEBUG
    RecorderWindow::RecorderWindow()  
    123124    fSaveButton = NULL;
    124125    fLoopButton = NULL;
    125126    fInputField = NULL;
    126     fRecordNode = 0;
     127    fRecorder = NULL;
    127128    fRecording = false;
    128129    fTempCount = -1;
    129130    fButtonState = btnPaused;
    RecorderWindow::RecorderWindow()  
    146147
    147148RecorderWindow::~RecorderWindow()
    148149{
    149     //  The sound consumer and producer are Nodes; it has to be released and the Roster
    150     //  will reap it when it's done.
    151     if (fRecordNode)
    152         fRecordNode->Release();
     150    //  The MediaRecorder have to be deleted, the dtor
     151    //  disconnect it from the media_kit.
     152    delete fRecorder;
     153
    153154    delete fPlayer;
    154155
    155156    if (fPlayTrack && fPlayFile)
    156157        fPlayFile->ReleaseTrack(fPlayTrack);
     158
    157159    if (fPlayFile)
    158160        delete fPlayFile;
    159161    fPlayTrack = NULL;
    RecorderWindow::InitWindow()  
    232234        if (error < B_OK) //    there's no mixer?
    233235            goto bad_mojo;
    234236
    235         //  Create our internal Node which records sound, and register it.
    236         fRecordNode = new SoundConsumer("Sound Recorder");
    237         error = fRoster->RegisterNode(fRecordNode);
    238         if (error < B_OK)
     237        fRecorder = new BMediaRecorder("Sound Recorder",
     238            B_MEDIA_RAW_AUDIO);
     239
     240        if (fRecorder->InitCheck() < B_OK)
    239241            goto bad_mojo;
    240242
     243        // Set the node to accept only audio data
     244        media_format output_format;
     245        output_format.type = B_MEDIA_RAW_AUDIO;
     246        output_format.u.raw_audio = media_raw_audio_format::wildcard;
     247        fRecorder->SetAcceptedFormat(output_format);
     248
    241249        //  Create the window header with controls
    242250        BRect r(Bounds());
    243251        r.bottom = r.top + 175;
    244         BBox *background = new BBox(r, "_background", B_FOLLOW_LEFT_RIGHT
    245             | B_FOLLOW_TOP, B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE_JUMP, B_NO_BORDER);
     252        BBox *background = new BBox(r, "_background",
     253            B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP, B_WILL_DRAW
     254            | B_FRAME_EVENTS | B_NAVIGABLE_JUMP, B_NO_BORDER);
     255
    246256        AddChild(background);
    247257
    248258        r = background->Bounds();
    RecorderWindow::InitWindow()  
    451461        dormant_node_info dni[maxInputCount];
    452462
    453463        int32 real_count = maxInputCount;
    454         media_format output_format;
    455         output_format.type = B_MEDIA_RAW_AUDIO;
    456         output_format.u.raw_audio = media_raw_audio_format::wildcard;
     464
    457465        error = fRoster->GetDormantNodes(dni, &real_count, 0, &output_format,
    458466            0, B_BUFFER_PRODUCER | B_PHYSICAL_INPUT);
    459467        if (real_count > maxInputCount) {
    RecorderWindow::InitWindow()  
    514522bad_mojo:
    515523    if (error >= 0)
    516524        error = B_ERROR;
    517     if (fRecordNode)
    518         fRecordNode->Release();
     525    if (fRecorder)
     526        delete fRecorder;
    519527
    520528    delete fPlayer;
    521529    if (!fInputField)
    RecorderWindow::Record(BMessage * message)  
    696704        return;
    697705    }
    698706
    699     //  And get it going...
    700     bigtime_t then = fRecordNode->TimeSource()->Now() + 50000LL;
    701     fRoster->StartNode(fRecordNode->Node(), then);
    702     if (fAudioInputNode.kind & B_TIME_SOURCE) {
    703         fRoster->StartNode(fAudioInputNode,
    704             fRecordNode->TimeSource()->RealTimeFor(then, 0));
    705     } else
    706         fRoster->StartNode(fAudioInputNode, then);
     707    fRecorder->Start();
    707708}
    708709
    709710
    RecorderWindow::MakeRecordConnection(const media_node & input)  
    871872{
    872873    CONNECT((stderr, "RecorderWindow::MakeRecordConnection()\n"));
    873874
    874     //  Find an available output for the given input node.
    875     int32 count = 0;
    876     status_t err = fRoster->GetFreeOutputsFor(input, &fAudioOutput, 1, &count, B_MEDIA_RAW_AUDIO);
    877     if (err < B_OK) {
    878         CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
    879             " couldn't get free outputs from audio input node\n"));
    880         return err;
    881     }
    882     if (count < 1) {
    883         CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
    884             " no free outputs from audio input node\n"));
    885         return B_BUSY;
    886     }
     875    status_t err = B_OK;
     876    media_output audioOutput;
    887877
    888     //  Find an available input for our own Node. Note that we go through the
    889     //  MediaRoster; calling Media Kit methods directly on Nodes in our app is
    890     //  not OK (because synchronization happens in the service thread, not in
    891     //  the calling thread).
    892     // TODO: explain this
    893     err = fRoster->GetFreeInputsFor(fRecordNode->Node(), &fRecInput, 1, &count, B_MEDIA_RAW_AUDIO);
    894     if (err < B_OK) {
    895         CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
    896             " couldn't get free inputs for sound recorder\n"));
    897         return err;
    898     }
    899     if (count < 1) {
    900         CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
    901             " no free inputs for sound recorder\n"));
    902         return B_BUSY;
    903     }
     878    if (!fRecorder->IsConnected()) {
     879        //  Find an available output for the given input node.
     880        int32 count = 0;
     881        err = fRoster->GetFreeOutputsFor(input, &audioOutput, 1,
     882            &count, B_MEDIA_RAW_AUDIO);
    904883
    905     //  Find out what the time source of the input is.
    906     //  For most nodes, we just use the preferred time source (the DAC) for synchronization.
    907     //  However, nodes that record from an input need to synchronize to the audio input node
    908     //  instead for best results.
    909     //  MakeTimeSourceFor gives us a "clone" of the time source node that we can manipulate
    910     //  to our heart's content. When we're done with it, though, we need to call Release()
    911     //  on the time source node, so that it keeps an accurate reference count and can delete
    912     //  itself when it's no longer needed.
    913     // TODO: what about filters connected to audio input?
    914     media_node use_time_source;
    915     BTimeSource * tsobj = fRoster->MakeTimeSourceFor(input);
    916     if (! tsobj) {
    917         CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
    918             " couldn't clone time source from audio input node\n"));
    919         return B_MEDIA_BAD_NODE;
    920     }
     884        if (err < B_OK) {
     885            CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
     886                " couldn't get free outputs from audio input node\n"));
     887            return err;
     888        }
    921889
    922     //  Apply the time source in effect to our own Node.
    923     err = fRoster->SetTimeSourceFor(fRecordNode->Node().node, tsobj->Node().node);
    924     if (err < B_OK) {
     890        if (count < 1) {
     891            CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
     892                " no free outputs from audio input node\n"));
     893            return B_BUSY;
     894        }
     895
     896    } else {
    925897        CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
    926             " couldn't set the sound recorder's time source\n"));
    927         tsobj->Release();
    928         return err;
     898            " audio input node already connected\n"));
     899
     900        return B_BUSY;
    929901    }
    930902
    931903    //  Get a format, any format.
    932     fRecordFormat.u.raw_audio = fAudioOutput.format.u.raw_audio;
     904    fRecordFormat.u.raw_audio = audioOutput.format.u.raw_audio;
    933905    fRecordFormat.type = B_MEDIA_RAW_AUDIO;
    934906
    935907    //  Tell the consumer where we want data to go.
    936     err = fRecordNode->SetHooks(RecordFile, NotifyRecordFile, this);
     908    err = fRecorder->SetHooks(RecordFile, NotifyRecordFile, this);
     909
    937910    if (err < B_OK) {
    938911        CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
    939912            " couldn't set the sound recorder's hook functions\n"));
    940         tsobj->Release();
    941913        return err;
    942914    }
    943915
    944     //  Using the same structs for input and output is OK in
    945     //  BMediaRoster::Connect().
    946     err = fRoster->Connect(fAudioOutput.source, fRecInput.destination,
    947         &fRecordFormat, &fAudioOutput, &fRecInput);
    948     if (err < B_OK) {
    949         CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
    950             " failed to connect sound recorder to audio input node.\n"));
    951         tsobj->Release();
    952         fRecordNode->SetHooks(0, 0, 0);
    953         return err;
    954     }
     916    if (!fRecorder->IsConnected()) {
     917
     918        err = fRecorder->Connect(input, &audioOutput, &fRecordFormat);
     919
     920        if (err < B_OK) {
     921            CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
     922                " failed to connect sound recorder to audio input node.\n"));
    955923
    956     //  Start the time source if it's not running.
    957     if ((tsobj->Node() != input) && !tsobj->IsRunning())
    958         fRoster->StartNode(tsobj->Node(), BTimeSource::RealTime());
     924            fRecorder->SetHooks(NULL, NULL, NULL);
     925            return err;
     926        }
     927    }
    959928
    960     tsobj->Release();   //  we're done with this time source instance!
    961929    return B_OK;
    962930}
    963931
    RecorderWindow::MakeRecordConnection(const media_node & input)  
    965933status_t
    966934RecorderWindow::BreakRecordConnection()
    967935{
    968     status_t err;
     936    status_t err = B_OK;
     937
     938    err = fRecorder->Stop(true);
     939    if (err < B_OK)
     940        return err;
    969941
    970     //  If we are the last connection, the Node will stop automatically since it
    971     //  has nowhere to send data to.
    972     err = fRoster->StopNode(fRecInput.node, 0);
    973     err = fRoster->Disconnect(fAudioOutput.node.node, fAudioOutput.source,
    974         fRecInput.node.node, fRecInput.destination);
    975     fAudioOutput.source = media_source::null;
    976     fRecInput.destination = media_destination::null;
    977     return err;
     942    return fRecorder->Disconnect();
    978943}
    979944
    980945
    RecorderWindow::StopRecording()  
    984949    if (!fRecording)
    985950        return B_OK;
    986951    fRecording = false;
     952
    987953    BreakRecordConnection();
    988     fRecordNode->SetHooks(NULL,NULL,NULL);
     954
     955    fRecorder->SetHooks(NULL, NULL, NULL);
     956
    989957    if (fRecSize > 0) {
    990958
    991959        wave_struct header;
    RecorderWindow::RemoveCurrentSoundItem() {  
    12401208
    12411209void
    12421210RecorderWindow::RecordFile(void* cookie, bigtime_t timestamp,
    1243     void* data, size_t size, const media_raw_audio_format &format)
     1211    void* data, size_t size, const media_format &format)
    12441212{
    12451213    //  Callback called from the SoundConsumer when receiving buffers.
    12461214    RecorderWindow * window = (RecorderWindow *)cookie;
    RecorderWindow::RecordFile(void* cookie, bigtime_t timestamp,  
    12491217        //  Write the data to file (we don't buffer or guard file access
    12501218        //  or anything)
    12511219        window->fRecFile.WriteAt(window->fRecSize, data, size);
    1252         window->fVUView->ComputeLevels(data, size, format.format);
     1220        window->fVUView->ComputeLevels(data, size, format.u.raw_audio.format);
    12531221        window->fRecSize += size;
    12541222    }
    12551223}
  • src/apps/soundrecorder/RecorderWindow.h

    diff --git a/src/apps/soundrecorder/RecorderWindow.h b/src/apps/soundrecorder/RecorderWindow.h
    index 1688334..d3e3087 100644
    a b  
    2121#include <Window.h>
    2222
    2323#include "DrawButton.h"
     24#include "MediaRecorder.h"
    2425#include "ScopeView.h"
    2526#include "SoundListView.h"
    2627#include "TransportButton.h"
    class BScrollView;  
    4041class BSlider;
    4142class BStringView;
    4243
    43 namespace BPrivate {
    44 class SoundConsumer;
    45 }
    4644
    47 
    48 using BPrivate::SoundConsumer;
     45using BPrivate::media::BMediaRecorder;
    4946
    5047
    5148class RecorderWindow : public BWindow {
    public:  
    7875        };
    7976
    8077        void AddSoundItem(const BEntry& entry, bool temp = false);
     78
    8179        void RemoveCurrentSoundItem();
    8280
    8381private:
    private:  
    9593        TrackSlider *fTrackSlider;
    9694        UpDownButton * fUpDownButton;
    9795        BMenuField * fInputField;
    98         SoundConsumer * fRecordNode;
     96        BMediaRecorder * fRecorder;
    9997        BSoundPlayer * fPlayer;
    10098        bool fRecording;
    10199        SoundListView * fSoundList;
    private:  
    128126        off_t fRecSize;
    129127
    130128        media_node fAudioInputNode;
    131         media_output fAudioOutput;
    132         media_input fRecInput;
    133129
    134130        BMediaFile *fPlayFile;
    135131        media_format fPlayFormat;
    private:  
    171167        status_t UpdatePlayFile(SoundListItem *item, bool updateDisplay = false);
    172168        void ErrorAlert(const char * action, status_t err);
    173169
    174 static  void RecordFile(void * cookie, bigtime_t timestamp, void * data, size_t size, const media_raw_audio_format & format);
     170static  void RecordFile(void * cookie, bigtime_t timestamp, void * data, size_t size, const media_format & format);
    175171static  void NotifyRecordFile(void * cookie, int32 code, ...);
    176172
    177173static  void PlayFile(void * cookie, void * data, size_t size, const media_raw_audio_format & format);
  • src/kits/media/Jamfile

    diff --git a/src/kits/media/Jamfile b/src/kits/media/Jamfile
    index 907794c..9c17f9e 100644
    a b for architectureObject in [ MultiArchSubDirSetup ] {  
    1919        SharedLibrary [ MultiArchDefaultGristFiles libmedia.so ] :
    2020            # Private Media Kit
    2121            !missing_symbols.cpp
    22             SoundConsumer.cpp
     22            MediaRecorder.cpp
     23            MediaRecorderNode.cpp
    2324
    2425            # Public Media Kit
    2526            Buffer.cpp
  • new file src/kits/media/MediaRecorder.cpp

    diff --git a/src/kits/media/MediaRecorder.cpp b/src/kits/media/MediaRecorder.cpp
    new file mode 100644
    index 0000000..9c92909
    - +  
     1/*
     2 * Copyright 1999, Be Incorporated
     3 * Copyright 2014, Dario Casalinuovo
     4 * All Rights Reserved.
     5 * This file may be used under the terms of the Be Sample Code License.
     6 */
     7
     8
     9#include "MediaRecorder.h"
     10
     11#include <MediaAddOn.h>
     12#include <MediaRoster.h>
     13#include <TimeSource.h>
     14
     15#include "MediaDebug.h"
     16#include "MediaRecorderNode.h"
     17
     18
     19class MediaNodeReleaser {
     20public:
     21                MediaNodeReleaser(media_node& mediaNode,
     22                    bool release = true)
     23                    :
     24                    fNode(mediaNode),
     25                    fRelease(release)
     26                {
     27                }
     28
     29                ~MediaNodeReleaser()
     30                {
     31                    if (fRelease)
     32                        BMediaRoster::Roster()->ReleaseNode(fNode);
     33                }
     34
     35    void        SetTo(media_node& node)
     36                {
     37                    fNode = node;
     38                }
     39
     40    void        SetRelease(bool release)
     41                {
     42                    fRelease = release;
     43                }
     44
     45private:
     46    media_node& fNode;
     47    bool        fRelease;
     48};
     49
     50
     51BMediaRecorder::BMediaRecorder(const char* name, media_type type)
     52    :
     53    fInitErr(B_OK),
     54    fConnected(false),
     55    fRunning(false),
     56    fTimeSource(NULL),
     57    fRecordHook(NULL),
     58    fNotifyHook(NULL),
     59    fNode(NULL),
     60    fBufferCookie(NULL)
     61{
     62    CALLED();
     63
     64    BMediaRoster::Roster(&fInitErr);
     65
     66    if (fInitErr == B_OK) {
     67        fNode = new BMediaRecorderNode(name, this, type);
     68        fInitErr = BMediaRoster::CurrentRoster()->RegisterNode(fNode);
     69    }
     70}
     71
     72
     73BMediaRecorder::~BMediaRecorder()
     74{
     75    CALLED();
     76
     77    if (fNode != NULL) {
     78        Stop();
     79        Disconnect();
     80        fNode->Release();
     81    }
     82
     83    if (fTimeSource != NULL)
     84        fTimeSource->Release();
     85
     86}
     87
     88
     89status_t
     90BMediaRecorder::InitCheck() const
     91{
     92    CALLED();
     93
     94    return fInitErr;
     95}
     96
     97
     98void
     99BMediaRecorder::SetAcceptedFormat(const media_format& format)
     100{
     101    CALLED();
     102
     103    fNode->SetAcceptedFormat(format);
     104}
     105
     106
     107status_t
     108BMediaRecorder::SetHooks(ProcessFunc recordFunc, NotifyFunc notifyFunc,
     109    void* cookie)
     110{
     111    CALLED();
     112
     113    fRecordHook = recordFunc;
     114    fNotifyHook = notifyFunc;
     115    fBufferCookie = cookie;
     116
     117    return B_OK;
     118}
     119
     120
     121void
     122BMediaRecorder::BufferReceived(void* buffer, size_t size,
     123    const media_header& header)
     124{
     125    CALLED();
     126
     127    if (fRecordHook) {
     128        (*fRecordHook)(fBufferCookie, header.start_time,
     129            buffer, size, Format());
     130    }
     131}
     132
     133
     134status_t
     135BMediaRecorder::Connect(const media_format& format, uint32 flags)
     136{
     137    CALLED();
     138
     139    if (fInitErr != B_OK)
     140        return fInitErr;
     141
     142    if (fConnected)
     143        return B_MEDIA_ALREADY_CONNECTED;
     144
     145    return _Connect(&format, flags, NULL, NULL, NULL);
     146}
     147
     148
     149status_t
     150BMediaRecorder::Connect(const dormant_node_info& dormantInfo,
     151    const media_format* format, uint32 flags)
     152{
     153    CALLED();
     154
     155    if (fInitErr != B_OK)
     156        return fInitErr;
     157
     158    if (fConnected)
     159        return B_MEDIA_ALREADY_CONNECTED;
     160
     161    return _Connect(format, flags, &dormantInfo, NULL, NULL);
     162}
     163
     164
     165status_t
     166BMediaRecorder::Connect(const media_node& node,
     167    const media_output* useOutput, const media_format* format,
     168    uint32 flags)
     169{
     170    CALLED();
     171
     172    if (fInitErr != B_OK)
     173        return fInitErr;
     174
     175    if (fConnected)
     176        return B_MEDIA_ALREADY_CONNECTED;
     177
     178    return _Connect(format, flags, NULL, &node, useOutput);
     179}
     180
     181
     182status_t
     183BMediaRecorder::Disconnect()
     184{
     185    CALLED();
     186
     187    status_t err = B_OK;
     188
     189    if (fInitErr != B_OK)
     190        return fInitErr;
     191
     192    if (!fConnected)
     193        return B_MEDIA_NOT_CONNECTED;
     194
     195    if (!fNode)
     196        return B_ERROR;
     197
     198    if (fRunning)
     199        err = Stop();
     200
     201    if (err != B_OK)
     202        return err;
     203
     204    // do the disconnect
     205    err = BMediaRoster::CurrentRoster()->Disconnect(
     206        fOutputNode.node, fOutput.source,
     207            fNode->Node().node, fInput.destination);
     208
     209    BMediaRoster::Roster()->ReleaseNode(fOutputNode);
     210
     211    if (fTimeSource != NULL) {
     212        fTimeSource->Release();
     213        fTimeSource = NULL;
     214    }
     215
     216    fConnected = false;
     217    fRunning = false;
     218
     219    return err;
     220}
     221
     222
     223status_t
     224BMediaRecorder::Start(bool force)
     225{
     226    CALLED();
     227
     228    if (fInitErr != B_OK)
     229        return fInitErr;
     230
     231    if (!fConnected)
     232        return B_MEDIA_NOT_CONNECTED;
     233
     234    if (fRunning && !force)
     235        return EALREADY;
     236
     237    if (!fNode)
     238        return B_ERROR;
     239
     240    // start node here
     241    status_t err = B_OK;
     242
     243    if (fNode->Node().kind & B_TIME_SOURCE)
     244        err = BMediaRoster::CurrentRoster()->StartTimeSource(
     245            fOutputNode, BTimeSource::RealTime());
     246    else
     247        err = BMediaRoster::CurrentRoster()->StartNode(
     248            fOutputNode, fTimeSource->Now());
     249
     250    // then un-mute it
     251    if (err == B_OK) {
     252        fNode->SetDataEnabled(true);
     253        fRunning = true;
     254    } else
     255        fRunning = false;
     256
     257    return err;
     258}
     259
     260
     261status_t
     262BMediaRecorder::Stop(bool force)
     263{
     264    CALLED();
     265
     266    if (fInitErr != B_OK)
     267        return fInitErr;
     268
     269    if (!fRunning && !force)
     270        return EALREADY;
     271
     272    if (!fNode)
     273        return B_ERROR;
     274
     275    // should have the Node mute the output here
     276    fNode->SetDataEnabled(false);
     277
     278    fRunning = false;
     279
     280    return BMediaRoster::CurrentRoster()->StopNode(fNode->Node(), 0);
     281}
     282
     283
     284bool
     285BMediaRecorder::IsRunning() const
     286{
     287    CALLED();
     288
     289    return fRunning;
     290}
     291
     292
     293bool
     294BMediaRecorder::IsConnected() const
     295{
     296    CALLED();
     297
     298    return fConnected;
     299}
     300
     301
     302const media_output&
     303BMediaRecorder::MediaOutput() const
     304{
     305    CALLED();
     306
     307    return fOutput;
     308}
     309
     310
     311const media_input&
     312BMediaRecorder::MediaInput() const
     313{
     314    CALLED();
     315
     316    return fInput;
     317}
     318
     319
     320const media_format&
     321BMediaRecorder::Format() const
     322{
     323    CALLED();
     324
     325    return fOutput.format;
     326}
     327
     328
     329status_t
     330BMediaRecorder::_Connect(const media_format* format,
     331    uint32 flags, const dormant_node_info* dormantNode,
     332    const media_node* mediaNode, const media_output* output)
     333{
     334    CALLED();
     335
     336    status_t err = B_OK;
     337    media_format ourFormat;
     338    media_node node;
     339    MediaNodeReleaser away(node, false);
     340    media_output ourOutput;
     341
     342    // argument checking and set-up
     343    if (format != NULL)
     344        ourFormat = *format;
     345
     346    if (fNode == NULL)
     347        return B_ERROR;
     348
     349    if (mediaNode == NULL && output != NULL)
     350        return B_MISMATCHED_VALUES;
     351
     352    if (dormantNode != NULL && (mediaNode != NULL || output != NULL))
     353        return B_MISMATCHED_VALUES;
     354
     355    if (format == NULL && output != NULL)
     356        ourFormat = output->format;
     357
     358    fNode->SetAcceptedFormat(ourFormat);
     359
     360    // figure out the node to instantiate
     361    if (dormantNode != NULL) {
     362
     363        err = BMediaRoster::Roster()->InstantiateDormantNode(
     364            *dormantNode, &node, B_FLAVOR_IS_GLOBAL);
     365
     366        away.SetRelease(true);
     367
     368    } else if (mediaNode != NULL) {
     369        node = *mediaNode;
     370    } else {
     371        switch (ourFormat.type) {
     372
     373            // switch on format for default
     374            case B_MEDIA_RAW_AUDIO:
     375                err = BMediaRoster::Roster()->GetAudioInput(&node);
     376                away.SetRelease(true);
     377                break;
     378
     379            case B_MEDIA_RAW_VIDEO:
     380            case B_MEDIA_ENCODED_VIDEO:
     381                err = BMediaRoster::Roster()->GetVideoInput(&node);
     382                away.SetRelease(true);
     383                break;
     384
     385            // give up?
     386            default:
     387                return B_MEDIA_BAD_FORMAT;
     388                break;
     389        }
     390    }
     391
     392    fOutputNode = node;
     393
     394    // figure out the output provided
     395
     396    if (output != NULL) {
     397        ourOutput = *output;
     398    } else if (err == B_OK) {
     399
     400        media_output outputs[10];
     401        int32 count = 10;
     402
     403        err = BMediaRoster::Roster()->GetFreeOutputsFor(node,
     404            outputs, count, &count, ourFormat.type);
     405
     406        if (err != B_OK)
     407            return err;
     408
     409        err = B_MEDIA_BAD_SOURCE;
     410
     411        for (int i = 0; i < count; i++) {
     412            if (format_is_compatible(outputs[i].format, ourFormat)) {
     413                ourOutput = outputs[i];
     414                err = B_OK;
     415                ourFormat = outputs[i].format;
     416                break;
     417            }
     418
     419        }
     420
     421    } else {
     422        return err;
     423    }
     424
     425    if (ourOutput.source == media_source::null)
     426        return B_MEDIA_BAD_SOURCE;
     427
     428    // find our Node's free input
     429    media_input ourInput;
     430
     431    err = fNode->GetInput(&ourInput);
     432    if (err != B_OK)
     433        return err;
     434
     435    media_node time_source;
     436    if (node.kind & B_TIME_SOURCE)
     437        time_source = node;
     438    else
     439        BMediaRoster::Roster()->GetSystemTimeSource(&time_source);
     440
     441    // set time source
     442    BMediaRoster::Roster()->SetTimeSourceFor(
     443        fNode->Node().node, time_source.node);
     444
     445    fTimeSource = BMediaRoster::CurrentRoster()->MakeTimeSourceFor(
     446        fNode->Node());
     447
     448    if (err != B_OK)
     449        return err;
     450
     451    // start the recorder node (it's always running)
     452    err = BMediaRoster::CurrentRoster()->StartNode(
     453        fOutputNode, fTimeSource->Now());
     454
     455    if (err != B_OK)
     456        return err;
     457
     458    // perform the connection
     459    fOutput = ourOutput;
     460    fInput = ourInput;
     461
     462    err = BMediaRoster::CurrentRoster()->Connect(fOutput.source,
     463        fInput.destination, &ourFormat, &fOutput, &fInput,
     464        BMediaRoster::B_CONNECT_MUTED);
     465
     466    if (err != B_OK)
     467        return err;
     468
     469    fConnected = true;
     470    away.SetRelease(false);
     471
     472    return err;
     473}
  • new file src/kits/media/MediaRecorderNode.cpp

    diff --git a/src/kits/media/MediaRecorderNode.cpp b/src/kits/media/MediaRecorderNode.cpp
    new file mode 100644
    index 0000000..b8c3f97
    - +  
     1/*
     2 * Copyright 1999, Be Incorporated
     3 * Copyright 2014, Dario Casalinuovo
     4 * All Rights Reserved.
     5 * This file may be used under the terms of the Be Sample Code License.
     6 */
     7
     8
     9#include "MediaRecorderNode.h"
     10
     11#include <Buffer.h>
     12#include <scheduler.h>
     13#include <TimedEventQueue.h>
     14#include <TimeSource.h>
     15
     16#include "MediaDebug.h"
     17#include "MediaRecorder.h"
     18
     19
     20BMediaRecorderNode::BMediaRecorderNode(const char* name,
     21    BMediaRecorder* recorder, media_type type)
     22    :
     23    BMediaNode(name),
     24    BMediaEventLooper(),
     25    BBufferConsumer(type),
     26    fRecorder(recorder)
     27{
     28    CALLED();
     29
     30    fInput.destination.id = 1;
     31    fInput.destination.port = ControlPort();
     32
     33    fName.SetTo(name);
     34
     35    BString str(name);
     36    str << " Input";
     37    strcpy(fInput.name, str.String());
     38}
     39
     40
     41BMediaRecorderNode::~BMediaRecorderNode()
     42{
     43    CALLED();
     44}
     45
     46
     47BMediaAddOn*
     48BMediaRecorderNode::AddOn(int32* id) const
     49{
     50    CALLED();
     51
     52    if (id)
     53        *id = -1;
     54
     55    return NULL;
     56}
     57
     58
     59void
     60BMediaRecorderNode::NodeRegistered()
     61{
     62    CALLED();
     63    Run();
     64}
     65
     66
     67void
     68BMediaRecorderNode::SetRunMode(run_mode mode)
     69{
     70    CALLED();
     71
     72    int32 priority;
     73
     74    if (mode == BMediaNode::B_OFFLINE)
     75        priority = B_OFFLINE_PROCESSING;
     76    else {
     77        switch(ConsumerType()) {
     78            case B_MEDIA_RAW_AUDIO:
     79            case B_MEDIA_ENCODED_AUDIO:
     80                priority = B_AUDIO_RECORDING;
     81                break;
     82
     83            case B_MEDIA_RAW_VIDEO:
     84            case B_MEDIA_ENCODED_VIDEO:
     85                priority = B_VIDEO_RECORDING;
     86                break;
     87
     88            default:
     89                priority = B_DEFAULT_MEDIA_PRIORITY;
     90        }
     91    }
     92
     93    SetPriority(suggest_thread_priority(priority));
     94
     95    BMediaNode::SetRunMode(mode);
     96}
     97
     98
     99void
     100BMediaRecorderNode::SetAcceptedFormat(const media_format& format)
     101{
     102    CALLED();
     103
     104    fOKFormat = format;
     105}
     106
     107
     108status_t
     109BMediaRecorderNode::GetInput(media_input* outInput)
     110{
     111    CALLED();
     112
     113    fInput.node = Node();
     114    *outInput = fInput;
     115
     116    return B_OK;
     117}
     118
     119
     120void
     121BMediaRecorderNode::SetDataEnabled(bool enabled)
     122{
     123    CALLED();
     124
     125    int32 tag;
     126
     127    SetOutputEnabled(fInput.source,
     128        fInput.destination, enabled, NULL, &tag);
     129}
     130
     131
     132void
     133BMediaRecorderNode::HandleEvent(const media_timed_event* event,
     134    bigtime_t lateness, bool realTimeEvent)
     135{
     136    CALLED();
     137
     138    // we ignore them all!
     139}
     140
     141
     142void
     143BMediaRecorderNode::Start(bigtime_t performanceTime)
     144{
     145    CALLED();
     146
     147    if (fRecorder->fNotifyHook)
     148        (*fRecorder->fNotifyHook)(fRecorder->fBufferCookie,
     149            B_WILL_START, performanceTime);
     150
     151    fRecorder->fRunning = true;
     152}
     153
     154
     155void
     156BMediaRecorderNode::Stop(bigtime_t performanceTime, bool immediate)
     157{
     158    CALLED();
     159
     160    if (fRecorder->fNotifyHook)
     161        (*fRecorder->fNotifyHook)(fRecorder->fBufferCookie,
     162            B_WILL_STOP, performanceTime, immediate);
     163
     164    fRecorder->fRunning = false;
     165}
     166
     167
     168void
     169BMediaRecorderNode::Seek(bigtime_t mediaTime, bigtime_t performanceTime)
     170{
     171    CALLED();
     172
     173    if (fRecorder->fNotifyHook)
     174        (*fRecorder->fNotifyHook)(fRecorder->fBufferCookie,
     175            B_WILL_SEEK, performanceTime, mediaTime);
     176}
     177
     178
     179void
     180BMediaRecorderNode::TimeWarp(bigtime_t realTime, bigtime_t performanceTime)
     181{
     182    CALLED();
     183
     184    // Since buffers will come pre-time-stamped, we only need to look
     185    // at them, so we can ignore the time warp as a consumer.
     186    if (fRecorder->fNotifyHook)
     187        (*fRecorder->fNotifyHook)(fRecorder->fBufferCookie,
     188            B_WILL_TIMEWARP, realTime, performanceTime);
     189}
     190
     191
     192status_t
     193BMediaRecorderNode::HandleMessage(int32 message,
     194    const void* data, size_t size)
     195{
     196    CALLED();
     197
     198    if (BBufferConsumer::HandleMessage(message, data, size) < 0
     199        && BMediaEventLooper::HandleMessage(message, data, size) < 0
     200        && BMediaNode::HandleMessage(message, data, size) < 0) {
     201        HandleBadMessage(message, data, size);
     202        return B_ERROR;
     203    }
     204    return B_OK;
     205}
     206
     207
     208status_t
     209BMediaRecorderNode::AcceptFormat(const media_destination& dest,
     210    media_format* format)
     211{
     212    CALLED();
     213
     214    if (format_is_compatible(*format, fOKFormat))
     215        return B_OK;
     216
     217    *format = fOKFormat;
     218
     219    return B_MEDIA_BAD_FORMAT;
     220}
     221
     222
     223status_t
     224BMediaRecorderNode::GetNextInput(int32* cookie, media_input* outInput)
     225{
     226    CALLED();
     227
     228    if (*cookie == 0) {
     229        *cookie = -1;
     230        *outInput = fInput;
     231        return B_OK;
     232    }
     233
     234    return B_BAD_INDEX;
     235}
     236
     237
     238void
     239BMediaRecorderNode::DisposeInputCookie(int32 cookie)
     240{
     241    CALLED();
     242}
     243
     244
     245void
     246BMediaRecorderNode::BufferReceived(BBuffer* buffer)
     247{
     248    CALLED();
     249
     250    fRecorder->BufferReceived(buffer->Data(), buffer->SizeUsed(),
     251        *buffer->Header());
     252
     253    buffer->Recycle();
     254}
     255
     256
     257void
     258BMediaRecorderNode::ProducerDataStatus(
     259    const media_destination& forWhom, int32 status,
     260    bigtime_t performanceTime)
     261{
     262    CALLED();
     263}
     264
     265
     266status_t
     267BMediaRecorderNode::GetLatencyFor(const media_destination& forWhom,
     268    bigtime_t* outLatency, media_node_id* outTimesource)
     269{
     270    CALLED();
     271
     272    *outLatency = 0;
     273    *outTimesource = TimeSource()->ID();
     274
     275    return B_OK;
     276}
     277
     278
     279status_t
     280BMediaRecorderNode::Connected(const media_source &producer,
     281    const media_destination &where, const media_format &withFormat,
     282    media_input* outInput)
     283{
     284    CALLED();
     285
     286    fInput.source = producer;
     287    fInput.format = withFormat;
     288    *outInput = fInput;
     289
     290    fRecorder->fConnected = true;
     291    fRecorder->fInput = fInput;
     292
     293    return B_OK;
     294}
     295
     296
     297void
     298BMediaRecorderNode::Disconnected(const media_source& producer,
     299    const media_destination& where)
     300{
     301    CALLED();
     302
     303    fInput.source = media_source::null;
     304
     305    fRecorder->fConnected = false;
     306
     307    fRecorder->fInput.format = fOKFormat;
     308}
     309
     310
     311status_t
     312BMediaRecorderNode::FormatChanged(const media_source& producer,
     313    const media_destination& consumer, int32 tag,
     314    const media_format& format)
     315{
     316    CALLED();
     317
     318    if (!format_is_compatible(format, fOKFormat))
     319        return B_MEDIA_BAD_FORMAT;
     320
     321    fInput.format = format;
     322
     323    return B_OK;
     324}
  • deleted file src/kits/media/SoundConsumer.cpp

    diff --git a/src/kits/media/SoundConsumer.cpp b/src/kits/media/SoundConsumer.cpp
    deleted file mode 100644
    index e2ca10f..0000000
    + -  
    1 /******************************************************************************
    2 /
    3 /   File:           SoundConsumer.cpp
    4 /
    5 /   Description:    Record sound from some sound-producing Node.
    6 /
    7 /   Copyright 1998-1999, Be Incorporated, All Rights Reserved
    8 /
    9 ******************************************************************************/
    10 #include "SoundConsumer.h"
    11 
    12 #include <new>
    13 #include <stdio.h>
    14 
    15 #include <OS.h>
    16 #include <scheduler.h>
    17 #include <Buffer.h>
    18 #include <TimeSource.h>
    19 
    20 #include "AutoDeleter.h"
    21 
    22 
    23 using std::nothrow;
    24 
    25 
    26 namespace BPrivate {
    27 
    28 
    29 //  If we don't mind the format changing to another format while
    30 //  running, we can define this to 1. Look for the symbol down in the source.
    31 #define ACCEPT_ANY_FORMAT_CHANGE 0
    32 
    33 
    34 //  Compiling with NDEBUG means "release" -- it also turns off assert() and
    35 //  other such debugging aids. By contrast, DEBUG turns on extra debugging
    36 //  in some programs.
    37 #if !NDEBUG
    38 #define TRACE_SOUNDCONSUMER
    39 #endif
    40 
    41 //  Comment out the FPRINTF part of these lines to reduce verbiage.
    42 //  Enabling MESSAGE will kill performance on slower machines, because it
    43 //  prints for each message received (including each buffer).
    44 #ifdef TRACE_SOUNDCONSUMER
    45 #define NODE fprintf
    46 #define MESSAGE fprintf
    47 #else
    48 #define NODE(x...)
    49 #define MESSAGE(x...)
    50 #endif
    51 
    52 
    53 //  This structure is the body of a request that we use to
    54 //  implement SetHooks().
    55 struct set_hooks_q {
    56     port_id reply;
    57     void * cookie;
    58     SoundProcessFunc process;
    59     SoundNotifyFunc notify;
    60 };
    61 
    62 
    63 //  All incoming buffers and Media Kit requests arrive at a
    64 //  media node in the form of messages (which are generally
    65 //  dispatched for you by your superclasses' HandleMessage
    66 //  implementations). Each message has a 'type' which is
    67 //  analagous to a BMessage's 'what' field. We'll define our
    68 //  own private message types for our SoundConsumer and
    69 //  SoundProducer to use. The BeOS reserves a range,
    70 //  0x60000000 to 0x7fffffff, for us to use.
    71 enum {
    72     MSG_QUIT_NOW = 0x60000000L,
    73     MSG_CHANGE_HOOKS
    74 };
    75 
    76 
    77 SoundConsumer::SoundConsumer(
    78     const char * name,
    79     SoundProcessFunc recordFunc,
    80     SoundNotifyFunc notifyFunc,
    81     void * cookie) :
    82     BMediaNode(name ? name : "SoundConsumer"),
    83     BBufferConsumer(B_MEDIA_RAW_AUDIO)
    84 {
    85     NODE(stderr, "SoundConsumer::SoundConsumer(%p, %p, %p, %p)\n", name,
    86         recordFunc, notifyFunc, cookie);
    87 
    88     if (!name) name = "SoundConsumer";
    89 
    90     //  Set up the hook functions.
    91     m_recordHook = recordFunc;
    92     m_notifyHook = notifyFunc;
    93     m_cookie = cookie;
    94 
    95     //  Create the port that we publish as our Control Port.
    96     char pname[32];
    97     sprintf(pname, "%.20s Control", name);
    98     m_port = create_port(10, pname);
    99 
    100     // Initialize our single media_input. Make sure it knows
    101     // the Control Port associated with the destination, and
    102     // the index of the destination (since we only have one,
    103     // that's trivial).
    104     m_input.destination.port = m_port;
    105     m_input.destination.id = 1;
    106     sprintf(m_input.name, "%.20s Input", name);
    107 
    108     // Set up the timing variables that we'll be using.
    109     m_trTimeout = 0LL;
    110     m_tpSeekAt = 0;
    111     m_tmSeekTo = 0;
    112     m_delta = 0;
    113     m_seeking = false;
    114 
    115     //  Create, and run, the thread that we use to service
    116     // the Control Port.
    117     sprintf(pname, "%.20s Service", name);
    118     m_thread = spawn_thread(ThreadEntry, pname, 110, this);
    119     resume_thread(m_thread);
    120 }
    121 
    122 
    123 SoundConsumer::~SoundConsumer()
    124 {
    125     NODE(stderr, "SoundConsumer::~SoundConsumer()\n");
    126     //  Signal to our thread that it's time to go home.
    127     write_port(m_port, MSG_QUIT_NOW, 0, 0);
    128     status_t s;
    129     while (wait_for_thread(m_thread, &s) == B_INTERRUPTED)
    130         NODE(stderr, "wait_for_thread() B_INTERRUPTED\n");
    131     delete_port(m_port);
    132 }
    133 
    134 
    135 status_t
    136 SoundConsumer::SetHooks(SoundProcessFunc recordFunc, SoundNotifyFunc notifyFunc,
    137     void* cookie)
    138 {
    139     //  SetHooks needs to be synchronized with the service thread, else we may
    140     //  call the wrong hook function with the wrong cookie, which would be bad.
    141     //  Rather than do locking, which is expensive, we streamline the process
    142     //  by sending our service thread a request to change the hooks, and waiting
    143     //  for the acknowledge.
    144     status_t err = B_OK;
    145     set_hooks_q cmd;
    146     cmd.process = recordFunc;
    147     cmd.notify = notifyFunc;
    148     cmd.cookie = cookie;
    149     //  If we're not in the service thread, we need to round-trip a message.
    150     if (find_thread(0) != m_thread) {
    151         cmd.reply = create_port(1, "SetHooks reply");
    152         //  Send the private message to our service thread.
    153         err = write_port(ControlPort(), MSG_CHANGE_HOOKS, &cmd, sizeof(cmd));
    154         if (err >= 0) {
    155             int32 code;
    156             //  Wait for acknowledge from the service thread.
    157             err = read_port_etc(cmd.reply, &code, 0, 0, B_TIMEOUT, 6000000LL);
    158             if (err > 0) err = 0;
    159             NODE(stderr, "SoundConsumer::SetHooks read reply: %#" B_PRIx32
    160                 "\n", err);
    161         }
    162         //  Clean up.
    163         delete_port(cmd.reply);
    164     } else {
    165         //  Within the service thread, it's OK to just go ahead and do the
    166         //  change.
    167         DoHookChange(&cmd);
    168     }
    169     return err;
    170 }
    171 
    172 
    173 // #pragma mark -BMediaNode-derived methods
    174 
    175 
    176 port_id
    177 SoundConsumer::ControlPort() const
    178 {
    179     return m_port;
    180 }
    181 
    182 
    183 BMediaAddOn*
    184 SoundConsumer::AddOn(int32 * internal_id) const
    185 {
    186     //  This object is instantiated inside an application.
    187     //  Therefore, it has no add-on.
    188     if (internal_id) *internal_id = 0;
    189     return 0;
    190 }
    191 
    192 
    193 void
    194 SoundConsumer::Start(bigtime_t performance_time)
    195 {
    196     //  Since we are a consumer and just blindly accept buffers that are
    197     //  thrown at us, we don't need to do anything special in Start()/Stop().
    198     //  If we were (also) a producer, we'd have to be more elaborate.
    199     //  The only thing we do is immediately perform any queued Seek based on
    200     //  the start time, which is the right thing to do (seeing as we were
    201     //  Seek()-ed when we weren't started).
    202     if (m_seeking) {
    203         m_delta = performance_time - m_tmSeekTo;
    204         m_seeking = false;
    205     }
    206     if (m_notifyHook)
    207         (*m_notifyHook)(m_cookie, B_WILL_START, performance_time);
    208     else
    209         Notify(B_WILL_START, performance_time);
    210 }
    211 
    212 
    213 void
    214 SoundConsumer::Stop(bigtime_t performance_time, bool immediate)
    215 {
    216     //  Since we are a consumer and just blindly accept buffers that are
    217     //  thrown at us, we don't need to do anything special in Start()/Stop().
    218     //  If we were (also) a producer, we'd have to be more elaborate.
    219     //  Note that this is not strictly in conformance with The Rules,
    220     //  but since this is not an add-on Node for use with any application;
    221     //  it's a Node over which we have complete control, we can live with
    222     //  treating buffers received before the start time or after the stop
    223     //  time as any other buffer.
    224     if (m_notifyHook)
    225         (*m_notifyHook)(m_cookie, B_WILL_STOP, performance_time, immediate);
    226     else
    227         Notify(B_WILL_STOP, performance_time, immediate);
    228 }
    229 
    230 
    231 void
    232 SoundConsumer::Seek(bigtime_t media_time, bigtime_t performance_time)
    233 {
    234     //  Seek() on a consumer just serves to offset the time stamp
    235     //  of received buffers passed to our Record hook function.
    236     //  In the hook function, you may wish to save those time stamps
    237     //  to disk or otherwise store them. You may also want to
    238     //  synchronize this node's media time with an upstream
    239     //  producer's media time to make this offset meaningful.
    240     if (m_notifyHook)
    241         (*m_notifyHook)(m_cookie, B_WILL_SEEK, performance_time, media_time);
    242     else
    243         Notify(B_WILL_SEEK, performance_time, media_time);
    244 
    245     m_tpSeekAt = performance_time;
    246     m_tmSeekTo = media_time;
    247     m_seeking = true;
    248 }
    249 
    250 
    251 void
    252 SoundConsumer::SetRunMode(run_mode mode)
    253 {
    254     if (mode == BMediaNode::B_OFFLINE) {
    255         // BMediaNode::B_OFFLINE means we don't need to run in
    256         // real time. So, we shouldn't run as a real time
    257         // thread.
    258         int32 new_prio = suggest_thread_priority(B_OFFLINE_PROCESSING);
    259         set_thread_priority(m_thread, new_prio);
    260     } else {
    261         //  We're running in real time, so we'd better have
    262         //  a big enough thread priority to handle it!
    263         //  Here's where those magic scheduler values
    264         //  come from:
    265         //
    266         //  * In the worst case, we process one buffer per
    267         //    reschedule (we get rescheduled when we go to
    268         //    look for a message on our Control Port), so
    269         //    in order to keep up with the incoming buffers,
    270         //    the duration of one buffer becomes our
    271         //    scheduling period. If we don't know anything
    272         //    about the buffers, we pick a reasonable
    273         //    default.
    274         //  * We're a simple consumer, so we don't have to
    275         //    be too picky about the jitter. Half a period
    276         //    of jitter means that we'd have to get two
    277         //    consecutive worst-case reschedules before
    278         //    we'd fall behind.
    279         //  * The amount of time we spend processing is
    280         //    our ProcessingLatency().
    281         bigtime_t period = 10000;
    282         if (buffer_duration(m_input.format.u.raw_audio) > 0)
    283             period = buffer_duration(m_input.format.u.raw_audio);
    284 
    285         //  assuming we're running for 500 us or less per buffer
    286         int32 new_prio = suggest_thread_priority(B_AUDIO_RECORDING,
    287             period, period / 2, ProcessingLatency());
    288         set_thread_priority(m_thread, new_prio);
    289     }
    290 }
    291 
    292 
    293 void
    294 SoundConsumer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time)
    295 {
    296     //  Since buffers will come pre-time-stamped, we only need to look
    297     //  at them, so we can ignore the time warp as a consumer.
    298     if (m_notifyHook) {
    299         (*m_notifyHook)(m_cookie, B_WILL_TIMEWARP, at_real_time,
    300             to_performance_time);
    301     } else
    302         Notify(B_WILL_TIMEWARP, at_real_time, to_performance_time);
    303 }
    304 
    305 
    306 void
    307 SoundConsumer::Preroll()
    308 {
    309     //  There is nothing for us to do in Preroll()
    310 }
    311 
    312 
    313 void
    314 SoundConsumer::SetTimeSource(BTimeSource* /* time_source */)
    315 {
    316     //  We don't need to do anything special to take note of the
    317     //  fact that the time source changed, because we get our timing
    318     //  information from the buffers we receive.
    319 }
    320 
    321 
    322 status_t
    323 SoundConsumer::HandleMessage(int32 message, const void* data, size_t size)
    324 {
    325     // Check with each of our superclasses to see if they
    326     // understand the message. If none of them do, call
    327     // BMediaNode::HandleBadMessage().
    328     if (BBufferConsumer::HandleMessage(message, data, size) < 0
    329         && BMediaNode::HandleMessage(message, data, size) < 0) {
    330         HandleBadMessage(message, data, size);
    331         return B_ERROR;
    332     }
    333     return B_OK;
    334 }
    335 
    336 
    337 // #pragma mark - BBufferConsumer-derived methods
    338 
    339 
    340 status_t
    341 SoundConsumer::AcceptFormat(const media_destination& dest, media_format* format)
    342 {
    343     //  We only accept formats aimed at our single input.
    344     if (dest != m_input.destination)
    345         return B_MEDIA_BAD_DESTINATION;
    346 
    347     if (format->type <= 0) {
    348         //  If no format is specified, we say we want raw audio.
    349         format->type = B_MEDIA_RAW_AUDIO;
    350         format->u.raw_audio = media_raw_audio_format::wildcard;
    351     } else if (format->type != B_MEDIA_RAW_AUDIO) {
    352         //  If a non-raw-audio format is specified, we tell the world what
    353         //  we want, and that the specified format was unacceptable to us.
    354         format->type = B_MEDIA_RAW_AUDIO;
    355         format->u.raw_audio = media_raw_audio_format::wildcard;
    356         return B_MEDIA_BAD_FORMAT;
    357     }
    358 #if !ACCEPT_ANY_FORMAT_CHANGE
    359     //  If we're already connected, and this format doesn't go with the
    360     //  format in effect, we dont' accept this new format.
    361     if (!format_is_compatible(*format, m_input.format)) {
    362         *format = m_input.format;
    363         return B_MEDIA_BAD_FORMAT;
    364     }
    365 #endif
    366     //  I guess we're OK by now, because we have no particular needs as
    367     //  far as frame rate, sample format, etc go.
    368     return B_OK;
    369 }
    370 
    371 
    372 status_t
    373 SoundConsumer::GetNextInput(int32* cookie, media_input* out_input)
    374 {
    375     NODE(stderr, "SoundConsumer: GetNextInput()\n");
    376     //  The "next" is kind of misleading, since it's also used for
    377     //  getting the first (and only) input.
    378     if (*cookie == 0) {
    379         if (m_input.source == media_source::null) {
    380             //  If there's no current connection, make sure we return a
    381             //  reasonable format telling the world we accept any raw audio.
    382             m_input.format.type = B_MEDIA_RAW_AUDIO;
    383             m_input.format.u.raw_audio = media_raw_audio_format::wildcard;
    384             m_input.node = Node();
    385             m_input.destination.port = ControlPort();
    386             m_input.destination.id = 1;
    387         }
    388         *out_input = m_input;
    389         *cookie = 1;
    390         return B_OK;
    391     }
    392     //  There's only one input.
    393     return B_BAD_INDEX;
    394 }
    395 
    396 
    397 void
    398 SoundConsumer::DisposeInputCookie(int32 /* cookie */)
    399 {
    400     //  We didn't allocate any memory or set any state in GetNextInput()
    401     //  so this function is a no-op.
    402 }
    403 
    404 
    405 void
    406 SoundConsumer::BufferReceived(BBuffer* buffer)
    407 {
    408     NODE(stderr, "SoundConsumer::BufferReceived()\n");
    409     //  Whee, a buffer! Update the seek info, if necessary.
    410     if (m_seeking && buffer->Header()->start_time >= m_tpSeekAt) {
    411         m_delta = m_tpSeekAt - m_tmSeekTo;
    412         m_seeking = false;
    413     }
    414     //  If there is a record hook, let the interested party have at it!
    415     if (m_recordHook) {
    416         (*m_recordHook)(m_cookie, buffer->Header()->start_time-m_delta,
    417             buffer->Data(), buffer->Header()->size_used,
    418             m_input.format.u.raw_audio);
    419     } else {
    420         Record(buffer->Header()->start_time-m_delta, buffer->Data(),
    421             buffer->Header()->size_used, m_input.format.u.raw_audio);
    422     }
    423     //  Buffers should ALWAYS be recycled, else whomever is producing them
    424     //  will starve.
    425     buffer->Recycle();
    426 }
    427 
    428 
    429 void
    430 SoundConsumer::ProducerDataStatus(const media_destination& for_whom,
    431     int32 status, bigtime_t at_media_time)
    432 {
    433     if (for_whom != m_input.destination)
    434         return;
    435 
    436     //  Tell whomever is interested that the upstream producer will or won't
    437     //  send more data in the immediate future.
    438     if (m_notifyHook) {
    439         (*m_notifyHook)(m_cookie, B_PRODUCER_DATA_STATUS, status,
    440             at_media_time);
    441     } else
    442         Notify(B_PRODUCER_DATA_STATUS, status, at_media_time);
    443 }
    444 
    445 
    446 status_t
    447 SoundConsumer::GetLatencyFor(const media_destination& for_whom,
    448     bigtime_t* out_latency, media_node_id* out_timesource)
    449 {
    450     //  We only accept requests for the one-and-only input of our Node.
    451     if (for_whom != m_input.destination)
    452         return B_MEDIA_BAD_DESTINATION;
    453 
    454     //  Tell the world about our latency information (overridable by user).
    455     *out_latency = TotalLatency();
    456     *out_timesource = TimeSource()->Node().node;
    457     return B_OK;
    458 }
    459 
    460 
    461 status_t
    462 SoundConsumer::Connected(const media_source& producer,
    463     const media_destination& where, const media_format& with_format,
    464     media_input* out_input)
    465 {
    466     NODE(stderr, "SoundConsumer::Connected()\n");
    467     //  Only accept connection requests when we're not already connected.
    468     if (m_input.source != media_source::null)
    469         return B_MEDIA_BAD_DESTINATION;
    470 
    471     //  Only accept connection requests on the one-and-only available input.
    472     if (where != m_input.destination)
    473         return B_MEDIA_BAD_DESTINATION;
    474 
    475     //  Other than that, we accept pretty much anything. The format has been
    476     //  pre-cleared through AcceptFormat(), and we accept any format anyway.
    477     m_input.source = producer;
    478     m_input.format = with_format;
    479     //  Tell whomever is interested that there's now a connection.
    480     if (m_notifyHook)
    481         (*m_notifyHook)(m_cookie, B_CONNECTED, m_input.name);
    482     else
    483         Notify(B_CONNECTED, m_input.name);
    484 
    485     //  This is the most important line -- return our connection information
    486     //  to the world so it can use it!
    487     *out_input = m_input;
    488     return B_OK;
    489 }
    490 
    491 
    492 void
    493 SoundConsumer::Disconnected(const media_source& producer,
    494     const media_destination& where)
    495 {
    496     //  We can't disconnect something which isn't us.
    497     if (where != m_input.destination)
    498         return;
    499     //  We can't disconnect from someone who isn't connected to us.
    500     if (producer != m_input.source)
    501         return;
    502     //  Tell the interested party that it's time to leave.
    503     if (m_notifyHook)
    504         (*m_notifyHook)(m_cookie, B_DISCONNECTED);
    505     else
    506         Notify(B_DISCONNECTED);
    507     //  Mark ourselves as not-connected.
    508     m_input.source = media_source::null;
    509 }
    510 
    511 
    512 status_t
    513 SoundConsumer::FormatChanged(const media_source& producer,
    514     const media_destination& consumer, int32 from_change_count,
    515     const media_format& format)
    516 {
    517     NODE(stderr, "SoundConsumer::Connected()\n");
    518     //  The up-stream guy feels like changing the format. If we can accept
    519     //  arbitrary format changes, we just say "OK". If, however, we're recording
    520     //  to a file, that's not such a good idea; we only accept format changes
    521     //  that are compatible with the format we're already using. You set this
    522     //  behaviour at compile time by defining ACCEPT_ANY_FORMAT_CHANGE to 1 or
    523     //  0.
    524     status_t err = B_OK;
    525 #if ACCEPT_ANY_FORMAT_CHANGE
    526     media_format fmt(format);
    527     err = AcceptFormat(m_input.destination, &fmt);
    528 #else
    529     if (m_input.source != media_source::null) {
    530         err = format_is_compatible(format, m_input.format) ? B_OK
    531             : B_MEDIA_BAD_FORMAT;
    532     }
    533 #endif
    534     if (err >= B_OK) {
    535         m_input.format = format;
    536         if (m_notifyHook) {
    537             (*m_notifyHook)(m_cookie, B_FORMAT_CHANGED,
    538                 &m_input.format.u.raw_audio);
    539         } else
    540             Notify(B_FORMAT_CHANGED, &m_input.format.u.raw_audio);
    541     }
    542     return err;
    543 }
    544 
    545 
    546 void
    547 SoundConsumer::DoHookChange(void* msg)
    548 {
    549     //  Tell the old guy we're changing the hooks ...
    550     if (m_notifyHook)
    551         (*m_notifyHook)(m_cookie, B_HOOKS_CHANGED);
    552     else
    553         Notify(B_HOOKS_CHANGED);
    554 
    555     //  ... and then do it.
    556     set_hooks_q * ptr = (set_hooks_q *)msg;
    557     m_recordHook = ptr->process;
    558     m_notifyHook = ptr->notify;
    559     m_cookie = ptr->cookie;
    560 }
    561 
    562 
    563 status_t
    564 SoundConsumer::ThreadEntry(void* cookie)
    565 {
    566     SoundConsumer* consumer = (SoundConsumer*)cookie;
    567     consumer->ServiceThread();
    568     return 0;
    569 }
    570 
    571 
    572 void
    573 SoundConsumer::ServiceThread()
    574 {
    575     //  The Big Bad ServiceThread receives messages aimed at this
    576     //  Node and dispatches them (typically to HandleMessage()).
    577     //  If we were a Producer, we might have to do finicky timing and
    578     //  queued Start()/Stop() processing in here. But we ain't.
    579 
    580     //  A media kit message will never be bigger than B_MEDIA_MESSAGE_SIZE.
    581     //  Avoid wasing stack space by dynamically allocating at start.
    582     char* msg = new (nothrow) char[B_MEDIA_MESSAGE_SIZE];
    583     if (msg == NULL)
    584         return;
    585     //  Make sure we clean up this data when we exit the function.
    586     ArrayDeleter<char> _(msg);
    587     int bad = 0;
    588     while (true) {
    589         //  Call read_port_etc() with a timeout derived from a virtual function,
    590         //  to allow clients to do special processing if necessary.
    591         bigtime_t timeout = Timeout();
    592         int32 code = 0;
    593         status_t err = read_port_etc(m_port, &code, msg, B_MEDIA_MESSAGE_SIZE,
    594             B_TIMEOUT, timeout);
    595         MESSAGE(stderr, "SoundConsumer::ServiceThread() port %" B_PRId32
    596             " message %#" B_PRIx32 "\n", m_port, code);
    597         //  If we received a message, err will be the size of the message
    598         //  (including 0).
    599         if (err >= B_OK) {
    600             //  Real messages reset the timeout time.
    601             m_trTimeout = 0;
    602             bad = 0;
    603             if (code == MSG_QUIT_NOW) {
    604                 //  Check for our private stop message.
    605                 if (m_notifyHook)
    606                     (*m_notifyHook)(m_cookie, B_NODE_DIES, 0);
    607                 else
    608                     Notify(B_NODE_DIES, 0);
    609                 break;
    610             } else if (code == MSG_CHANGE_HOOKS) {
    611             //  Else check for our private change-hooks message.
    612                 DoHookChange(msg);
    613                 //  Write acknowledge to waiting thread.
    614                 write_port(((set_hooks_q *)msg)->reply, 0, 0, 0);
    615             } else {
    616                 //  Else it has to be a regular media kit message;
    617                 //  go ahead and dispatch it.
    618                 HandleMessage(code, msg, err);
    619             }
    620         } else if (err == B_TIMED_OUT) {
    621             //  Timing out means that there was no buffer. Tell the interested
    622             //  party.
    623             if (m_notifyHook)
    624                 (*m_notifyHook)(m_cookie, B_OP_TIMED_OUT, timeout);
    625             else
    626                 Notify(B_OP_TIMED_OUT, timeout);
    627         } else {
    628             //  Other errors are bad.
    629             MESSAGE(stderr, "SoundConsumer: error %#" B_PRIx32 "\n", err);
    630             bad++;
    631             //  If we receive three bad reads with no good messages inbetween,
    632             //  things are probably not going to improve (like the port
    633             //  disappeared or something) so we call it a day.
    634             if (bad > 3) {
    635                 if (m_notifyHook)
    636                     (*m_notifyHook)(m_cookie, B_NODE_DIES, bad, err, code, msg);
    637                 else
    638                     Notify(B_NODE_DIES, bad, err, code, msg);
    639                 break;
    640             }
    641         }
    642     }
    643 }
    644 
    645 
    646 bigtime_t
    647 SoundConsumer::Timeout()
    648 {
    649     //  Timeout() is called for each call to read_port_etc() in the service
    650     //  thread to figure out a reasonable time-out value. The default behaviour
    651     //  we've picked is to exponentially back off from one second and upwards.
    652     //  While it's true that 44 back-offs will run us out of precision in a
    653     //  bigtime_t, the time to actually reach 44 consecutive back-offs is longer
    654     //  than the expected market longevity of just about any piece of real
    655     //  estate. Is that the sound of an impending year-fifteen-million software
    656     //  problem? :-)
    657     m_trTimeout = (m_trTimeout < 1000000) ? 1000000 : m_trTimeout*2;
    658     return m_trTimeout;
    659 }
    660 
    661 
    662 bigtime_t
    663 SoundConsumer::ProcessingLatency()
    664 {
    665     //  We're saying it takes us 500 us to process each buffer. If all we do is
    666     //  copy the data, it probably takes much less than that, but it doesn't
    667     //  hurt to be slightly conservative.
    668     return 500LL;
    669 }
    670 
    671 
    672 bigtime_t
    673 SoundConsumer::TotalLatency()
    674 {
    675     //  Had we been a producer that passes buffers on, we'd have to
    676     //  include downstream latency in this value. But we are not.
    677     return ProcessingLatency();
    678 }
    679 
    680 
    681 // #pragma mark -
    682 
    683 
    684 void
    685 SoundConsumer::Record(bigtime_t /*time*/, const void* /*data*/,
    686     size_t /*size*/, const media_raw_audio_format& /*format*/)
    687 {
    688     //  If there is no record hook installed, we instead call this function
    689     //  for received buffers.
    690 }
    691 
    692 
    693 void
    694 SoundConsumer::Notify(int32 /*cause*/, ...)
    695 {
    696     //  If there is no notification hook installed, we instead call this
    697     //  function for giving notification of various events.
    698 }
    699 
    700 }