changed: Improve (fallback) logic for subtitle downloading
[vuplus_xbmc] / xbmc / cores / AudioEngine / Engines / CoreAudio / CoreAudioGraph.cpp
1 /*
2  *      Copyright (C) 2011-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "CoreAudioGraph.h"
22
23 #include "CoreAudioAEHAL.h"
24 #include "CoreAudioMixMap.h"
25 #include "CoreAudioUnit.h"
26
27 #include "utils/log.h"
28 #include "utils/SystemInfo.h"
29
30 CCoreAudioGraph::CCoreAudioGraph() :
31   m_audioGraph    (NULL ),
32   m_inputUnit     (NULL ),
33   m_audioUnit     (NULL ),
34   m_mixerUnit     (NULL ),
35   m_initialized   (false),
36   m_deviceId      (NULL ),
37   m_allowMixing   (false),
38   m_mixMap        (NULL )
39 {
40   for (int i = 0; i < MAX_CONNECTION_LIMIT; i++)
41   {
42     m_reservedBusNumber[i] = false;
43   }
44 }
45
46 CCoreAudioGraph::~CCoreAudioGraph()
47 {
48   Close();
49
50   delete m_mixMap;
51 }
52
53 bool CCoreAudioGraph::Open(ICoreAudioSource *pSource, AEAudioFormat &format,
54   AudioDeviceID deviceId, bool allowMixing, AudioChannelLayoutTag layoutTag, float initVolume, bool encoded)
55 {
56   AudioStreamBasicDescription fmt = {0};
57   AudioStreamBasicDescription inputFormat = {0};
58   AudioStreamBasicDescription outputFormat = {0};
59
60   m_deviceId = deviceId;
61   m_allowMixing = allowMixing;
62
63   OSStatus ret = NewAUGraph(&m_audioGraph);
64   if (ret)
65   {
66     CLog::Log(LOGERROR, "CCoreAudioGraph::Open: "
67       "Error create audio grpah. Error = %s", GetError(ret).c_str());
68     return false;
69   }
70   ret = AUGraphOpen(m_audioGraph);
71   if (ret)
72   {
73     CLog::Log(LOGERROR, "CCoreAudioGraph::Open: "
74       "Error open audio grpah. Error = %s", GetError(ret).c_str());
75     return false;
76   }
77
78   // get output unit
79   if (m_audioUnit)
80   {
81     CLog::Log(LOGERROR, "CCoreAudioGraph::Open: "
82       "Error audio unit already open. double call ?");
83     return false;
84   }
85
86   m_audioUnit = new CAUOutputDevice();
87   if (!m_audioUnit->Open(m_audioGraph,
88     kAudioUnitType_Output, kAudioUnitSubType_HALOutput, kAudioUnitManufacturer_Apple))
89     return false;
90   m_audioUnit->SetBus(GetFreeBus());
91
92   m_audioUnit->GetFormatDesc(format, &inputFormat, &fmt, encoded);
93
94   if (!m_audioUnit->EnableInputOuput())
95     return false;
96
97   if (!m_audioUnit->SetCurrentDevice(deviceId))
98     return false;
99
100   SetCurrentVolume(initVolume);
101
102   if (allowMixing)
103   {
104     delete m_mixMap;
105     m_mixMap = CCoreAudioMixMap::CreateMixMap(m_audioUnit, format, layoutTag);
106
107     if (m_mixMap && m_mixMap->IsValid())
108     {
109       // maximum input channel ber input bus
110       //fmt.mChannelsPerFrame = MAXIMUM_MIXER_CHANNELS;
111
112       // get output unit
113       if (m_inputUnit)
114       {
115         CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error mixer unit already open. double call ?");
116         return false;
117       }
118
119       m_inputUnit = new CAUOutputDevice();
120
121       if (!m_inputUnit->Open(m_audioGraph,
122         kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter, kAudioUnitManufacturer_Apple))
123         return false;
124
125       if (!m_inputUnit->SetFormat(&inputFormat, kAudioUnitScope_Input, kOutputBus))
126         return false;
127
128       if (!m_inputUnit->SetFormat(&fmt, kAudioUnitScope_Output, kOutputBus))
129         return false;
130
131       // get mixer unit
132       if (m_mixerUnit)
133       {
134         CLog::Log(LOGERROR, "CCoreAudioGraph::Open: Error mixer unit already open. double call ?");
135         return false;
136       }
137
138       m_mixerUnit = new CAUMatrixMixer();
139
140       if (!m_mixerUnit->Open(m_audioGraph,
141         kAudioUnitType_Mixer, kAudioUnitSubType_MatrixMixer, kAudioUnitManufacturer_Apple))
142         return false;
143
144       // set number of input buses
145       if (!m_mixerUnit->SetInputBusCount(MAX_CONNECTION_LIMIT))
146         return false;
147
148       // set number of output buses
149       if (!m_mixerUnit->SetOutputBusCount(1))
150         return false;
151
152       if (!m_mixerUnit->SetInputBusFormat(MAX_CONNECTION_LIMIT, &fmt))
153         return false;
154
155       // Update format structure to reflect the desired format from the mixer
156       // The output format of the mixer is identical to the input format, except for the channel count
157       AudioStreamBasicDescription mixOutput(fmt);
158       mixOutput.mChannelsPerFrame = m_mixMap->GetOutputChannels();
159
160       if (!m_mixerUnit->SetFormat(&mixOutput, kAudioUnitScope_Output, kOutputBus))
161         return false;
162
163       ret =  AUGraphConnectNodeInput(m_audioGraph, m_mixerUnit->GetNode(), 0, m_audioUnit->GetNode(), 0);
164       if (ret)
165       {
166         CLog::Log(LOGERROR, "CCoreAudioGraph::Open: "
167           "Error connecting m_m_mixerNode. Error = %s", GetError(ret).c_str());
168         return false;
169       }
170
171       m_mixerUnit->SetBus(0);
172
173       // configure output unit
174       int busNumber = GetFreeBus();
175
176       ret = AUGraphConnectNodeInput(m_audioGraph, m_inputUnit->GetNode(), 0, m_mixerUnit->GetNode(), busNumber);
177       if (ret)
178       {
179         CLog::Log(LOGERROR, "CCoreAudioGraph::Open: "
180           "Error connecting m_converterNode. Error = %s", GetError(ret).c_str());
181         return false;
182       }
183
184       m_inputUnit->SetBus(busNumber);
185
186       ret = AUGraphUpdate(m_audioGraph, NULL);
187       if (ret)
188       {
189         CLog::Log(LOGERROR, "CCoreAudioGraph::Open: "
190           "Error update graph. Error = %s", GetError(ret).c_str());
191         return false;
192       }
193       ret = AUGraphInitialize(m_audioGraph);
194       if (ret)
195       {
196         CLog::Log(LOGERROR, "CCoreAudioGraph::Open: "
197           "Error initialize graph. Error = %s", GetError(ret).c_str());
198         return false;
199       }
200
201       UInt32 inputNumber = m_inputUnit->GetBus();
202       int channelOffset = GetMixerChannelOffset(inputNumber);
203       if (!CCoreAudioMixMap::SetMixingMatrix(m_mixerUnit, m_mixMap, &fmt, &mixOutput, channelOffset))
204         return false;
205
206       // Regenerate audio format and copy format for the Output AU
207       outputFormat = mixOutput;
208     }
209     else
210     {
211       outputFormat = inputFormat;
212     }
213
214   }
215   else
216   {
217     outputFormat = inputFormat;
218   }
219
220   if (!m_audioUnit->SetFormat(&outputFormat, kAudioUnitScope_Input, kOutputBus))
221   {
222     CLog::Log(LOGERROR, "CCoreAudioGraph::Open: "
223       "Error setting input format on audio device. Channel count %d, set it to %d",
224       (int)outputFormat.mChannelsPerFrame, format.m_channelLayout.Count());
225     outputFormat.mChannelsPerFrame = format.m_channelLayout.Count();
226     if (!m_audioUnit->SetFormat(&outputFormat, kAudioUnitScope_Input, kOutputBus))
227       return false;
228   }
229
230   std::string formatString;
231   // asume we are in dd-wave mode
232   if (!m_inputUnit)
233   {
234     if (!m_audioUnit->SetFormat(&inputFormat, kAudioUnitScope_Output, kInputBus))
235     {
236       CLog::Log(LOGERROR, "CCoreAudioGraph::Open: "
237         "Error setting Device Output Stream Format %s",
238         StreamDescriptionToString(inputFormat, formatString));
239     }
240   }
241
242   ret = AUGraphUpdate(m_audioGraph, NULL);
243   if (ret)
244   {
245     CLog::Log(LOGERROR, "CCoreAudioGraph::Open: "
246       "Error update graph. Error = %s", GetError(ret).c_str());
247     return false;
248   }
249
250   AudioStreamBasicDescription inputDesc_end, outputDesc_end;
251   m_audioUnit->GetFormat(&inputDesc_end, kAudioUnitScope_Input, kOutputBus);
252   m_audioUnit->GetFormat(&outputDesc_end, kAudioUnitScope_Output, kInputBus);
253   CLog::Log(LOGDEBUG, "CCoreAudioGraph::Open: audioUnit, Input Stream Format  %s",
254     StreamDescriptionToString(inputDesc_end, formatString));
255   CLog::Log(LOGDEBUG, "CCoreAudioGraph::Open: audioUnit, Output Stream Format %s",
256     StreamDescriptionToString(outputDesc_end, formatString));
257
258   if (m_mixerUnit)
259   {
260     m_mixerUnit->GetFormat(&inputDesc_end, kAudioUnitScope_Input, kOutputBus);
261     m_mixerUnit->GetFormat(&outputDesc_end, kAudioUnitScope_Output, kOutputBus);
262     CLog::Log(LOGDEBUG, "CCoreAudioGraph::Open: mixerUnit, Input Stream Format  %s",
263       StreamDescriptionToString(inputDesc_end, formatString));
264     CLog::Log(LOGDEBUG, "CCoreAudioGraph::Open: mixerUnit, Output Stream Format %s",
265       StreamDescriptionToString(outputDesc_end, formatString));
266   }
267
268   if (m_inputUnit)
269   {
270     m_inputUnit->GetFormat(&inputDesc_end, kAudioUnitScope_Input, kOutputBus);
271     m_inputUnit->GetFormat(&outputDesc_end, kAudioUnitScope_Output, kOutputBus);
272     CLog::Log(LOGDEBUG, "CCoreAudioGraph::Open: inputUnit, Input Stream Format  %s",
273       StreamDescriptionToString(inputDesc_end, formatString));
274     CLog::Log(LOGDEBUG, "CCoreAudioGraph::Open: inputUnit, Output Stream Format %s",
275       StreamDescriptionToString(outputDesc_end, formatString));
276   }
277
278   ret = AUGraphInitialize(m_audioGraph);
279   if (ret)
280   {
281     CLog::Log(LOGERROR, "CCoreAudioGraph::Open: "
282       "Error initialize graph. Error = %s", GetError(ret).c_str());
283     return false;
284   }
285
286   UInt32 bufferFrames = m_audioUnit->GetBufferFrameSize();
287   if (!m_audioUnit->SetMaxFramesPerSlice(bufferFrames))
288     return false;
289
290   SetInputSource(pSource);
291
292   return Start();
293 }
294
295 bool CCoreAudioGraph::Close()
296 {
297   if (!m_audioGraph)
298     return false;
299
300   Stop();
301
302   SetInputSource(NULL);
303
304   while (!m_auUnitList.empty())
305   {
306     CAUOutputDevice *d = m_auUnitList.front();
307     m_auUnitList.pop_front();
308     ReleaseBus(d->GetBus());
309     d->SetInputSource(NULL);
310     d->Close();
311     delete d;
312   }
313
314   if (m_inputUnit)
315   {
316     ReleaseBus(m_inputUnit->GetBus());
317     m_inputUnit->Close();
318     delete m_inputUnit;
319     m_inputUnit = NULL;
320   }
321
322   if (m_mixerUnit)
323   {
324     m_mixerUnit->Close();
325     delete m_mixerUnit;
326     m_mixerUnit = NULL;
327   }
328
329   if (m_audioUnit)
330   {
331     m_audioUnit->Close();
332     delete m_audioUnit;
333     m_audioUnit = NULL;
334   }
335
336   OSStatus ret = AUGraphUninitialize(m_audioGraph);
337   if (ret)
338   {
339     CLog::Log(LOGERROR, "CCoreAudioGraph::Close: "
340       "Error unitialize. Error = %s", GetError(ret).c_str());
341   }
342
343   ret = AUGraphClose(m_audioGraph);
344   if (ret)
345   {
346     CLog::Log(LOGERROR, "CCoreAudioGraph::Close: "
347       "Error close. Error = %s", GetError(ret).c_str());
348   }
349
350   ret = DisposeAUGraph(m_audioGraph);
351   if (ret)
352   {
353     CLog::Log(LOGERROR, "CCoreAudioGraph::Close: "
354       "Error dispose. Error = %s", GetError(ret).c_str());
355   }
356
357   return true;
358 }
359
360 bool CCoreAudioGraph::Start()
361 {
362   if (!m_audioGraph)
363     return false;
364
365   Boolean isRunning = false;
366   OSStatus ret = AUGraphIsRunning(m_audioGraph, &isRunning);
367   if (ret)
368   {
369     CLog::Log(LOGERROR, "CCoreAudioGraph::Start: "
370       "Audio graph not running. Error = %s", GetError(ret).c_str());
371     return false;
372   }
373   if (!isRunning)
374   {
375     if (m_audioUnit)
376       m_audioUnit->Start();
377     if (m_mixerUnit)
378       m_mixerUnit->Start();
379     if (m_inputUnit)
380       m_inputUnit->Start();
381
382     ret = AUGraphStart(m_audioGraph);
383     if (ret)
384     {
385       CLog::Log(LOGERROR, "CCoreAudioGraph::Start: "
386         "Error starting audio graph. Error = %s", GetError(ret).c_str());
387     }
388   }
389
390   return true;
391 }
392
393 bool CCoreAudioGraph::Stop()
394 {
395   if (!m_audioGraph)
396     return false;
397
398   Boolean isRunning = false;
399   OSStatus ret = AUGraphIsRunning(m_audioGraph, &isRunning);
400   if (ret)
401   {
402     if (m_inputUnit)
403       m_inputUnit->Stop();
404     if (m_mixerUnit)
405       m_mixerUnit->Stop();
406     if (m_audioUnit)
407       m_audioUnit->Stop();
408
409     CLog::Log(LOGERROR, "CCoreAudioGraph::Stop: "
410       "Audio graph not running. Error = %s", GetError(ret).c_str());
411     return false;
412   }
413   if (isRunning)
414   {
415     ret = AUGraphStop(m_audioGraph);
416     if (ret)
417     {
418       CLog::Log(LOGERROR, "CCoreAudioGraph::Stop: "
419         "Error stopping audio graph. Error = %s", GetError(ret).c_str());
420     }
421   }
422
423   return true;
424 }
425
426 AudioChannelLayoutTag CCoreAudioGraph::GetChannelLayoutTag(int layout)
427 {
428   return g_LayoutMap[layout];
429 }
430
431 bool CCoreAudioGraph::SetInputSource(ICoreAudioSource* pSource)
432 {
433   if (m_inputUnit)
434     return m_inputUnit->SetInputSource(pSource);
435   else if (m_audioUnit)
436     return m_audioUnit->SetInputSource(pSource);
437
438   return false;
439 }
440
441 bool CCoreAudioGraph::SetCurrentVolume(Float32 vol)
442 {
443   if (!m_audioUnit)
444     return false;
445
446   return m_audioUnit->SetCurrentVolume(vol);
447 }
448
449 CAUOutputDevice *CCoreAudioGraph::DestroyUnit(CAUOutputDevice *outputUnit)
450 {
451   if (!outputUnit)
452     return NULL;
453
454   Stop();
455
456   for (AUUnitList::iterator itt = m_auUnitList.begin(); itt != m_auUnitList.end(); ++itt)
457   {
458     if (*itt == outputUnit)
459     {
460       m_auUnitList.erase(itt);
461       break;
462     }
463   }
464
465   ReleaseBus(outputUnit->GetBus());
466   outputUnit->SetInputSource(NULL);
467   outputUnit->Close();
468   delete outputUnit;
469
470   AUGraphUpdate(m_audioGraph, NULL);
471
472   Start();
473
474   return NULL;
475 }
476
477 CAUOutputDevice *CCoreAudioGraph::CreateUnit(AEAudioFormat &format)
478 {
479   if (!m_audioUnit || !m_mixerUnit)
480     return NULL;
481
482   AudioStreamBasicDescription fmt = {0};
483   AudioStreamBasicDescription inputFormat = {0};
484   AudioStreamBasicDescription outputFormat = {0};
485
486   int busNumber = GetFreeBus();
487   if (busNumber == INVALID_BUS)
488     return  NULL;
489
490   OSStatus ret;
491   // create output unit
492   CAUOutputDevice *outputUnit = new CAUOutputDevice();
493   if (!outputUnit->Open(m_audioGraph,
494     kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter, kAudioUnitManufacturer_Apple))
495     goto error;
496
497   m_audioUnit->GetFormatDesc(format, &inputFormat, &fmt);
498
499   // get the format frm the mixer
500   if (!m_mixerUnit->GetFormat(&outputFormat, kAudioUnitScope_Input, kOutputBus))
501     goto error;
502
503   if (!outputUnit->SetFormat(&inputFormat, kAudioUnitScope_Input, kOutputBus))
504     goto error;
505
506   if (!outputUnit->SetFormat(&outputFormat, kAudioUnitScope_Output, kOutputBus))
507     goto error;
508
509   ret = AUGraphConnectNodeInput(m_audioGraph, outputUnit->GetNode(), 0, m_mixerUnit->GetNode(), busNumber);
510   if (ret)
511   {
512     CLog::Log(LOGERROR, "CCoreAudioGraph::CreateUnit: "
513       "Error connecting outputUnit. Error = %s", GetError(ret).c_str());
514     goto error;
515   }
516
517   // TODO: setup mixmap, get free bus number for connection
518
519   outputUnit->SetBus(busNumber);
520
521   if (m_mixMap || m_mixMap->IsValid())
522   {
523     UInt32 inputNumber = outputUnit->GetBus();
524     int channelOffset = GetMixerChannelOffset(inputNumber);
525     CCoreAudioMixMap::SetMixingMatrix(m_mixerUnit, m_mixMap, &inputFormat, &fmt, channelOffset);
526   }
527
528   AUGraphUpdate(m_audioGraph, NULL);
529   m_auUnitList.push_back(outputUnit);
530
531   return outputUnit;
532
533 error:
534   delete outputUnit;
535   return NULL;
536 }
537
538 int CCoreAudioGraph::GetFreeBus()
539 {
540   for (int i = 0; i < MAX_CONNECTION_LIMIT; i++)
541   {
542     if (!m_reservedBusNumber[i])
543     {
544       m_reservedBusNumber[i] = true;
545       return i;
546     }
547   }
548   return INVALID_BUS;
549 }
550
551 void CCoreAudioGraph::ReleaseBus(int busNumber)
552 {
553   if (busNumber > MAX_CONNECTION_LIMIT || busNumber < 0)
554     return;
555
556   m_reservedBusNumber[busNumber] = false;
557 }
558
559 bool CCoreAudioGraph::IsBusFree(int busNumber)
560 {
561   if (busNumber > MAX_CONNECTION_LIMIT || busNumber < 0)
562     return false;
563   return m_reservedBusNumber[busNumber];
564 }
565
566 int CCoreAudioGraph::GetMixerChannelOffset(int busNumber)
567 {
568   if (!m_mixerUnit)
569     return 0;
570
571   int offset = 0;
572   AudioStreamBasicDescription fmt = {0};
573
574   for (int i = 0; i < busNumber; i++)
575   {
576     memset(&fmt, 0x0, sizeof(fmt));
577     m_mixerUnit->GetFormat(&fmt, kAudioUnitScope_Input, busNumber);
578     offset += fmt.mChannelsPerFrame;
579   }
580   return offset;
581 }