Skip to content
This repository
Browse code

Merge pull request #2202 from davilla/fix-nullsink

Fix nullsink
  • Loading branch information...
commit 3f2c26bbf3be4041f7ce060eee2bfc310013aafc 2 parents 5d8749d + 5c8b0d0
davilla authored February 18, 2013
146  xbmc/cores/AudioEngine/Sinks/AESinkNULL.cpp
@@ -20,21 +20,21 @@
20 20
 
21 21
 #include "system.h"
22 22
 
23  
-#include "AESinkNULL.h"
24 23
 #include <stdint.h>
25 24
 #include <limits.h>
26 25
 
27  
-#include "guilib/LocalizeStrings.h"
28  
-#include "dialogs/GUIDialogKaiToast.h"
29  
-
30  
-#include "Utils/AEUtil.h"
31  
-#include "utils/StdString.h"
  26
+#include "AESinkNULL.h"
  27
+#include "cores/AudioEngine/Utils/AEUtil.h"
32 28
 #include "utils/log.h"
33  
-#include "utils/MathUtils.h"
34  
-#include "utils/TimeUtils.h"
35  
-#include "settings/GUISettings.h"
36 29
 
37  
-CAESinkNULL::CAESinkNULL() {
  30
+CAESinkNULL::CAESinkNULL()
  31
+  : CThread("nullsink"),
  32
+    m_draining(false),
  33
+    m_sink_frameSize(0),
  34
+    m_sinkbuffer_size(0),
  35
+    m_sinkbuffer_level(0),
  36
+    m_sinkbuffer_sec_per_byte(0)
  37
+{
38 38
 }
39 39
 
40 40
 CAESinkNULL::~CAESinkNULL()
@@ -43,56 +43,138 @@ CAESinkNULL::~CAESinkNULL()
43 43
 
44 44
 bool CAESinkNULL::Initialize(AEAudioFormat &format, std::string &device)
45 45
 {
46  
-  m_msPerFrame           = 1000.0f / format.m_sampleRate;
47  
-  m_ts                   = 0;
48  
-
  46
+  // setup for a 250ms sink feed from SoftAE 
49 47
   format.m_dataFormat    = AE_IS_RAW(format.m_dataFormat) ? AE_FMT_S16NE : AE_FMT_FLOAT;
50  
-  format.m_frames        = format.m_sampleRate / 1000 * 500; /* 500ms */
  48
+  format.m_frames        = format.m_sampleRate / 1000 * 250;
51 49
   format.m_frameSamples  = format.m_channelLayout.Count();
52 50
   format.m_frameSize     = format.m_frameSamples * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);
53  
-
54  
-#if 0
55  
-  /* FIXME, CAUSES A DEADLOCK */
56  
-  /* display failure notification */
57  
-  CGUIDialogKaiToast::QueueNotification(
58  
-    CGUIDialogKaiToast::Error,
59  
-    g_localizeStrings.Get(34402),
60  
-    g_localizeStrings.Get(34403),
61  
-    TOAST_DISPLAY_TIME,
62  
-    false
63  
-  );
64  
-#endif
  51
+  m_format = format;
  52
+
  53
+  // setup a pretend 500ms internal buffer
  54
+  m_sink_frameSize = format.m_channelLayout.Count() * CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3;
  55
+  m_sinkbuffer_size = (double)m_sink_frameSize * format.m_sampleRate / 2;
  56
+  m_sinkbuffer_sec_per_byte = 1.0 / (double)(m_sink_frameSize * format.m_sampleRate);
  57
+
  58
+  m_draining = false;
  59
+  m_wake.Reset();
  60
+  m_inited.Reset();
  61
+  Create();
  62
+  if (!m_inited.WaitMSec(100))
  63
+  {
  64
+    while(!m_inited.WaitMSec(1))
  65
+      Sleep(10);
  66
+  }
65 67
 
66 68
   return true;
67 69
 }
68 70
 
69 71
 void CAESinkNULL::Deinitialize()
70 72
 {
  73
+  // force m_bStop and set m_wake, if might be sleeping.
  74
+  m_bStop = true;
  75
+  StopThread();
71 76
 }
72 77
 
73 78
 bool CAESinkNULL::IsCompatible(const AEAudioFormat format, const std::string device)
74 79
 {
75  
-  return false;
  80
+  return ((m_format.m_sampleRate    == format.m_sampleRate) &&
  81
+          (m_format.m_dataFormat    == format.m_dataFormat) &&
  82
+          (m_format.m_channelLayout == format.m_channelLayout));
76 83
 }
77 84
 
78 85
 double CAESinkNULL::GetDelay()
79 86
 {
80  
-  return std::max(0.0, (double)(m_ts - CurrentHostCounter()) / 1000000.0f);
  87
+  double sinkbuffer_seconds_to_empty = m_sinkbuffer_sec_per_byte * (double)m_sinkbuffer_level;
  88
+  return sinkbuffer_seconds_to_empty;
  89
+}
  90
+
  91
+double CAESinkNULL::GetCacheTime()
  92
+{
  93
+  double sinkbuffer_seconds_to_empty = m_sinkbuffer_sec_per_byte * (double)m_sinkbuffer_level;
  94
+  return sinkbuffer_seconds_to_empty;
  95
+}
  96
+
  97
+double CAESinkNULL::GetCacheTotal()
  98
+{
  99
+  return m_sinkbuffer_sec_per_byte * (double)m_sinkbuffer_size;
81 100
 }
82 101
 
83 102
 unsigned int CAESinkNULL::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio)
84 103
 {
85  
-  float timeout = m_msPerFrame * frames;
86  
-  m_ts = CurrentHostCounter() + MathUtils::round_int(timeout * 1000000.0f);
87  
-  Sleep(MathUtils::round_int(timeout));
  104
+  unsigned int max_frames = (m_sinkbuffer_size - m_sinkbuffer_level) / m_sink_frameSize;
  105
+  if (frames > max_frames)
  106
+    frames = max_frames;
  107
+
  108
+  if (hasAudio && frames)
  109
+  {
  110
+    m_sinkbuffer_level += frames * m_sink_frameSize;
  111
+    m_wake.Set();
  112
+  }
  113
+  // AddPackets runs under a non-idled AE thread we must block or sleep.
  114
+  // Trying to calc the optimal sleep is tricky so just a minimal sleep.
  115
+  Sleep(10);
  116
+
88 117
   return frames;
89 118
 }
90 119
 
91 120
 void CAESinkNULL::Drain()
92 121
 {
  122
+  m_draining = true;
  123
+  m_wake.Set();
93 124
 }
94 125
 
95 126
 void CAESinkNULL::EnumerateDevices (AEDeviceList &devices, bool passthrough)
96 127
 {
97  
-  /* we never return any devices */
  128
+  // we never return any devices
  129
+}
  130
+
  131
+void CAESinkNULL::Process()
  132
+{
  133
+  CLog::Log(LOGDEBUG, "CAESinkNULL::Process");
  134
+
  135
+  // The object has been created and waiting to play,
  136
+  m_inited.Set();
  137
+  // yield to give other threads a chance to do some work.
  138
+  sched_yield();
  139
+
  140
+  SetPriority(THREAD_PRIORITY_ABOVE_NORMAL);
  141
+  while (!m_bStop)
  142
+  {
  143
+    if (m_draining)
  144
+    {
  145
+      // TODO: is it correct to not take data at the appropriate rate while draining?
  146
+      m_sinkbuffer_level = 0;
  147
+      m_draining = false;
  148
+    }
  149
+
  150
+    // pretend we have a 64k audio buffer
  151
+    unsigned int min_buffer_size = 64 * 1024;
  152
+    unsigned int read_bytes = m_sinkbuffer_level;
  153
+    if (read_bytes > min_buffer_size)
  154
+      read_bytes = min_buffer_size;
  155
+
  156
+    if (read_bytes > 0)
  157
+    {
  158
+      // drain it
  159
+      m_sinkbuffer_level -= read_bytes;
  160
+
  161
+      // we MUST drain at the correct audio sample rate
  162
+      // or the NULL sink will not work right. So calc
  163
+      // an approximate sleep time.
  164
+      int frames_written = read_bytes / m_sink_frameSize;
  165
+      double empty_ms = 1000.0 * (double)frames_written / m_format.m_sampleRate;
  166
+      #if defined(_LINUX)
  167
+        usleep(empty_ms * 1000.0);
  168
+      #else
  169
+        Sleep((int)empty_ms);
  170
+      #endif
  171
+    }
  172
+
  173
+    if (m_sinkbuffer_level == 0)
  174
+    {
  175
+      // sleep this audio thread, we will get woken when we have audio data.
  176
+      m_wake.WaitMSec(250);
  177
+    }
  178
+  }
  179
+  SetPriority(THREAD_PRIORITY_NORMAL);
98 180
 }
23  xbmc/cores/AudioEngine/Sinks/AESinkNULL.h
@@ -21,10 +21,9 @@
21 21
 
22 22
 #include "system.h"
23 23
 
24  
-#include "Interfaces/AESink.h"
25  
-#include <stdint.h>
  24
+#include "cores/AudioEngine/Interfaces/AESink.h"
26 25
 
27  
-class CAESinkNULL : public IAESink
  26
+class CAESinkNULL : public CThread, public IAESink
28 27
 {
29 28
 public:
30 29
   virtual const char *GetName() { return "NULL"; }
@@ -32,18 +31,26 @@ class CAESinkNULL : public IAESink
32 31
   CAESinkNULL();
33 32
   virtual ~CAESinkNULL();
34 33
 
35  
-  virtual bool Initialize  (AEAudioFormat &format, std::string &device);
  34
+  virtual bool Initialize(AEAudioFormat &format, std::string &device);
36 35
   virtual void Deinitialize();
37 36
   virtual bool IsCompatible(const AEAudioFormat format, const std::string device);
38 37
 
39 38
   virtual double       GetDelay        ();
40  
-  virtual double       GetCacheTime    () { return 0.0; }
41  
-  virtual double       GetCacheTotal   () { return 0.0; }
  39
+  virtual double       GetCacheTime    ();
  40
+  virtual double       GetCacheTotal   ();
42 41
   virtual unsigned int AddPackets      (uint8_t *data, unsigned int frames, bool hasAudio);
43 42
   virtual void         Drain           ();
44 43
 
45 44
   static void          EnumerateDevices(AEDeviceList &devices, bool passthrough);
46 45
 private:
47  
-  int64_t m_ts;
48  
-  float   m_msPerFrame;
  46
+  virtual void         Process();
  47
+
  48
+  CEvent               m_wake;
  49
+  CEvent               m_inited;
  50
+  volatile bool        m_draining;
  51
+  AEAudioFormat        m_format;
  52
+  unsigned int         m_sink_frameSize;
  53
+  unsigned int         m_sinkbuffer_size;  ///< total size of the buffer
  54
+  unsigned int         m_sinkbuffer_level; ///< current level in the buffer
  55
+  double               m_sinkbuffer_sec_per_byte;
49 56
 };
10  xbmc/cores/AudioEngine/Utils/AERingBuffer.h
@@ -179,7 +179,8 @@ class AERingBuffer {
179 179
 #ifdef AE_RING_BUFFER_DEBUG
180 180
       CLog::Log(LOGDEBUG, "AERingBuffer: Reading from: %u size: %u space before: %u\n", m_iWritePos, size, space);
181 181
 #endif
182  
-      memcpy(dest, &(m_Buffer[m_iReadPos]), size);
  182
+      if (dest)
  183
+        memcpy(dest, &(m_Buffer[m_iReadPos]), size);
183 184
       m_iReadPos+=size;
184 185
     }
185 186
     //need to wrap
@@ -190,8 +191,11 @@ class AERingBuffer {
190 191
 #ifdef AE_RING_BUFFER_DEBUG
191 192
       CLog::Log(LOGDEBUG, "AERingBuffer: Reading from (split) first: %u second: %u size: %u space before: %u\n", first, second, size, space);
192 193
 #endif
193  
-      memcpy(dest, &(m_Buffer[m_iReadPos]), first);
194  
-      memcpy(&dest[first], &(m_Buffer[0]), second);
  194
+      if (dest)
  195
+      {
  196
+        memcpy(dest, &(m_Buffer[m_iReadPos]), first);
  197
+        memcpy(&dest[first], &(m_Buffer[0]), second);
  198
+      }
195 199
       m_iReadPos = second;
196 200
     }
197 201
     //we can increase the read count now

0 notes on commit 3f2c26b

Please sign in to comment.
Something went wrong with that request. Please try again.