static inline gboolean
_audio_stream_is_spdif_avail (AudioStreamID stream_id)
{
  AudioStreamRangedDescription *formats;
  gint i, nformats = 0;
  gboolean res = FALSE;

  formats = _audio_stream_get_formats (stream_id, &nformats);
  GST_DEBUG ("found %d stream formats", nformats);

  if (formats) {
    GST_DEBUG ("formats supported on stream ID: %u", (unsigned) stream_id);

    for (i = 0; i < nformats; i++) {
      GST_DEBUG ("  " CORE_AUDIO_FORMAT,
          CORE_AUDIO_FORMAT_ARGS (formats[i].mFormat));

      if (CORE_AUDIO_FORMAT_IS_SPDIF (formats[i])) {
        res = TRUE;
      }
    }
    g_free (formats);
  }

  return res;
}
static inline gboolean
_acquire_spdif (GstCoreAudio * core_audio, AudioStreamBasicDescription format)
{
  AudioStreamID *streams = NULL;
  gint i, j, nstreams = 0;
  gboolean ret = FALSE;

  if (!_open_spdif (core_audio))
    goto done;

  streams = _audio_device_get_streams (core_audio->device_id, &nstreams);

  for (i = 0; i < nstreams; i++) {
    AudioStreamRangedDescription *formats = NULL;
    gint nformats = 0;

    formats = _audio_stream_get_formats (streams[i], &nformats);

    if (formats) {
      gboolean is_spdif = FALSE;

      /* Check if one of the supported formats is a digital format */
      for (j = 0; j < nformats; j++) {
        if (CORE_AUDIO_FORMAT_IS_SPDIF (formats[j])) {
          is_spdif = TRUE;
          break;
        }
      }

      if (is_spdif) {
        /* if this stream supports a digital (cac3) format,
         * then go set it. */
        gint requested_rate_format = -1;
        gint current_rate_format = -1;
        gint backup_rate_format = -1;

        core_audio->stream_id = streams[i];
        core_audio->stream_idx = i;

        if (!core_audio->revert_format) {
          if (!_audio_stream_get_current_format (core_audio->stream_id,
                  &core_audio->original_format)) {
            GST_WARNING_OBJECT (core_audio->osxbuf,
                "format could not be saved");
            g_free (formats);
            continue;
          }
          core_audio->revert_format = TRUE;
        }

        for (j = 0; j < nformats; j++) {
          if (CORE_AUDIO_FORMAT_IS_SPDIF (formats[j])) {
            GST_LOG_OBJECT (core_audio->osxbuf,
                "found stream format: " CORE_AUDIO_FORMAT,
                CORE_AUDIO_FORMAT_ARGS (formats[j].mFormat));

            if (formats[j].mFormat.mSampleRate == format.mSampleRate) {
              requested_rate_format = j;
              break;
            } else if (formats[j].mFormat.mSampleRate ==
                core_audio->original_format.mSampleRate) {
              current_rate_format = j;
            } else {
              if (backup_rate_format < 0 ||
                  formats[j].mFormat.mSampleRate >
                  formats[backup_rate_format].mFormat.mSampleRate) {
                backup_rate_format = j;
              }
            }
          }
        }

        if (requested_rate_format >= 0) {
          /* We prefer to output at the rate of the original audio */
          core_audio->stream_format = formats[requested_rate_format].mFormat;
        } else if (current_rate_format >= 0) {
          /* If not possible, we will try to use the current rate */
          core_audio->stream_format = formats[current_rate_format].mFormat;
        } else {
          /* And if we have to, any digital format will be just
           * fine (highest rate possible) */
          core_audio->stream_format = formats[backup_rate_format].mFormat;
        }
      }
      g_free (formats);
    }
  }
  g_free (streams);

  GST_DEBUG_OBJECT (core_audio,
      "original stream format: " CORE_AUDIO_FORMAT,
      CORE_AUDIO_FORMAT_ARGS (core_audio->original_format));

  if (!_audio_stream_change_format (core_audio->stream_id,
          core_audio->stream_format))
    goto done;

  ret = TRUE;

done:
  return ret;
}
static gboolean
_audio_stream_change_format (AudioStreamID stream_id,
    AudioStreamBasicDescription format)
{
  OSStatus status = noErr;
  gint i;
  gboolean ret = FALSE;
  AudioStreamBasicDescription cformat;
  PropertyMutex prop_mutex;

  AudioObjectPropertyAddress formatAddress = {
    kAudioStreamPropertyPhysicalFormat,
    kAudioObjectPropertyScopeGlobal,
    kAudioObjectPropertyElementMaster
  };

  GST_DEBUG ("setting stream format: " CORE_AUDIO_FORMAT,
      CORE_AUDIO_FORMAT_ARGS (format));

  /* Condition because SetProperty is asynchronous */
  g_mutex_init (&prop_mutex.lock);
  g_cond_init (&prop_mutex.cond);

  g_mutex_lock (&prop_mutex.lock);

  /* Install the property listener to serialize the operations */
  status = AudioObjectAddPropertyListener (stream_id, &formatAddress,
      _audio_stream_format_listener, (void *) &prop_mutex);
  if (status != noErr) {
    GST_ERROR ("AudioObjectAddPropertyListener failed: %d", (int) status);
    goto done;
  }

  /* Change the format */
  if (!_audio_stream_set_current_format (stream_id, format)) {
    goto done;
  }

  /* The AudioObjectSetProperty is not only asynchronous
   * it is also not atomic in its behaviour.
   * Therefore we check 4 times before we really give up. */
  for (i = 0; i < 4; i++) {
    GTimeVal timeout;

    g_get_current_time (&timeout);
    g_time_val_add (&timeout, 250000);

    if (!g_cond_wait_until (&prop_mutex.cond, &prop_mutex.lock, timeout.tv_sec)) {
      GST_LOG ("timeout...");
    }

    if (_audio_stream_get_current_format (stream_id, &cformat)) {
      GST_DEBUG ("current stream format: " CORE_AUDIO_FORMAT,
          CORE_AUDIO_FORMAT_ARGS (cformat));

      if (cformat.mSampleRate == format.mSampleRate &&
          cformat.mFormatID == format.mFormatID &&
          cformat.mFramesPerPacket == format.mFramesPerPacket) {
        /* The right format is now active */
        break;
      }
    }
  }

  if (cformat.mSampleRate != format.mSampleRate ||
      cformat.mFormatID != format.mFormatID ||
      cformat.mFramesPerPacket != format.mFramesPerPacket) {
    goto done;
  }

  ret = TRUE;

done:
  /* Removing the property listener */
  status = AudioObjectRemovePropertyListener (stream_id,
      &formatAddress, _audio_stream_format_listener, (void *) &prop_mutex);
  if (status != noErr) {
    GST_ERROR ("AudioObjectRemovePropertyListener failed: %d", (int) status);
  }
  /* Destroy the lock and condition */
  g_mutex_unlock (&prop_mutex.lock);
  g_mutex_clear (&prop_mutex.lock);
  g_cond_clear (&prop_mutex.cond);

  return ret;
}
static gboolean
gst_osx_audio_ring_buffer_acquire (GstAudioRingBuffer * buf,
    GstAudioRingBufferSpec * spec)
{
  gboolean ret = FALSE, is_passthrough = FALSE;
  GstOsxAudioRingBuffer *osxbuf;
  AudioStreamBasicDescription format;

  osxbuf = GST_OSX_AUDIO_RING_BUFFER (buf);

  if (RINGBUFFER_IS_SPDIF (spec->type)) {
    format.mFormatID = kAudioFormat60958AC3;
    format.mSampleRate = (double) GST_AUDIO_INFO_RATE (&spec->info);
    format.mChannelsPerFrame = 2;
    format.mFormatFlags = kAudioFormatFlagIsSignedInteger |
        kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonMixable;
    format.mBytesPerFrame = 0;
    format.mBitsPerChannel = 16;
    format.mBytesPerPacket = 6144;
    format.mFramesPerPacket = 1536;
    format.mReserved = 0;
    spec->segsize = 6144;
    spec->segtotal = 10;
    is_passthrough = TRUE;
  } else {
    int width, depth;
    /* Fill out the audio description we're going to be using */
    format.mFormatID = kAudioFormatLinearPCM;
    format.mSampleRate = (double) GST_AUDIO_INFO_RATE (&spec->info);
    format.mChannelsPerFrame = GST_AUDIO_INFO_CHANNELS (&spec->info);
    if (GST_AUDIO_INFO_IS_FLOAT (&spec->info)) {
      format.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
      width = depth = GST_AUDIO_INFO_WIDTH (&spec->info);
    } else {
      format.mFormatFlags = kAudioFormatFlagIsSignedInteger;
      width = GST_AUDIO_INFO_WIDTH (&spec->info);
      depth = GST_AUDIO_INFO_DEPTH (&spec->info);
      if (width == depth) {
        format.mFormatFlags |= kAudioFormatFlagIsPacked;
      } else {
        format.mFormatFlags |= kAudioFormatFlagIsAlignedHigh;
      }
    }

    if (GST_AUDIO_INFO_IS_BIG_ENDIAN (&spec->info)) {
      format.mFormatFlags |= kAudioFormatFlagIsBigEndian;
    }

    format.mBytesPerFrame = GST_AUDIO_INFO_BPF (&spec->info);
    format.mBitsPerChannel = depth;
    format.mBytesPerPacket = GST_AUDIO_INFO_BPF (&spec->info);
    format.mFramesPerPacket = 1;
    format.mReserved = 0;
    spec->segsize =
        (spec->latency_time * GST_AUDIO_INFO_RATE (&spec->info) /
        G_USEC_PER_SEC) * GST_AUDIO_INFO_BPF (&spec->info);
    spec->segtotal = spec->buffer_time / spec->latency_time;
    is_passthrough = FALSE;
  }

  GST_DEBUG_OBJECT (osxbuf, "Format: " CORE_AUDIO_FORMAT,
      CORE_AUDIO_FORMAT_ARGS (format));

  /* gst_audio_ring_buffer_set_channel_positions is not called
   * since the AUs perform channel reordering themselves.
   * (see gst_core_audio_set_channel_layout) */

  buf->size = spec->segtotal * spec->segsize;
  buf->memory = g_malloc0 (buf->size);

  ret = gst_core_audio_initialize (osxbuf->core_audio, format, spec->caps,
      is_passthrough);

  if (!ret) {
    g_free (buf->memory);
    buf->memory = NULL;
    buf->size = 0;
  }

  osxbuf->segoffset = 0;

  return ret;
}