コード例 #1
0
static void
_MetadataCallback(void* aAppleATDecoder,
                  AudioFileStreamID aStream,
                  AudioFileStreamPropertyID aProperty,
                  UInt32* aFlags)
{
  AppleATDecoder* decoder = static_cast<AppleATDecoder*>(aAppleATDecoder);
  LOG("MetadataCallback receiving: '%s'", FourCC2Str(aProperty));
  if (aProperty == kAudioFileStreamProperty_MagicCookieData) {
    UInt32 size;
    Boolean writeable;
    OSStatus rv = AudioFileStreamGetPropertyInfo(aStream,
                                                 aProperty,
                                                 &size,
                                                 &writeable);
    if (rv) {
      LOG("Couldn't get property info for '%s' (%s)",
          FourCC2Str(aProperty), FourCC2Str(rv));
      decoder->mFileStreamError = true;
      return;
    }
    nsAutoArrayPtr<uint8_t> data(new uint8_t[size]);
    rv = AudioFileStreamGetProperty(aStream, aProperty,
                                    &size, data);
    if (rv) {
      LOG("Couldn't get property '%s' (%s)",
          FourCC2Str(aProperty), FourCC2Str(rv));
      decoder->mFileStreamError = true;
      return;
    }
    decoder->mMagicCookie.AppendElements(data.get(), size);
  }
}
コード例 #2
0
INT32 PORT_GetPortType(void* id, INT32 portIndex) {
    PortMixer *mixer = (PortMixer *)id;
    INT32 ret = 0;

    if (portIndex < 0 || portIndex >= mixer->portCount) {
        ERROR1("PORT_GetPortType: line (portIndex = %d) not found\n", portIndex);
        return 0;
    }

    AudioObjectPropertyScope scope = mixer->ports[portIndex].scope;
    AudioStreamID streamID = mixer->ports[portIndex].streamID;
    if (streamID != 0) {
        UInt32 terminalType;

        OSStatus err = GetAudioObjectProperty(streamID, kAudioObjectPropertyScopeGlobal,
            kAudioStreamPropertyTerminalType, sizeof(terminalType), &terminalType, 1);
        if (err) {
            OS_ERROR1(err, "PORT_GetPortType(kAudioStreamPropertyTerminalType), portIndex=%d", portIndex);
            return 0;
        }

        // Note that kAudioStreamPropertyTerminalType actually returns values from
        // IOAudioTypes.h, not the defined kAudioStreamTerminalType*.
        TRACE4("PORT_GetPortType (portIndex=%d), scope=%s, termType=0x%04x (%s)\n",
            (int)portIndex, FourCC2Str(scope), (int)terminalType, FourCC2Str(terminalType));
        switch (terminalType) {
        case INPUT_MICROPHONE:
            ret = PORT_SRC_MICROPHONE;
            break;

        case OUTPUT_SPEAKER:
            ret = PORT_DST_SPEAKER;
            break;
        case OUTPUT_HEADPHONES:
            ret = PORT_DST_HEADPHONE;
            break;

        case EXTERNAL_LINE_CONNECTOR:
            ret = scope == kAudioDevicePropertyScopeInput ? PORT_SRC_LINE_IN : PORT_DST_LINE_OUT;
            break;

        default:
            TRACE1("  unknown output terminal type %#x\n", terminalType);
        }
    } else {
        TRACE0("  PORT_GetPortType: multiple streams\n");
    }

    if (ret == 0) {
        // if the type not detected, return "common type"
        ret = scope == kAudioDevicePropertyScopeInput ? PORT_SRC_UNKNOWN : PORT_DST_UNKNOWN;
    }

    TRACE2("<<PORT_GetPortType (portIndex=%d) = %d\n", portIndex, ret);
    return ret;
}
コード例 #3
0
ファイル: AppleATDecoder.cpp プロジェクト: luke-chang/gecko-1
static void
_MetadataCallback(void* aAppleATDecoder,
                  AudioFileStreamID aStream,
                  AudioFileStreamPropertyID aProperty,
                  UInt32* aFlags)
{
  AppleATDecoder* decoder = static_cast<AppleATDecoder*>(aAppleATDecoder);
  MOZ_RELEASE_ASSERT(decoder->mTaskQueue->IsCurrentThreadIn());

  LOGEX(decoder, "MetadataCallback receiving: '%s'", FourCC2Str(aProperty));
  if (aProperty == kAudioFileStreamProperty_MagicCookieData) {
    UInt32 size;
    Boolean writeable;
    OSStatus rv = AudioFileStreamGetPropertyInfo(aStream,
                                                 aProperty,
                                                 &size,
                                                 &writeable);
    if (rv) {
      LOGEX(decoder,
            "Couldn't get property info for '%s' (%s)",
            FourCC2Str(aProperty),
            FourCC2Str(rv));
      decoder->mFileStreamError = true;
      return;
    }
    auto data = MakeUnique<uint8_t[]>(size);
    rv = AudioFileStreamGetProperty(aStream, aProperty,
                                    &size, data.get());
    if (rv) {
      LOGEX(decoder,
            "Couldn't get property '%s' (%s)",
            FourCC2Str(aProperty),
            FourCC2Str(rv));
      decoder->mFileStreamError = true;
      return;
    }
    decoder->mMagicCookie.AppendElements(data.get(), size);
  }
}
コード例 #4
0
void PORT_GetControls(void* id, INT32 portIndex, PortControlCreator* creator) {
    PortMixer *mixer = (PortMixer *)id;

    TRACE1(">>PORT_GetControls (portIndex = %d)\n", portIndex);

    if (portIndex < 0 || portIndex >= mixer->portCount) {
        ERROR1("<<PORT_GetControls: line (portIndex = %d) not found\n", portIndex);
        return;
    }

    PortLine *port = &(mixer->ports[portIndex]);

    if (mixer->deviceControlCount < 0) {    // not initialized
        OSStatus err;
        UInt32 size;
        // deviceControlCount is overestimated
        // because we don't actually filter by if the owned objects are controls
        err = GetAudioObjectPropertySize(mixer->deviceID, kAudioObjectPropertyScopeGlobal,
            kAudioObjectPropertyOwnedObjects, &size);

        if (err) {
            OS_ERROR1(err, "PORT_GetControls (portIndex = %d) get OwnedObject size", portIndex);
        } else {
            mixer->deviceControlCount = size / sizeof(AudioObjectID);
            TRACE1("  PORT_GetControls: detected %d owned objects\n", mixer->deviceControlCount);

            AudioObjectID controlIDs[mixer->deviceControlCount];

            err = GetAudioObjectProperty(mixer->deviceID, kAudioObjectPropertyScopeGlobal,
                kAudioObjectPropertyOwnedObjects, sizeof(controlIDs), controlIDs, 1);

            if (err) {
                OS_ERROR1(err, "PORT_GetControls (portIndex = %d) get OwnedObject values", portIndex);
            } else {
                mixer->deviceControls = (AudioControl *)calloc(mixer->deviceControlCount, sizeof(AudioControl));

                for (int i = 0; i < mixer->deviceControlCount; i++) {
                    AudioControl *control = &mixer->deviceControls[i];

                    control->controlID = controlIDs[i];

                    OSStatus err1 = GetAudioObjectProperty(control->controlID, kAudioObjectPropertyScopeGlobal,
                        kAudioObjectPropertyClass, sizeof(control->classID), &control->classID, 1);
                    OSStatus err2 = GetAudioObjectProperty(control->controlID, kAudioObjectPropertyScopeGlobal,
                        kAudioControlPropertyScope, sizeof(control->scope), &control->scope, 1);
                    OSStatus err3 = GetAudioObjectProperty(control->controlID, kAudioObjectPropertyScopeGlobal,
                        kAudioControlPropertyElement, sizeof(control->channel), &control->channel, 1);
                    if (err1 || err2 || err3) { // not a control or other error
                        control->classID = 0;
                        continue;
                    }

                    TRACE4("- control 0x%x, class='%s', scope='%s', channel=%d\n",
                        control->controlID, FourCC2Str(control->classID), FourCC2Str(control->scope), control->channel);
                }
            }
        }
    }

    if (mixer->deviceControlCount <= 0) {
        TRACE1("<<PORT_GetControls (portIndex = %d): no owned AudioControls\n", portIndex);
        return;
    }

    int totalChannels = GetChannelCount(mixer->deviceID, port->scope == kAudioDevicePropertyScopeOutput ? 1 : 0);

    // collect volume and mute controls
    AudioControl* volumeControls[totalChannels+1];  // 0 - for master channel
    memset(&volumeControls, 0, sizeof(AudioControl *) * (totalChannels+1));
    AudioControl* muteControls[totalChannels+1];  // 0 - for master channel
    memset(&muteControls, 0, sizeof(AudioControl *) * (totalChannels+1));

    for (int i=0; i<mixer->deviceControlCount; i++) {
        AudioControl *control = &mixer->deviceControls[i];
        if (control->classID == 0 || control->scope != port->scope || control->channel > (unsigned)totalChannels) {
            continue;
        }
        if (control->classID == kAudioVolumeControlClassID) {
            if (volumeControls[control->channel] == NULL) {
                volumeControls[control->channel] = control;
            } else {
                ERROR4("WARNING: duplicate VOLUME control 0x%x, class='%s', scope='%s', channel=%d\n",
                    control->controlID, FourCC2Str(control->classID), FourCC2Str(control->scope), control->channel);
            }
        } else if (control->classID == kAudioMuteControlClassID) {
            if (muteControls[control->channel] == NULL) {
                muteControls[control->channel] = control;
            } else {
                ERROR4("WARNING: duplicate MUTE control 0x%x, class='%s', scope='%s', channel=%d\n",
                    control->controlID, FourCC2Str(control->classID), FourCC2Str(control->scope), control->channel);
            }
        } else {
#ifdef USE_ERROR
            if (control->classID != 0) {
                ERROR4("WARNING: unhandled control 0x%x, class='%s', scope='%s', channel=%d\n",
                    control->controlID, FourCC2Str(control->classID), FourCC2Str(control->scope), control->channel);
            }
#endif
        }
    }

    ////////////////////////////////////////////////////////
    // create java control hierarchy

    void *masterVolume = NULL, *masterMute = NULL, *masterBalance = NULL;
    // volumeControls[0] and muteControls[0] - master volume/mute
    // volumeControls[n] and muteControls[n] (n=1..totalChannels) - corresponding channel controls
    if (volumeControls[0] != NULL) {    // "master volume" AudioControl
        masterVolume = CreatePortControl(mixer, creator, PortControl::Volume, volumeControls, 0, 1);
    } else {
        if (ValidControlCount(volumeControls, 1, totalChannels) == totalChannels) {
            // every channel has volume control => create virtual master volume
            masterVolume = CreatePortControl(mixer, creator, PortControl::Volume, volumeControls, 1, totalChannels);
        } else {
            TRACE2("  PORT_GetControls (master volume): totalChannels = %d, valid volume controls = %d\n",
                totalChannels, ValidControlCount(volumeControls, 1, totalChannels));
        }
    }

    if (muteControls[0] != NULL) {      // "master mute"
        masterMute = CreatePortControl(mixer, creator, PortControl::Mute, muteControls, 0, 1);
    } else {
        if (ValidControlCount(muteControls, 1, totalChannels) == totalChannels) {
            // every channel has mute control => create virtual master mute control
            masterMute = CreatePortControl(mixer, creator, PortControl::Mute, muteControls, 1, totalChannels);
        } else {
            TRACE2("  PORT_GetControls (master mute): totalChannels = %d, valid volume controls = %d\n",
                totalChannels, ValidControlCount(muteControls, 1, totalChannels));
        }
    }

    // virtual balance
    if (totalChannels == 2) {
        if (ValidControlCount(volumeControls, 1, totalChannels) == totalChannels) {
            masterBalance = CreatePortControl(mixer, creator, PortControl::Balance, volumeControls, 1, totalChannels);
        } else {
            TRACE2("  PORT_GetControls (naster balance): totalChannels = %d, valid volume controls = %d\n",
                totalChannels, ValidControlCount(volumeControls, 1, totalChannels));
        }
    }

    // add "master" controls
    if (masterVolume != NULL) {
        creator->addControl(creator, masterVolume);
    }
    if (masterBalance != NULL) {
        creator->addControl(creator, masterBalance);
    }
    if (masterMute != NULL) {
        creator->addControl(creator, masterMute);
    }

    // don't add per-channel controls for mono & stereo - they are handled by "master" controls
    // TODO: this should be reviewed to handle controls other than mute & volume
    if (totalChannels > 2) {
        // add separate compound control for each channel (containing volume and mute)
        // (ensure that we have controls)
        if (ValidControlCount(volumeControls, 1, totalChannels) > 0 || ValidControlCount(muteControls, 1, totalChannels) > 0) {
            for (int ch=1; ch<=totalChannels; ch++) {
                // get the channel name
                char *channelName;
                CFStringRef cfname = NULL;
                const AudioObjectPropertyAddress address = {kAudioObjectPropertyElementName, port->scope, ch};
                UInt32 size = sizeof(cfname);
                OSStatus err = AudioObjectGetPropertyData(mixer->deviceID, &address, 0, NULL, &size, &cfname);
                if (err == noErr) {
                    CFIndex length = CFStringGetLength(cfname) + 1;
                    channelName = (char *)malloc(length);
                    CFStringGetCString(cfname, channelName, length, kCFStringEncodingUTF8);
                    CFRelease(cfname);
                } else {
                    channelName = (char *)malloc(16);
                    sprintf(channelName, "Ch %d", ch);
                }

                void* jControls[2];
                int controlCount = 0;
                if (volumeControls[ch] != NULL) {
                    jControls[controlCount++] = CreatePortControl(mixer, creator, PortControl::Volume, volumeControls, ch, 1);
                }
                if (muteControls[ch] != NULL) {
                    jControls[controlCount++] = CreatePortControl(mixer, creator, PortControl::Mute, muteControls, ch, 1);
                }
                // TODO: add any extra controls for "other" controls for the channel

                void *compoundControl = creator->newCompoundControl(creator, channelName, jControls, controlCount);
                creator->addControl(creator, compoundControl);

                free(channelName);
            }
        }
    }

    AddChangeListeners(mixer);

    TRACE1("<<PORT_GetControls (portIndex = %d)\n", portIndex);
}
コード例 #5
0
ファイル: AviBitmap.cpp プロジェクト: killbug2004/cosps
HRESULT CAviBitmap::Init()
{
	HRESULT hr = E_FAIL;
	
	do 
	{
		//Open file
		hr = AVIFileOpen(&m_pAviFile, m_szFileName, OF_READ, NULL);
		if(hr != S_OK)
		{
			m_szLastErrorMsg.Format(_T("Unable to Open the Movie File"));
			break;
		}

		//Get video stream
		hr = AVIFileGetStream(m_pAviFile, &m_pAviStream, streamtypeVIDEO /*video stream*/, 0 /*first stream*/);
		if(hr != S_OK)
		{
			m_szLastErrorMsg.Format(_T("Unable to Get the video stream"));
			break;
		}

		hr = AVIStreamInfo(m_pAviStream, &m_aviInfo, sizeof(AVISTREAMINFO));
		if(hr != S_OK)
		{
			m_szLastErrorMsg.Format(_T("Unable to Get the video stream info"));
			break;
		}
		CString szFourCC;
		FourCC2Str(m_aviInfo.fccHandler, szFourCC);
		AfxTrace(_T("fccHandler=%s, 0x%08X\n"), szFourCC, m_aviInfo.fccHandler);

		ZeroMemory(&m_biWanted, sizeof(m_biWanted));
		LONG lFormat = sizeof(m_biWanted);

		hr = AVIStreamReadFormat(m_pAviStream, 0, &m_biWanted, &lFormat);
		if(hr != S_OK)
		{
			m_szLastErrorMsg.Format(_T("Unable to Get the foramt of the 1st frame"));
			break;
		}
		m_biWanted.biCompression = BI_RGB;
		m_biWanted.biBitCount = 32;
		m_biWanted.biSizeImage = m_biWanted.biWidth * 4 * m_biWanted.biHeight;

		//Set the result to Fail
		hr = E_FAIL;

		//Get the GETFRAME handle
		m_pGetFrame = AVIStreamGetFrameOpen(m_pAviStream, &m_biWanted);
		if(m_pGetFrame == NULL)
		{
			m_szLastErrorMsg.Format(_T("Unable to Get the GETFRAME handle"));
			break;
		}

		//Get the 1st sample
		m_lFirstSample = AVIStreamStart(m_pAviStream);
		if(m_lFirstSample == -1)
		{
			m_szLastErrorMsg.Format(_T("Unable to Get the first sample"));
			break;
		}

		//Get the total sample count
		m_lSampleCount = AVIStreamLength(m_pAviStream);
		if(m_lSampleCount == -1)
		{
			m_szLastErrorMsg.Format(_T("Unable to Get the sample count"));
			break;
		}

		//Done
		hr = S_OK;

	} while (FALSE);
	
	if(hr != S_OK)
	{
		ReleaseMemory();
	}

	return hr;
}
コード例 #6
0
ファイル: AviBitmap.cpp プロジェクト: killbug2004/cosps
HRESULT CAviBitmap::GetAllFrames(LPCTSTR lpszFolderName)
{
	if(m_pGetFrame == NULL)
	{
		m_szLastErrorMsg.Format(_T("Not initialized yet"));
		return E_FAIL;
	}
	HRESULT hr = S_OK;

	int nBmpInfoHdrSize = sizeof(BITMAPINFO) + sizeof(RGBQUAD) * 256;
	BITMAPINFOHEADER* lpBmpInfoHdr = (BITMAPINFOHEADER*)(new BYTE[nBmpInfoHdrSize]);
	LONG lpcbFormat = nBmpInfoHdrSize;

	BYTE* lpDib = NULL;
	BYTE* lpBuffer = NULL;

	LONG lBytes = 0, lSamples = 0;
	BOOL bReadRaw = FALSE;

	int nPos = 0;
	int nSampleCount = min(m_lSampleCount, 101);
	for(nPos = 0; nPos < nSampleCount; nPos++)
	{
		//Get the frame format
		hr = AVIStreamReadFormat(m_pAviStream, nPos, lpBmpInfoHdr, &lpcbFormat);
		if(hr != S_OK)
		{
			m_szLastErrorMsg.Format(_T("Unable to Get the sample format: %d"), nPos);
			break;
		}

		lpBuffer = NULL;
		//Try to read raw data when the bitmap is BI_RGB
		if(lpBmpInfoHdr->biCompression == BI_RGB && (lpBmpInfoHdr->biBitCount == 24 || lpBmpInfoHdr->biBitCount == 32))
		{
			//Get the frame data
			lpBuffer = new BYTE[m_biWanted.biSizeImage];
			hr = AVIStreamRead(m_pAviStream, nPos, 1, lpBuffer, m_biWanted.biSizeImage, &lBytes, &lSamples);
			if(hr != S_OK)
			{
				m_szLastErrorMsg.Format(_T("Unable to Get the sample data: %d"), nPos);
				break;
			}
		}
		else
		{
			CString szFourCC;
			FourCC2Str(m_aviInfo.fccHandler, szFourCC);
			AfxTrace(_T("Non-RGB format at frame(%03d)=%s, 0x%08X\n"), nPos, szFourCC, lpBmpInfoHdr->biCompression);
		}


		//Get the frame at nPos
		lpDib = (BYTE*)AVIStreamGetFrame(m_pGetFrame, nPos);
		if(lpDib == NULL)
		{
			m_szLastErrorMsg.Format(_T("Unable to Get the sample: %d"), nPos);
			hr = E_FAIL;
			break;
		}

		//compare the data retrieved in 2 ways if needed
		if(lpBuffer != NULL)
		{
			if(memcmp(lpBuffer, lpDib + sizeof(BITMAPINFOHEADER), lpBmpInfoHdr->biSizeImage) != 0)
			{
				m_szLastErrorMsg.Format(_T("not equals: %d"), nPos);
				hr = E_FAIL;
				break;
			}
		}

		CString szFileName;
		if(lpszFolderName == NULL)
		{
			szFileName.Format(_T(".\\Frame%03d.bmp"), nPos);
		}
		else
		{
			szFileName.Format(_T("%s\\Frame%03d.bmp"), lpszFolderName, nPos);
		}
		BITMAPINFOHEADER* pTemp = (BITMAPINFOHEADER*)lpDib;

//		hr = SaveBitmap(lpBmpInfoHdr, lpBuffer, lpBmpInfoHdr->biSizeImage, szFileName);
		hr = SaveBitmap(&m_biWanted, lpDib + sizeof(BITMAPINFOHEADER), m_biWanted.biSizeImage, szFileName);

		if(lpBuffer != NULL)
		{
			delete [] lpBuffer;
			lpBuffer = NULL;
		}
		//Done
	}

	if(lpBuffer != NULL)
	{
		delete [] lpBuffer;
		lpBuffer = NULL;
	}

	if(lpBmpInfoHdr != NULL)
	{
		delete [] lpBmpInfoHdr;
		lpBmpInfoHdr = NULL;
	}

	ReleaseMemory();

	return hr;
}
コード例 #7
0
// Will set mChannelLayout if a channel layout could properly be identified
// and is supported.
nsresult
AppleATDecoder::SetupChannelLayout()
{
  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());

  // Determine the channel layout.
  UInt32 propertySize;
  UInt32 size;
  OSStatus status =
    AudioConverterGetPropertyInfo(mConverter,
                                  kAudioConverterOutputChannelLayout,
                                  &propertySize, NULL);
  if (status || !propertySize) {
    LOG("Couldn't get channel layout property (%s)", FourCC2Str(status));
    return NS_ERROR_FAILURE;
  }

  auto data = MakeUnique<uint8_t[]>(propertySize);
  size = propertySize;
  status =
    AudioConverterGetProperty(mConverter, kAudioConverterInputChannelLayout,
                              &size, data.get());
  if (status || size != propertySize) {
    LOG("Couldn't get channel layout property (%s)",
        FourCC2Str(status));
    return NS_ERROR_FAILURE;
  }

  AudioChannelLayout* layout =
    reinterpret_cast<AudioChannelLayout*>(data.get());
  AudioChannelLayoutTag tag = layout->mChannelLayoutTag;

  // if tag is kAudioChannelLayoutTag_UseChannelDescriptions then the structure
  // directly contains the the channel layout mapping.
  // If tag is kAudioChannelLayoutTag_UseChannelBitmap then the layout will
  // be defined via the bitmap and can be retrieved using
  // kAudioFormatProperty_ChannelLayoutForBitmap property.
  // Otherwise the tag itself describes the layout.
  if (tag != kAudioChannelLayoutTag_UseChannelDescriptions) {
    AudioFormatPropertyID property =
      tag == kAudioChannelLayoutTag_UseChannelBitmap
        ? kAudioFormatProperty_ChannelLayoutForBitmap
        : kAudioFormatProperty_ChannelLayoutForTag;

    if (property == kAudioFormatProperty_ChannelLayoutForBitmap) {
      status =
        AudioFormatGetPropertyInfo(property,
                                   sizeof(UInt32), &layout->mChannelBitmap,
                                   &propertySize);
    } else {
      status =
        AudioFormatGetPropertyInfo(property,
                                   sizeof(AudioChannelLayoutTag), &tag,
                                   &propertySize);
    }
    if (status || !propertySize) {
      LOG("Couldn't get channel layout property info (%s:%s)",
          FourCC2Str(property), FourCC2Str(status));
      return NS_ERROR_FAILURE;
    }
    data = MakeUnique<uint8_t[]>(propertySize);
    layout = reinterpret_cast<AudioChannelLayout*>(data.get());
    size = propertySize;

    if (property == kAudioFormatProperty_ChannelLayoutForBitmap) {
      status = AudioFormatGetProperty(property,
                                      sizeof(UInt32), &layout->mChannelBitmap,
                                      &size, layout);
    } else {
      status = AudioFormatGetProperty(property,
                                      sizeof(AudioChannelLayoutTag), &tag,
                                      &size, layout);
    }
    if (status || size != propertySize) {
      LOG("Couldn't get channel layout property (%s:%s)",
          FourCC2Str(property), FourCC2Str(status));
      return NS_ERROR_FAILURE;
    }
    // We have retrieved the channel layout from the tag or bitmap.
    // We can now directly use the channel descriptions.
    layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
  }

  if (layout->mNumberChannelDescriptions > MAX_AUDIO_CHANNELS ||
      layout->mNumberChannelDescriptions != mOutputFormat.mChannelsPerFrame) {
    LOG("Nonsensical channel layout or not matching the original channel number");
    return NS_ERROR_FAILURE;
  }

  AudioConfig::Channel channels[MAX_AUDIO_CHANNELS];
  for (uint32_t i = 0; i < layout->mNumberChannelDescriptions; i++) {
    AudioChannelLabel id = layout->mChannelDescriptions[i].mChannelLabel;
    AudioConfig::Channel channel = ConvertChannelLabel(id);
    channels[i] = channel;
  }
  mChannelLayout =
    MakeUnique<AudioConfig::ChannelLayout>(mOutputFormat.mChannelsPerFrame,
                                           channels);
  return NS_OK;
}