Ticket #8911: 0001-Replaced-SoundConsumer-with-MediaRecorder.patch

File 0001-Replaced-SoundConsumer-with-MediaRecorder.patch, 59.5 KB (added by Barrett, 10 years ago)

Patch to replace SoundConsumer with BMediaRecorder.

  • new file headers/private/media/MediaRecorder.h

    From 5e596cc99dcc2162d8aa3e4f99828cbd48068ded Mon Sep 17 00:00:00 2001
    From: Barrett17 <b.vitruvio@gmail.com>
    Date: Tue, 11 Feb 2014 01:25:39 +0000
    Subject: [PATCH] Replaced SoundConsumer with MediaRecorder.
    
    ---
     headers/private/media/MediaRecorder.h     | 118 +++++
     headers/private/media/MediaRecorderNode.h | 115 +++++
     headers/private/media/SoundConsumer.h     | 151 -------
     src/apps/soundrecorder/RecorderWindow.cpp | 179 ++++----
     src/apps/soundrecorder/RecorderWindow.h   |  14 +-
     src/kits/media/Jamfile                    |   3 +-
     src/kits/media/MediaRecorder.cpp          | 465 ++++++++++++++++++++
     src/kits/media/MediaRecorderNode.cpp      | 290 +++++++++++++
     src/kits/media/SoundConsumer.cpp          | 700 ------------------------------
     9 files changed, 1069 insertions(+), 966 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..dd35b2e
    - +  
     1/*
     2 * Copyright 2014, Haiku, Inc. All rights reserved.
     3 * Distributed under the terms of the MIT License.
     4 */
     5
     6#ifndef _MEDIA_RECORDER_H
     7#define _MEDIA_RECORDER_H
     8
     9#include <MediaNode.h>
     10#include <TimeSource.h>
     11
     12#include "MediaRecorderNode.h"
     13#include "SoundUtils.h"
     14
     15typedef void (*ProcessFunc)(void* cookie,
     16    bigtime_t timestamp, void* data, size_t datasize,
     17    const media_format& format);
     18
     19typedef void (*NotifyFunc)(void* cookie,
     20    int32 code, ...);
     21
     22
     23namespace BPrivate { namespace media {
     24
     25class BMediaRecorder {
     26public:
     27                                BMediaRecorder(const char* name,
     28                                    int32 threadPriority = 0,
     29                                    media_type type
     30                                        = B_MEDIA_UNKNOWN_TYPE);
     31
     32    virtual                     ~BMediaRecorder();
     33
     34            status_t            InitCheck() const;
     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);
     73
     74                                BMediaRecorder();
     75
     76                                BMediaRecorder(const BMediaRecorder&
     77                                    recorder);
     78
     79                                BMediaRecorder&  operator=(
     80                                    const BMediaRecorder& );
     81
     82private:
     83
     84        status_t                _Connect(
     85                                    const media_format* format,
     86                                    uint32 flags,
     87                                    const dormant_node_info* dormantNode,
     88                                    const media_node* mediaNode,
     89                                    const media_output* output);
     90
     91        status_t                fInitErr;
     92
     93        bool                    fConnected;
     94        bool                    fRunning;
     95
     96        BTimeSource*            fTimeSource;
     97
     98        ProcessFunc             fRecordHook;
     99        NotifyFunc              fNotifyHook;
     100
     101        media_node              fOutputNode;
     102        media_output            fOutput;
     103
     104        BMediaRecorderNode*     fNode;
     105        media_input             fInput;
     106
     107        void*                   fBufferCookie;
     108
     109        friend class            BMediaRecorderNode;
     110};
     111
     112}
     113
     114}
     115
     116using namespace BPrivate::media;
     117
     118#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..8bc4459
    - +  
     1//  MediaRecorderNode.h
     2//  -------------------
     3//  Copyright 1999, Be Incorporated.   All Rights Reserved.
     4//  Copyright 2014, Dario Casalinuovo. 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#include <BufferConsumer.h>
     11#include <MediaEventLooper.h>
     12#include <String.h>
     13
     14
     15namespace BPrivate { namespace media {
     16
     17class BMediaRecorder;
     18
     19class BMediaRecorderNode : public BMediaEventLooper,
     20    public BBufferConsumer {
     21public:
     22                            BMediaRecorderNode(const char* name,
     23                                BMediaRecorder* rec,
     24                                int32 priority,
     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            HandleEvent(const media_timed_event* event,
     40                                bigtime_t lateness,
     41                                bool realTimeEvent);
     42
     43    virtual void            Start(bigtime_t performanceTime);
     44
     45    virtual void            Stop(bigtime_t performanceTime,
     46                                bool immediate);
     47
     48    virtual void            Seek(bigtime_t mediaTime,
     49                                bigtime_t performanceTime);
     50
     51    virtual void            TimeWarp(bigtime_t realTime,
     52                                bigtime_t performanceTime);
     53
     54    virtual status_t        HandleMessage(int32 message,
     55                                const void* data,
     56                                size_t size);
     57       
     58            // Someone, probably the producer, is asking you about
     59            // this format. Give your honest opinion, possibly
     60            // modifying *format. Do not ask upstream producer about
     61            //  the format, since he's synchronously waiting for your
     62            // reply.
     63
     64    virtual status_t        AcceptFormat(const media_destination& dest,
     65                                media_format* format);
     66
     67    virtual status_t        GetNextInput(int32* cookie,
     68                                media_input* outInput);
     69
     70    virtual void            DisposeInputCookie(int32 cookie);
     71
     72    virtual void            BufferReceived(BBuffer* buffer);
     73
     74    virtual void            ProducerDataStatus(
     75                                const media_destination& forWhom,
     76                                int32 status,
     77                                bigtime_t performanceTime);
     78
     79    virtual status_t        GetLatencyFor(
     80                                const media_destination& forWhom,
     81                                bigtime_t*  outLatency,
     82                                media_node_id*  outTimesource);
     83
     84    virtual status_t        Connected(
     85                                const media_source& producer,
     86                                const media_destination& where,
     87                                const media_format& withFormat,
     88                                media_input* outInput);
     89
     90    virtual void            Disconnected(
     91                                const media_source& producer,
     92                                const media_destination& where);
     93
     94    virtual status_t        FormatChanged(
     95                                const media_source& producer,
     96                                const media_destination& consumer,
     97                                int32 tag,
     98                                const media_format& format);
     99
     100protected:
     101
     102    virtual                 ~BMediaRecorderNode();
     103
     104            BMediaRecorder* fRecorder;
     105            media_format    fOKFormat;
     106            media_input     fInput;
     107            BString         fName;
     108};
     109
     110}
     111}
     112
     113using namespace BPrivate::media;
     114
     115#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..7dc85bc 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 disconnected and deleted
     151    delete fRecorder;
     152
    153153    delete fPlayer;
    154154
    155155    if (fPlayTrack && fPlayFile)
    156156        fPlayFile->ReleaseTrack(fPlayTrack);
     157
    157158    if (fPlayFile)
    158159        delete fPlayFile;
    159160    fPlayTrack = NULL;
    RecorderWindow::InitWindow()  
    232233        if (error < B_OK) //    there's no mixer?
    233234            goto bad_mojo;
    234235
    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)
     236        fRecorder = new BMediaRecorder("Sound Recorder",
     237            B_REAL_TIME_PRIORITY, B_MEDIA_RAW_AUDIO);
     238
     239        if (fRecorder->InitCheck() < B_OK)
    239240            goto bad_mojo;
    240241
     242        // Set the node to accept only audio data
     243        media_format output_format;
     244        output_format.type = B_MEDIA_RAW_AUDIO;
     245        output_format.u.raw_audio = media_raw_audio_format::wildcard;
     246        fRecorder->SetAcceptedFormat(output_format);
     247
    241248        //  Create the window header with controls
    242249        BRect r(Bounds());
    243250        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);
     251        BBox *background = new BBox(r, "_background",
     252            B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP, B_WILL_DRAW
     253            | B_FRAME_EVENTS | B_NAVIGABLE_JUMP, B_NO_BORDER);
     254
    246255        AddChild(background);
    247256
    248257        r = background->Bounds();
    RecorderWindow::InitWindow()  
    451460        dormant_node_info dni[maxInputCount];
    452461
    453462        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;
     463
    457464        error = fRoster->GetDormantNodes(dni, &real_count, 0, &output_format,
    458465            0, B_BUFFER_PRODUCER | B_PHYSICAL_INPUT);
    459466        if (real_count > maxInputCount) {
    RecorderWindow::InitWindow()  
    514521bad_mojo:
    515522    if (error >= 0)
    516523        error = B_ERROR;
    517     if (fRecordNode)
    518         fRecordNode->Release();
     524    if (fRecorder)
     525        delete fRecorder;
    519526
    520527    delete fPlayer;
    521528    if (!fInputField)
    RecorderWindow::Record(BMessage * message)  
    696703        return;
    697704    }
    698705
    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);
     706    fRecorder->Start();
    707707}
    708708
    709709
    RecorderWindow::MakeRecordConnection(const media_node & input)  
    871871{
    872872    CONNECT((stderr, "RecorderWindow::MakeRecordConnection()\n"));
    873873
    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     }
     874    status_t err = B_OK;
     875    media_output audioOutput;
    887876
    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     }
     877    if (!fRecorder->IsConnected()) {
     878        //  Find an available output for the given input node.
     879        int32 count = 0;
     880        err = fRoster->GetFreeOutputsFor(input, &audioOutput, 1,
     881            &count, B_MEDIA_RAW_AUDIO);
    904882
    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     }
     883        if (err < B_OK) {
     884            CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
     885                " couldn't get free outputs from audio input node\n"));
     886            return err;
     887        }
    921888
    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) {
     889        if (count < 1) {
     890            CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
     891                " no free outputs from audio input node\n"));
     892            return B_BUSY;
     893        }
     894
     895    } else {
    925896        CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
    926             " couldn't set the sound recorder's time source\n"));
    927         tsobj->Release();
    928         return err;
     897            " audio input node already connected\n"));
     898
     899        return B_BUSY;
    929900    }
    930901
    931902    //  Get a format, any format.
    932     fRecordFormat.u.raw_audio = fAudioOutput.format.u.raw_audio;
     903    fRecordFormat.u.raw_audio = audioOutput.format.u.raw_audio;
    933904    fRecordFormat.type = B_MEDIA_RAW_AUDIO;
    934905
    935906    //  Tell the consumer where we want data to go.
    936     err = fRecordNode->SetHooks(RecordFile, NotifyRecordFile, this);
     907    err = fRecorder->SetHooks(RecordFile, NotifyRecordFile, this);
     908
    937909    if (err < B_OK) {
    938910        CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
    939911            " couldn't set the sound recorder's hook functions\n"));
    940         tsobj->Release();
    941912        return err;
    942913    }
    943914
    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     }
     915    if (!fRecorder->IsConnected()) {
     916
     917        err = fRecorder->Connect(input, &audioOutput, &fRecordFormat);
    955918
    956     //  Start the time source if it's not running.
    957     if ((tsobj->Node() != input) && !tsobj->IsRunning())
    958         fRoster->StartNode(tsobj->Node(), BTimeSource::RealTime());
     919        if (err < B_OK) {
     920            CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
     921                " failed to connect sound recorder to audio input node.\n"));
     922
     923            fRecorder->SetHooks(NULL, NULL, NULL);
     924            return err;
     925        }
     926    }
    959927
    960     tsobj->Release();   //  we're done with this time source instance!
    961928    return B_OK;
    962929}
    963930
    RecorderWindow::MakeRecordConnection(const media_node & input)  
    965932status_t
    966933RecorderWindow::BreakRecordConnection()
    967934{
    968     status_t err;
     935    status_t err = B_OK;
     936
     937    err = fRecorder->Stop(true);
     938    if (err < B_OK)
     939        return err;
     940
     941    err = fRecorder->Disconnect();
    969942
    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;
    977943    return err;
    978944}
    979945
    RecorderWindow::StopRecording()  
    984950    if (!fRecording)
    985951        return B_OK;
    986952    fRecording = false;
     953
    987954    BreakRecordConnection();
    988     fRecordNode->SetHooks(NULL,NULL,NULL);
     955
     956    fRecorder->SetHooks(NULL, NULL, NULL);
     957
    989958    if (fRecSize > 0) {
    990959
    991960        wave_struct header;
    RecorderWindow::RemoveCurrentSoundItem() {  
    12401209
    12411210void
    12421211RecorderWindow::RecordFile(void* cookie, bigtime_t timestamp,
    1243     void* data, size_t size, const media_raw_audio_format &format)
     1212    void* data, size_t size, const media_format &format)
    12441213{
    12451214    //  Callback called from the SoundConsumer when receiving buffers.
    12461215    RecorderWindow * window = (RecorderWindow *)cookie;
    RecorderWindow::RecordFile(void* cookie, bigtime_t timestamp,  
    12491218        //  Write the data to file (we don't buffer or guard file access
    12501219        //  or anything)
    12511220        window->fRecFile.WriteAt(window->fRecSize, data, size);
    1252         window->fVUView->ComputeLevels(data, size, format.format);
     1221        window->fVUView->ComputeLevels(data, size, format.u.raw_audio.format);
    12531222        window->fRecSize += size;
    12541223    }
    12551224}
  • 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 96dd800..1e52601 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..8dde9c9
    - +  
     1//  MediaRecorder.cpp
     2//  -----------------
     3//  Copyright 1999, Be Incorporated.   All Rights Reserved.
     4//  Copyright 2014, Dario Casalinuovo. All Rights Reserved.
     5//  This file may be used under the terms of the Be Sample Code License.
     6
     7#include "MediaRecorder.h"
     8
     9#include <MediaAddOn.h>
     10#include <MediaRoster.h>
     11#include <TimeSource.h>
     12
     13#include "MediaDebug.h"
     14#include "MediaRecorderNode.h"
     15
     16
     17class StNodeRelease {
     18public:
     19                StNodeRelease(media_node& mediaNode, bool rel = true)
     20                    :
     21                    node(mediaNode),
     22                    release(rel)
     23                    {}
     24
     25                ~StNodeRelease()
     26                {
     27                    if (release)
     28                        BMediaRoster::Roster()->ReleaseNode(node);
     29                }
     30
     31    void        SetRelease(bool rel)
     32                {
     33                    release = rel;
     34                }
     35
     36private:
     37    media_node& node;
     38    bool        release;
     39};
     40
     41
     42BMediaRecorder::BMediaRecorder(const char* name, int32 threadPriority,
     43    media_type type)
     44    :
     45    fInitErr(B_OK),
     46    fConnected(false),
     47    fRunning(false),
     48    fTimeSource(NULL),
     49    fRecordHook(NULL),
     50    fNotifyHook(NULL),
     51    fNode(NULL),
     52    fBufferCookie(NULL)
     53{
     54    CALLED();
     55
     56    BMediaRoster::Roster(&fInitErr);
     57
     58    if (fInitErr == B_OK) {
     59
     60        if (threadPriority < 5)
     61            threadPriority = 115;
     62
     63        fNode = new BMediaRecorderNode(name, this, threadPriority, type);
     64        fInitErr = BMediaRoster::CurrentRoster()->RegisterNode(fNode);
     65    }
     66}
     67
     68
     69BMediaRecorder::~BMediaRecorder()
     70{
     71    CALLED();
     72
     73    if (fNode != NULL) {
     74        Stop();
     75        Disconnect();
     76        fNode->Release();
     77    }
     78
     79    if (fTimeSource != NULL)
     80        fTimeSource->Release();
     81
     82}
     83
     84
     85status_t
     86BMediaRecorder::InitCheck() const
     87{
     88    CALLED();
     89
     90    return fInitErr;
     91}
     92
     93
     94void
     95BMediaRecorder::SetAcceptedFormat(const media_format& format)
     96{
     97    CALLED();
     98
     99    fNode->SetAcceptedFormat(format);
     100}
     101
     102
     103status_t
     104BMediaRecorder::SetHooks(ProcessFunc recordFunc, NotifyFunc notifyFunc,
     105    void* cookie)
     106{
     107    CALLED();
     108
     109    fRecordHook = recordFunc;
     110    fNotifyHook = notifyFunc;
     111    fBufferCookie = cookie;
     112
     113    return B_OK;
     114}
     115
     116
     117void
     118BMediaRecorder::BufferReceived(void* buffer, size_t size,
     119    const media_header& header)
     120{
     121    CALLED();
     122
     123    if (fRecordHook) {
     124        (*fRecordHook)(fBufferCookie, header.start_time,
     125            buffer, size, Format());
     126    }
     127}
     128
     129
     130status_t
     131BMediaRecorder::Connect(const media_format& format, uint32 flags)
     132{
     133    CALLED();
     134
     135    if (fInitErr != B_OK)
     136        return fInitErr;
     137
     138    if (fConnected)
     139        return B_MEDIA_ALREADY_CONNECTED;
     140
     141    return _Connect(&format, flags, NULL, NULL, NULL);
     142}
     143
     144
     145status_t
     146BMediaRecorder::Connect(const dormant_node_info& dormantInfo,
     147    const media_format* format, uint32 flags)
     148{
     149    CALLED();
     150
     151    if (fInitErr != B_OK)
     152        return fInitErr;
     153
     154    if (fConnected)
     155        return B_MEDIA_ALREADY_CONNECTED;
     156
     157    return _Connect(format, flags, &dormantInfo, NULL, NULL);
     158}
     159
     160
     161status_t
     162BMediaRecorder::Connect(const media_node& node,
     163    const media_output* useOutput, const media_format* format,
     164    uint32 flags)
     165{
     166    CALLED();
     167
     168    if (fInitErr != B_OK)
     169        return fInitErr;
     170
     171    if (fConnected)
     172        return B_MEDIA_ALREADY_CONNECTED;
     173
     174    return _Connect(format, flags, NULL, &node, useOutput);
     175}
     176
     177
     178status_t
     179BMediaRecorder::Disconnect()
     180{
     181    CALLED();
     182
     183    status_t err = B_OK;
     184
     185    if (fInitErr != B_OK)
     186        return fInitErr;
     187
     188    if (!fConnected)
     189        return B_MEDIA_NOT_CONNECTED;
     190
     191    if (!fNode)
     192        return B_ERROR;
     193
     194    if (fRunning)
     195        err = Stop();
     196
     197    if (err != B_OK)
     198        return err;
     199
     200    // do the disconnect
     201    err = BMediaRoster::CurrentRoster()->Disconnect(
     202        fOutputNode.node, fOutput.source,
     203            fNode->Node().node, fInput.destination);
     204
     205    BMediaRoster::Roster()->ReleaseNode(fOutputNode);
     206
     207    if (fTimeSource != NULL) {
     208        fTimeSource->Release();
     209        fTimeSource = NULL;
     210    }
     211
     212    fConnected = false;
     213    fRunning = false;
     214
     215    return err;
     216}
     217
     218
     219status_t
     220BMediaRecorder::Start(bool force)
     221{
     222    CALLED();
     223
     224    if (fInitErr != B_OK)
     225        return fInitErr;
     226
     227    if (!fConnected)
     228        return B_MEDIA_NOT_CONNECTED;
     229
     230    if (fRunning && !force)
     231        return EALREADY;
     232
     233    if (!fNode)
     234        return B_ERROR;
     235
     236    // start node here
     237    status_t err = B_OK;
     238
     239    if (fNode->Node().kind & B_TIME_SOURCE) {
     240        err = BMediaRoster::CurrentRoster()->StartTimeSource(
     241            fOutputNode, BTimeSource::RealTime());
     242    } else {
     243        err = BMediaRoster::CurrentRoster()->StartNode(
     244            fOutputNode, fTimeSource->Now());
     245    }
     246
     247    // then un-mute it
     248    if (err == B_OK)
     249        fNode->SetDataEnabled(true);
     250
     251    fRunning = (err == B_OK);
     252
     253    return err;
     254}
     255
     256
     257status_t
     258BMediaRecorder::Stop(bool force)
     259{
     260    CALLED();
     261
     262    if (fInitErr != B_OK)
     263        return fInitErr;
     264
     265    if (!fRunning && !force)
     266        return EALREADY;
     267
     268    if (!fNode)
     269        return B_ERROR;
     270
     271    // should have the Node mute the output here
     272    fNode->SetDataEnabled(false);
     273
     274    fRunning = false;
     275
     276    return BMediaRoster::CurrentRoster()->StopNode(fNode->Node(), 0);
     277}
     278
     279
     280bool
     281BMediaRecorder::IsRunning() const
     282{
     283    CALLED();
     284
     285    return fRunning;
     286}
     287
     288
     289bool
     290BMediaRecorder::IsConnected() const
     291{
     292    CALLED();
     293
     294    return fConnected;
     295}
     296
     297
     298const media_output&
     299BMediaRecorder::MediaOutput() const
     300{
     301    CALLED();
     302
     303    return fOutput;
     304}
     305
     306
     307const media_input&
     308BMediaRecorder::MediaInput() const
     309{
     310    CALLED();
     311
     312    return fInput;
     313}
     314
     315
     316const media_format&
     317BMediaRecorder::Format() const
     318{
     319    CALLED();
     320
     321    return fOutput.format;
     322}
     323
     324
     325status_t
     326BMediaRecorder::_Connect(const media_format* format,
     327    uint32 flags, const dormant_node_info* dormantNode,
     328    const media_node* mediaNode, const media_output* output)
     329{
     330    CALLED();
     331
     332    status_t err = B_OK;
     333    media_format ourFormat;
     334    media_node node;
     335    StNodeRelease away(node, false);
     336    media_output ourOutput;
     337
     338    // argument checking and set-up
     339    if (format != NULL)
     340        ourFormat = *format;
     341
     342    if (fNode == NULL)
     343        return B_ERROR;
     344
     345    if (mediaNode == NULL && output != NULL)
     346        return B_MISMATCHED_VALUES;
     347
     348    if (dormantNode != NULL && (mediaNode != NULL || output != NULL))
     349        return B_MISMATCHED_VALUES;
     350
     351    if (format == NULL && output != NULL)
     352        ourFormat = output->format;
     353
     354    fNode->SetAcceptedFormat(ourFormat);
     355
     356    // figure out the node to instantiate
     357    if (dormantNode != NULL) {
     358
     359        err = BMediaRoster::Roster()->InstantiateDormantNode(
     360            *dormantNode, &node, B_FLAVOR_IS_GLOBAL);
     361
     362        away.SetRelease(true);
     363
     364    } else if (mediaNode != NULL) {
     365        node = *mediaNode;
     366    } else {
     367        switch (ourFormat.type) {
     368
     369            // switch on format for default
     370            case B_MEDIA_RAW_AUDIO:
     371                err = BMediaRoster::Roster()->GetAudioInput(&node);
     372                away.SetRelease(true);
     373                break;
     374
     375            case B_MEDIA_RAW_VIDEO:
     376            case B_MEDIA_ENCODED_VIDEO:
     377                err = BMediaRoster::Roster()->GetVideoInput(&node);
     378                away.SetRelease(true);
     379                break;
     380
     381            // give up?
     382            default:
     383                return B_MEDIA_BAD_FORMAT;
     384                break;
     385        }
     386    }
     387
     388    fOutputNode = node;
     389
     390    // figure out the output provided
     391
     392    if (output != NULL) {
     393        ourOutput = *output;
     394    } else if (err == B_OK) {
     395
     396        media_output outputs[10];
     397        int32 count = 10;
     398
     399        err = BMediaRoster::Roster()->GetFreeOutputsFor(node,
     400            outputs, count, &count, ourFormat.type);
     401
     402        if (err != B_OK)
     403            return err;
     404
     405        err = B_MEDIA_BAD_SOURCE;
     406
     407        for (int i = 0; i < count; i++) {
     408            if (format_is_compatible(outputs[i].format, ourFormat)) {
     409                ourOutput = outputs[i];
     410                err = B_OK;
     411                ourFormat = outputs[i].format;
     412                break;
     413            }
     414
     415        }
     416
     417    } else {
     418        return err;
     419    }
     420
     421    if (ourOutput.source == media_source::null)
     422        return B_MEDIA_BAD_SOURCE;
     423
     424    // find our Node's free input
     425    media_input ourInput;
     426    err = fNode->GetInput(&ourInput);
     427
     428    media_node time_source;
     429    if (node.kind & B_TIME_SOURCE)
     430        time_source = node;
     431    else
     432        BMediaRoster::Roster()->GetSystemTimeSource(&time_source);
     433
     434    // set time source
     435    if (err == B_OK) {
     436        BMediaRoster::Roster()->SetTimeSourceFor(
     437            fNode->Node().node, time_source.node);
     438
     439        fTimeSource = BMediaRoster::CurrentRoster()->MakeTimeSourceFor(
     440            fNode->Node());
     441    }
     442
     443    // start the recorder node (it's always running)
     444    if (err == B_OK)
     445        err = BMediaRoster::CurrentRoster()->StartNode(
     446            fOutputNode, fTimeSource->Now());
     447
     448
     449    // perform the connection
     450    if (err == B_OK) {
     451        fOutput = ourOutput;
     452        fInput = ourInput;
     453
     454        err = BMediaRoster::CurrentRoster()->Connect(fOutput.source,
     455            fInput.destination, &ourFormat, &fOutput, &fInput,
     456            BMediaRoster::B_CONNECT_MUTED);
     457
     458        if (err == B_OK) {
     459            fConnected = true;
     460            away.SetRelease(false);
     461        }
     462    }
     463
     464    return err;
     465}
  • 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..6dacaa6
    - +  
     1//  MediaRecorderNode.cpp
     2//  ---------------------
     3//  Copyright 1999, Be Incorporated.   All Rights Reserved.
     4//  Copyright 2014, Dario Casalinuovo. All Rights Reserved.
     5//  This file may be used under the terms of the Be Sample Code License.
     6
     7#include "MediaRecorderNode.h"
     8
     9#include <Buffer.h>
     10#include <TimedEventQueue.h>
     11#include <TimeSource.h>
     12
     13#include "MediaDebug.h"
     14#include "MediaRecorder.h"
     15
     16
     17BMediaRecorderNode::BMediaRecorderNode(const char* name,
     18    BMediaRecorder* rec, int32 priority, media_type type)
     19    :
     20    BMediaNode(name),
     21    BMediaEventLooper(),
     22    BBufferConsumer(type),
     23    fRecorder(rec)
     24{
     25    CALLED();
     26
     27    SetPriority(priority);
     28
     29    fInput.destination.id = 1;
     30    fInput.destination.port = ControlPort();
     31
     32    fName.SetTo(name);
     33
     34    BString str(name);
     35    str << " Input";
     36    strcpy(fInput.name, str.String());
     37}
     38
     39
     40BMediaRecorderNode::~BMediaRecorderNode()
     41{
     42    CALLED();
     43}
     44
     45
     46BMediaAddOn*
     47BMediaRecorderNode::AddOn(int32* id) const
     48{
     49    CALLED();
     50
     51    if (id)
     52        *id = -1;
     53
     54    return NULL;
     55}
     56
     57
     58void
     59BMediaRecorderNode::SetAcceptedFormat(const media_format& format)
     60{
     61    CALLED();
     62
     63    fOKFormat = format;
     64}
     65
     66
     67status_t
     68BMediaRecorderNode::GetInput(media_input* outInput)
     69{
     70    CALLED();
     71
     72    fInput.node = Node();
     73    *outInput = fInput;
     74
     75    return B_OK;
     76}
     77
     78
     79void
     80BMediaRecorderNode::SetDataEnabled(bool enabled)
     81{
     82    CALLED();
     83
     84    int32 tag;
     85
     86    SetOutputEnabled(fInput.source,
     87        fInput.destination, enabled, NULL, &tag);
     88}
     89
     90
     91void
     92BMediaRecorderNode::HandleEvent(const media_timed_event* event,
     93    bigtime_t lateness, bool realTimeEvent)
     94{
     95    CALLED();
     96
     97    //  we ignore them all!
     98}
     99
     100
     101void
     102BMediaRecorderNode::Start(bigtime_t performanceTime)
     103{
     104    CALLED();
     105
     106    if (fRecorder->fNotifyHook) {
     107        (*fRecorder->fNotifyHook)(fRecorder->fBufferCookie,
     108            B_WILL_START, performanceTime);
     109    }
     110
     111    fRecorder->fRunning = true;
     112}
     113
     114
     115void
     116BMediaRecorderNode::Stop(bigtime_t performanceTime, bool immediate)
     117{
     118    CALLED();
     119
     120    if (fRecorder->fNotifyHook) {
     121        (*fRecorder->fNotifyHook)(fRecorder->fBufferCookie,
     122            B_WILL_STOP, performanceTime, immediate);
     123    }
     124
     125    fRecorder->fRunning = false;
     126}
     127
     128
     129void
     130BMediaRecorderNode::Seek(bigtime_t mediaTime, bigtime_t performanceTime)
     131{
     132    CALLED();
     133
     134    if (fRecorder->fNotifyHook) {
     135        (*fRecorder->fNotifyHook)(fRecorder->fBufferCookie,
     136            B_WILL_SEEK, performanceTime, mediaTime);
     137    }
     138}
     139
     140
     141void
     142BMediaRecorderNode::TimeWarp(bigtime_t realTime, bigtime_t performanceTime)
     143{
     144    CALLED();
     145
     146    // Since buffers will come pre-time-stamped, we only need to look
     147    // at them, so we can ignore the time warp as a consumer.
     148    if (fRecorder->fNotifyHook) {
     149        (*fRecorder->fNotifyHook)(fRecorder->fBufferCookie,
     150            B_WILL_TIMEWARP, realTime, performanceTime);
     151    }
     152}
     153
     154
     155status_t
     156BMediaRecorderNode::HandleMessage(int32 message,
     157    const void* data, size_t size)
     158{
     159    CALLED();
     160
     161    if (BBufferConsumer::HandleMessage(message, data, size) < 0
     162        && BMediaEventLooper::HandleMessage(message, data, size) < 0
     163        && BMediaNode::HandleMessage(message, data, size) < 0) {
     164
     165        HandleBadMessage(message, data, size);
     166
     167        return B_ERROR;
     168    }
     169
     170    return B_OK;
     171}
     172
     173
     174status_t
     175BMediaRecorderNode::AcceptFormat(const media_destination& dest,
     176    media_format* format)
     177{
     178    CALLED();
     179
     180    if (format_is_compatible(*format, fOKFormat))
     181        return B_OK;
     182
     183    *format = fOKFormat;
     184
     185    return B_MEDIA_BAD_FORMAT;
     186}
     187
     188
     189status_t
     190BMediaRecorderNode::GetNextInput(int32* cookie, media_input* outInput)
     191{
     192    CALLED();
     193
     194    if (*cookie == 0) {
     195        *cookie = -1;
     196        *outInput = fInput;
     197        return B_OK;
     198    }
     199
     200    return B_BAD_INDEX;
     201}
     202
     203
     204void
     205BMediaRecorderNode::DisposeInputCookie(int32 cookie)
     206{
     207    CALLED();
     208}
     209
     210
     211void
     212BMediaRecorderNode::BufferReceived(BBuffer* buffer)
     213{
     214    CALLED();
     215
     216    fRecorder->BufferReceived(buffer->Data(), buffer->SizeUsed(),
     217        *buffer->Header());
     218
     219    buffer->Recycle();
     220}
     221
     222
     223void
     224BMediaRecorderNode::ProducerDataStatus(
     225    const media_destination& forWhom, int32 status,
     226    bigtime_t performanceTime)
     227{
     228    CALLED();
     229}
     230
     231
     232status_t
     233BMediaRecorderNode::GetLatencyFor(const media_destination& forWhom,
     234    bigtime_t* outLatency, media_node_id* outTimesource)
     235{
     236    CALLED();
     237
     238    *outLatency = 0;
     239    *outTimesource = TimeSource()->ID();
     240
     241    return B_OK;
     242}
     243
     244
     245status_t
     246BMediaRecorderNode::Connected(const media_source &producer,
     247    const media_destination &where, const media_format &withFormat,
     248    media_input* outInput)
     249{
     250    CALLED();
     251
     252    fInput.source = producer;
     253    fInput.format = withFormat;
     254    *outInput = fInput;
     255
     256    fRecorder->fConnected = true;
     257    fRecorder->fInput = fInput;
     258
     259    return B_OK;
     260}
     261
     262
     263void
     264BMediaRecorderNode::Disconnected(const media_source& producer,
     265    const media_destination& where)
     266{
     267    CALLED();
     268
     269    fInput.source = media_source::null;
     270
     271    fRecorder->fConnected = false;
     272
     273    fRecorder->fInput.format = fOKFormat;
     274}
     275
     276
     277status_t
     278BMediaRecorderNode::FormatChanged(const media_source& producer,
     279    const media_destination& consumer, int32 tag,
     280    const media_format& format)
     281{
     282    CALLED();
     283
     284    if (!format_is_compatible(format, fOKFormat))
     285        return B_MEDIA_BAD_FORMAT;
     286
     287    fInput.format = format;
     288
     289    return B_OK;
     290}
  • 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 }