chromium/media/base/mac/channel_layout_util_mac_unittests.cc

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "media/base/mac/channel_layout_util_mac.h"

#include <utility>

#include "media/base/channel_layout.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace media {

TEST(ChannelLayoutUtilMac, AudioChannelLabelToChannel) {
  Channels output_channel;
  EXPECT_EQ(
      AudioChannelLabelToChannel(kAudioChannelLabel_Left, &output_channel),
      true);
  EXPECT_EQ(output_channel, Channels::LEFT);
  EXPECT_EQ(
      AudioChannelLabelToChannel(kAudioChannelLabel_Right, &output_channel),
      true);
  EXPECT_EQ(output_channel, Channels::RIGHT);
  EXPECT_EQ(
      AudioChannelLabelToChannel(kAudioChannelLabel_Center, &output_channel),
      true);
  EXPECT_EQ(output_channel, Channels::CENTER);
  EXPECT_EQ(
      AudioChannelLabelToChannel(kAudioChannelLabel_Mono, &output_channel),
      true);
  EXPECT_EQ(output_channel, Channels::CENTER);

  EXPECT_EQ(
      AudioChannelLabelToChannel(kAudioChannelLabel_LFEScreen, &output_channel),
      true);
  EXPECT_EQ(output_channel, Channels::LFE);
  EXPECT_EQ(AudioChannelLabelToChannel(kAudioChannelLabel_RearSurroundLeft,
                                       &output_channel),
            true);
  EXPECT_EQ(output_channel, Channels::BACK_LEFT);
  EXPECT_EQ(AudioChannelLabelToChannel(kAudioChannelLabel_RearSurroundRight,
                                       &output_channel),
            true);
  EXPECT_EQ(output_channel, Channels::BACK_RIGHT);
  EXPECT_EQ(AudioChannelLabelToChannel(kAudioChannelLabel_LeftCenter,
                                       &output_channel),
            true);
  EXPECT_EQ(output_channel, Channels::LEFT_OF_CENTER);
  EXPECT_EQ(AudioChannelLabelToChannel(kAudioChannelLabel_RightCenter,
                                       &output_channel),
            true);
  EXPECT_EQ(output_channel, Channels::RIGHT_OF_CENTER);
  EXPECT_EQ(AudioChannelLabelToChannel(kAudioChannelLabel_CenterSurround,
                                       &output_channel),
            true);
  EXPECT_EQ(output_channel, Channels::BACK_CENTER);
  EXPECT_EQ(AudioChannelLabelToChannel(kAudioChannelLabel_LeftSurround,
                                       &output_channel),
            true);
  EXPECT_EQ(output_channel, Channels::SIDE_LEFT);
  EXPECT_EQ(AudioChannelLabelToChannel(kAudioChannelLabel_RightSurround,
                                       &output_channel),
            true);
  EXPECT_EQ(output_channel, Channels::SIDE_RIGHT);
  EXPECT_EQ(
      AudioChannelLabelToChannel(kAudioChannelLabel_LeftWide, &output_channel),
      false);
}

TEST(ChannelLayoutUtilMac, ChannelToAudioChannelLabel) {
  EXPECT_EQ(ChannelToAudioChannelLabel(Channels::LEFT),
            kAudioChannelLabel_Left);
  EXPECT_EQ(ChannelToAudioChannelLabel(Channels::RIGHT),
            kAudioChannelLabel_Right);
  EXPECT_EQ(ChannelToAudioChannelLabel(Channels::CENTER),
            kAudioChannelLabel_Center);
  EXPECT_EQ(ChannelToAudioChannelLabel(Channels::LFE),
            kAudioChannelLabel_LFEScreen);
  EXPECT_EQ(ChannelToAudioChannelLabel(Channels::BACK_LEFT),
            kAudioChannelLabel_RearSurroundLeft);
  EXPECT_EQ(ChannelToAudioChannelLabel(Channels::BACK_RIGHT),
            kAudioChannelLabel_RearSurroundRight);
  EXPECT_EQ(ChannelToAudioChannelLabel(Channels::LEFT_OF_CENTER),
            kAudioChannelLabel_LeftCenter);
  EXPECT_EQ(ChannelToAudioChannelLabel(Channels::RIGHT_OF_CENTER),
            kAudioChannelLabel_RightCenter);
  EXPECT_EQ(ChannelToAudioChannelLabel(Channels::BACK_CENTER),
            kAudioChannelLabel_CenterSurround);
  EXPECT_EQ(ChannelToAudioChannelLabel(Channels::SIDE_LEFT),
            kAudioChannelLabel_LeftSurround);
  EXPECT_EQ(ChannelToAudioChannelLabel(Channels::SIDE_RIGHT),
            kAudioChannelLabel_RightSurround);
}

TEST(ChannelLayoutUtilMac, ChannelLayoutMonoToAudioChannelLayout) {
  int channels = 1;
  auto output_layout = ChannelLayoutToAudioChannelLayout(
      ChannelLayout::CHANNEL_LAYOUT_MONO, channels);

  EXPECT_GT(output_layout->layout_size(), 0u);
  EXPECT_EQ(output_layout->layout()->mChannelLayoutTag,
            kAudioChannelLayoutTag_UseChannelDescriptions);
  EXPECT_EQ(output_layout->layout()->mNumberChannelDescriptions,
            static_cast<UInt32>(channels));
  EXPECT_EQ(output_layout->layout()->mChannelDescriptions[0].mChannelLabel,
            kAudioChannelLabel_Mono);
  EXPECT_EQ(output_layout->layout()->mChannelDescriptions[0].mChannelFlags,
            kAudioChannelFlags_AllOff);
}

TEST(ChannelLayoutUtilMac, ChannelLayoutDiscreteToAudioChannelLayout) {
  int channels = 12;
  auto output_layout = ChannelLayoutToAudioChannelLayout(
      ChannelLayout::CHANNEL_LAYOUT_DISCRETE, channels);

  EXPECT_GT(output_layout->layout_size(), 0u);
  EXPECT_EQ(output_layout->layout()->mChannelLayoutTag,
            kAudioChannelLayoutTag_UseChannelDescriptions);
  EXPECT_EQ(output_layout->layout()->mNumberChannelDescriptions,
            static_cast<UInt32>(channels));
  for (int i = 0; i < channels; i++) {
    EXPECT_EQ(output_layout->layout()->mChannelDescriptions[i].mChannelLabel,
              kAudioChannelLabel_Unknown);
    EXPECT_EQ(output_layout->layout()->mChannelDescriptions[i].mChannelFlags,
              kAudioChannelFlags_AllOff);
  }
}

TEST(ChannelLayoutUtilMac, ChannelLayout7Point1ToAudioChannelLayout) {
  int channels = 8;
  auto output_layout = ChannelLayoutToAudioChannelLayout(
      ChannelLayout::CHANNEL_LAYOUT_7_1, channels);

  EXPECT_GT(output_layout->layout_size(), 0u);
  EXPECT_EQ(output_layout->layout()->mChannelLayoutTag,
            kAudioChannelLayoutTag_UseChannelDescriptions);
  EXPECT_EQ(output_layout->layout()->mNumberChannelDescriptions,
            static_cast<UInt32>(channels));
  EXPECT_EQ(output_layout->layout()->mChannelDescriptions[0].mChannelLabel,
            kAudioChannelLabel_Left);
  EXPECT_EQ(output_layout->layout()->mChannelDescriptions[0].mChannelFlags,
            kAudioChannelFlags_AllOff);
  EXPECT_EQ(output_layout->layout()->mChannelDescriptions[1].mChannelLabel,
            kAudioChannelLabel_Right);
  EXPECT_EQ(output_layout->layout()->mChannelDescriptions[1].mChannelFlags,
            kAudioChannelFlags_AllOff);
  EXPECT_EQ(output_layout->layout()->mChannelDescriptions[2].mChannelLabel,
            kAudioChannelLabel_Center);
  EXPECT_EQ(output_layout->layout()->mChannelDescriptions[2].mChannelFlags,
            kAudioChannelFlags_AllOff);
  EXPECT_EQ(output_layout->layout()->mChannelDescriptions[3].mChannelLabel,
            kAudioChannelLabel_LFEScreen);
  EXPECT_EQ(output_layout->layout()->mChannelDescriptions[3].mChannelFlags,
            kAudioChannelFlags_AllOff);
  EXPECT_EQ(output_layout->layout()->mChannelDescriptions[4].mChannelLabel,
            kAudioChannelLabel_RearSurroundLeft);
  EXPECT_EQ(output_layout->layout()->mChannelDescriptions[4].mChannelFlags,
            kAudioChannelFlags_AllOff);
  EXPECT_EQ(output_layout->layout()->mChannelDescriptions[5].mChannelLabel,
            kAudioChannelLabel_RearSurroundRight);
  EXPECT_EQ(output_layout->layout()->mChannelDescriptions[5].mChannelFlags,
            kAudioChannelFlags_AllOff);
  EXPECT_EQ(output_layout->layout()->mChannelDescriptions[6].mChannelLabel,
            kAudioChannelLabel_LeftSurround);
  EXPECT_EQ(output_layout->layout()->mChannelDescriptions[6].mChannelFlags,
            kAudioChannelFlags_AllOff);
  EXPECT_EQ(output_layout->layout()->mChannelDescriptions[7].mChannelLabel,
            kAudioChannelLabel_RightSurround);
  EXPECT_EQ(output_layout->layout()->mChannelDescriptions[7].mChannelFlags,
            kAudioChannelFlags_AllOff);
}

TEST(ChannelLayoutUtilMac, AudioChannelLayoutWithDescriptionsToChannelLayout) {
  int channels = 6;
  int layout_size =
      offsetof(AudioChannelLayout, mChannelDescriptions[channels]);
  ScopedAudioChannelLayout input_layout(layout_size);

  input_layout.layout()->mNumberChannelDescriptions = channels;
  input_layout.layout()->mChannelLayoutTag =
      kAudioChannelLayoutTag_UseChannelDescriptions;

  input_layout.layout()->mChannelDescriptions[0].mChannelLabel =
      kAudioChannelLabel_Left;
  input_layout.layout()->mChannelDescriptions[0].mChannelFlags =
      kAudioChannelFlags_AllOff;
  input_layout.layout()->mChannelDescriptions[1].mChannelLabel =
      kAudioChannelLabel_Right;
  input_layout.layout()->mChannelDescriptions[1].mChannelFlags =
      kAudioChannelFlags_AllOff;
  input_layout.layout()->mChannelDescriptions[2].mChannelLabel =
      kAudioChannelLabel_Center;
  input_layout.layout()->mChannelDescriptions[2].mChannelFlags =
      kAudioChannelFlags_AllOff;
  input_layout.layout()->mChannelDescriptions[3].mChannelLabel =
      kAudioChannelLabel_LFEScreen;
  input_layout.layout()->mChannelDescriptions[3].mChannelFlags =
      kAudioChannelFlags_AllOff;
  input_layout.layout()->mChannelDescriptions[4].mChannelLabel =
      kAudioChannelLabel_LeftSurround;
  input_layout.layout()->mChannelDescriptions[4].mChannelFlags =
      kAudioChannelFlags_AllOff;
  input_layout.layout()->mChannelDescriptions[5].mChannelLabel =
      kAudioChannelLabel_RightSurround;
  input_layout.layout()->mChannelDescriptions[5].mChannelFlags =
      kAudioChannelFlags_AllOff;

  ChannelLayout output_layout;
  EXPECT_EQ(
      AudioChannelLayoutToChannelLayout(*input_layout.layout(), &output_layout),
      true);
  EXPECT_EQ(output_layout, ChannelLayout::CHANNEL_LAYOUT_5_1);
}

TEST(ChannelLayoutUtilMac, AudioChannelLayoutWithBitmapToChannelLayout) {
  int channels = 6;
  int layout_size = offsetof(AudioChannelLayout, mChannelDescriptions[0]);
  ScopedAudioChannelLayout input_layout(layout_size);

  input_layout.layout()->mNumberChannelDescriptions =
      static_cast<UInt32>(channels);
  input_layout.layout()->mChannelLayoutTag =
      kAudioChannelLayoutTag_UseChannelBitmap;
  input_layout.layout()->mChannelBitmap =
      kAudioChannelBit_Left | kAudioChannelBit_Right | kAudioChannelBit_Center |
      kAudioChannelBit_LFEScreen | kAudioChannelBit_LeftSurround |
      kAudioChannelBit_RightSurround;

  ChannelLayout output_layout;
  EXPECT_EQ(
      AudioChannelLayoutToChannelLayout(*input_layout.layout(), &output_layout),
      true);
  EXPECT_EQ(output_layout, CHANNEL_LAYOUT_5_1);
}

TEST(ChannelLayoutUtilMac, AudioChannelLayoutWithMonoTagToChannelLayout) {
  int layout_size = offsetof(AudioChannelLayout, mChannelDescriptions[0]);
  ScopedAudioChannelLayout input_layout(layout_size);

  input_layout.layout()->mNumberChannelDescriptions = 0;
  // C.
  input_layout.layout()->mChannelLayoutTag = kAudioChannelLayoutTag_Mono;

  ChannelLayout output_layout;
  EXPECT_EQ(
      AudioChannelLayoutToChannelLayout(*input_layout.layout(), &output_layout),
      true);
  EXPECT_EQ(output_layout, ChannelLayout::CHANNEL_LAYOUT_MONO);
}

TEST(ChannelLayoutUtilMac, AudioChannelLayoutWithStereoTagToChannelLayout) {
  int layout_size = offsetof(AudioChannelLayout, mChannelDescriptions[0]);
  ScopedAudioChannelLayout input_layout(layout_size);

  input_layout.layout()->mNumberChannelDescriptions = 0;
  // L R.
  input_layout.layout()->mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;

  ChannelLayout output_layout;
  EXPECT_EQ(
      AudioChannelLayoutToChannelLayout(*input_layout.layout(), &output_layout),
      true);
  EXPECT_EQ(output_layout, ChannelLayout::CHANNEL_LAYOUT_STEREO);
}

TEST(ChannelLayoutUtilMac,
     AudioChannelLayoutWithQuadraphonicTagToChannelLayout) {
  int layout_size = offsetof(AudioChannelLayout, mChannelDescriptions[0]);
  ScopedAudioChannelLayout input_layout(layout_size);

  input_layout.layout()->mNumberChannelDescriptions = 0;
  // L R Ls Rs.
  input_layout.layout()->mChannelLayoutTag =
      kAudioChannelLayoutTag_Quadraphonic;

  ChannelLayout output_layout;
  EXPECT_EQ(
      AudioChannelLayoutToChannelLayout(*input_layout.layout(), &output_layout),
      true);
  EXPECT_EQ(output_layout, ChannelLayout::CHANNEL_LAYOUT_2_2);
}

TEST(ChannelLayoutUtilMac, AudioChannelLayoutWith6Point1TagToChannelLayout) {
  int layout_size = offsetof(AudioChannelLayout, mChannelDescriptions[0]);
  ScopedAudioChannelLayout input_layout(layout_size);

  input_layout.layout()->mNumberChannelDescriptions = 0;
  // L R C LFE Ls Rs Cs.
  input_layout.layout()->mChannelLayoutTag = kAudioChannelLayoutTag_Logic_6_1_C;

  ChannelLayout output_layout;
  EXPECT_EQ(
      AudioChannelLayoutToChannelLayout(*input_layout.layout(), &output_layout),
      true);
  EXPECT_EQ(output_layout, ChannelLayout::CHANNEL_LAYOUT_6_1);
}

TEST(ChannelLayoutUtilMac, AudioChannelLayoutWithAAC5Point1TagToChannelLayout) {
  int layout_size = offsetof(AudioChannelLayout, mChannelDescriptions[0]);
  ScopedAudioChannelLayout input_layout(layout_size);

  input_layout.layout()->mNumberChannelDescriptions = 0;
  // C L R Ls Rs Lfe.
  input_layout.layout()->mChannelLayoutTag = kAudioChannelLayoutTag_AAC_5_1;

  ChannelLayout output_layout;
  EXPECT_EQ(
      AudioChannelLayoutToChannelLayout(*input_layout.layout(), &output_layout),
      true);
  EXPECT_EQ(output_layout, ChannelLayout::CHANNEL_LAYOUT_5_1);
}

TEST(ChannelLayoutUtilMac, AudioChannelLayoutWithAAC7Point1TagToChannelLayout) {
  int layout_size = offsetof(AudioChannelLayout, mChannelDescriptions[0]);
  ScopedAudioChannelLayout input_layout(layout_size);

  input_layout.layout()->mNumberChannelDescriptions = 0;
  // C Lc Rc L R Ls Rs Lfe.
  input_layout.layout()->mChannelLayoutTag = kAudioChannelLayoutTag_AAC_7_1;

  ChannelLayout output_layout;
  EXPECT_EQ(
      AudioChannelLayoutToChannelLayout(*input_layout.layout(), &output_layout),
      true);
  EXPECT_EQ(output_layout, ChannelLayout::CHANNEL_LAYOUT_7_1_WIDE);
}

TEST(ChannelLayoutUtilMac,
     AudioChannelLayoutWithEAC37Point1ATagToChannelLayout) {
  int layout_size = offsetof(AudioChannelLayout, mChannelDescriptions[0]);
  ScopedAudioChannelLayout input_layout(layout_size);

  input_layout.layout()->mNumberChannelDescriptions = 0;
  // L C R Ls Rs LFE Rls Rrs.
  input_layout.layout()->mChannelLayoutTag = kAudioChannelLayoutTag_EAC3_7_1_A;

  ChannelLayout output_layout;
  EXPECT_EQ(
      AudioChannelLayoutToChannelLayout(*input_layout.layout(), &output_layout),
      true);
  EXPECT_EQ(output_layout, ChannelLayout::CHANNEL_LAYOUT_7_1);
}

TEST(ChannelLayoutUtilMac,
     AudioChannelLayoutWithEAC37Point1DTagToChannelLayout) {
  int layout_size = offsetof(AudioChannelLayout, mChannelDescriptions[0]);
  ScopedAudioChannelLayout input_layout(layout_size);

  input_layout.layout()->mNumberChannelDescriptions = 0;
  // L C R Ls Rs LFE Lw Rw.
  input_layout.layout()->mChannelLayoutTag = kAudioChannelLayoutTag_EAC3_7_1_D;

  ChannelLayout output_layout;
  // Lw, Rw is not supported by Chrome for now thus the conversion fails.
  EXPECT_EQ(
      AudioChannelLayoutToChannelLayout(*input_layout.layout(), &output_layout),
      false);
}

TEST(ChannelLayoutUtilMac, ChannelLayoutConvertBackToChannelLayout) {
  for (int i = 0; i <= CHANNEL_LAYOUT_MAX; i++) {
    ChannelLayout input_layout = static_cast<ChannelLayout>(i);
    // Skip invalid channel layout.
    int input_channels = ChannelLayoutToChannelCount(input_layout);
    if (input_channels == 0) {
      continue;
    }
    // CHANNEL_LAYOUT_STEREO_DOWNMIX is the alias of CHANNEL_LAYOUT_STEREO,
    // CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC is the alias of
    // CHANNEL_LAYOUT_SURROUND, and CHANNEL_LAYOUT_5_1_4_DOWNMIX is the alias
    // of CHANNEL_LAYOUT_5_1.
    if (input_layout == CHANNEL_LAYOUT_STEREO_DOWNMIX ||
        input_layout == CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC ||
        input_layout == CHANNEL_LAYOUT_5_1_4_DOWNMIX) {
      continue;
    }
    auto intermediate_layout =
        ChannelLayoutToAudioChannelLayout(input_layout, input_channels);
    EXPECT_NE(intermediate_layout, nullptr);
    EXPECT_GT(intermediate_layout->layout_size(), 0u);
    ChannelLayout output_layout;
    EXPECT_EQ(AudioChannelLayoutToChannelLayout(*intermediate_layout->layout(),
                                                &output_layout),
              true);
    EXPECT_EQ(input_layout, output_layout);
  }
}

}  // namespace media