content: /*
   * Copyright (C) 2020 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package com.google.android.exoplayer2.audio;
  
  import androidx.annotation.Nullable;
  import com.google.android.exoplayer2.C;
  import com.google.android.exoplayer2.util.MimeTypes;
  
  /** Utility methods for handling MPEG audio streams. */
  public final class MpegAudioUtil {
  
    /** Stores the metadata for an MPEG audio frame. */
    public static final class Header {
  
      /** MPEG audio header version. */
      public int version;
      /** The mime type. */
      @Nullable public String mimeType;
      /** Size of the frame associated with this header, in bytes. */
      public int frameSize;
      /** Sample rate in samples per second. */
      public int sampleRate;
      /** Number of audio channels in the frame. */
      public int channels;
      /** Bitrate of the frame in bit/s. */
      public int bitrate;
      /** Number of samples stored in the frame. */
      public int samplesPerFrame;
  
      /**
       * Populates the fields in this instance to reflect the MPEG audio header in {@code headerData},
       * returning whether the header was valid. If false, the values of the fields in this instance
       * will not be updated.
       *
       * @param headerData Header data to parse.
       * @return True if the fields were populated. False otherwise, indicating that {@code
       *     headerData} is not a valid MPEG audio header.
       */
      public boolean setForHeaderData(int headerData) {
        if (!isMagicPresent(headerData)) {
          return false;
        }
  
        int version = (headerData >>> 19) & 3;
        if (version == 1) {
          return false;
        }
  
        int layer = (headerData >>> 17) & 3;
        if (layer == 0) {
          return false;
        }
  
        int bitrateIndex = (headerData >>> 12) & 15;
        if (bitrateIndex == 0 || bitrateIndex == 0xF) {
          // Disallow "free" bitrate.
          return false;
        }
  
        int samplingRateIndex = (headerData >>> 10) & 3;
        if (samplingRateIndex == 3) {
          return false;
        }
  
        this.version = version;
        mimeType = MIME_TYPE_BY_LAYER[3 - layer];
        sampleRate = SAMPLING_RATE_V1[samplingRateIndex];
        if (version == 2) {
          // Version 2
          sampleRate /= 2;
        } else if (version == 0) {
          // Version 2.5
          sampleRate /= 4;
        }
        int padding = (headerData >>> 9) & 1;
        samplesPerFrame = getFrameSizeInSamples(version, layer);
        if (layer == 3) {
          // Layer I (layer == 3)
          bitrate = version == 3 ? BITRATE_V1_L1[bitrateIndex - 1] : BITRATE_V2_L1[bitrateIndex - 1];
          frameSize = (12 * bitrate / sampleRate + padding) * 4;
        } else {
          // Layer II (layer == 2) or III (layer == 1)
          if (version == 3) {
            // Version 1
            bitrate = layer == 2 ? BITRATE_V1_L2[bitrateIndex - 1] : BITRATE_V1_L3[bitrateIndex - 1];
            frameSize = 144 * bitrate / sampleRate + padding;
          } else {
            // Version 2 or 2.5.
            bitrate = BITRATE_V2[bitrateIndex - 1];
            frameSize = (layer == 1 ? 72 : 144) * bitrate / sampleRate + padding;
          }
        }
        channels = ((headerData >> 6) & 3) == 3 ? 1 : 2;
        return true;
      }
    }
  
    /**
     * Returns the size of the frame associated with {@code header}, or {@link C#LENGTH_UNSET} if it
     * is invalid.
     */
    public static int getFrameSize(int headerData) {
      if (!isMagicPresent(headerData)) {
        return C.LENGTH_UNSET;
      }
  
      int version = (headerData >>> 19) & 3;
      if (version == 1) {
        return C.LENGTH_UNSET;
      }
  
      int layer = (headerData >>> 17) & 3;
      if (layer == 0) {
        return C.LENGTH_UNSET;
      }
  
      int bitrateIndex = (headerData >>> 12) & 15;
      if (bitrateIndex == 0 || bitrateIndex == 0xF) {
        // Disallow "free" bitrate.
        return C.LENGTH_UNSET;
      }
  
      int samplingRateIndex = (headerData >>> 10) & 3;
      if (samplingRateIndex == 3) {
        return C.LENGTH_UNSET;
      }
  
      int samplingRate = SAMPLING_RATE_V1[samplingRateIndex];
      if (version == 2) {
        // Version 2
        samplingRate /= 2;
      } else if (version == 0) {
        // Version 2.5
        samplingRate /= 4;
      }
  
      int bitrate;
      int padding = (headerData >>> 9) & 1;
      if (layer == 3) {
        // Layer I (layer == 3)
        bitrate = version == 3 ? BITRATE_V1_L1[bitrateIndex - 1] : BITRATE_V2_L1[bitrateIndex - 1];
        return (12 * bitrate / samplingRate + padding) * 4;
      } else {
        // Layer II (layer == 2) or III (layer == 1)
        if (version == 3) {
          bitrate = layer == 2 ? BITRATE_V1_L2[bitrateIndex - 1] : BITRATE_V1_L3[bitrateIndex - 1];
        } else {
          // Version 2 or 2.5.
          bitrate = BITRATE_V2[bitrateIndex - 1];
        }
      }
  
      if (version == 3) {
        // Version 1
        return 144 * bitrate / samplingRate + padding;
      } else {
        // Version 2 or 2.5
        return (layer == 1 ? 72 : 144) * bitrate / samplingRate + padding;
      }
    }
  
    /**
     * Returns the number of samples per frame associated with {@code headerData}, or {@link
     * C#LENGTH_UNSET} if it is invalid.
     */
    public static int parseMpegAudioFrameSampleCount(int headerData) {
      if (!isMagicPresent(headerData)) {
        return C.LENGTH_UNSET;
      }
  
      int version = (headerData >>> 19) & 3;
      if (version == 1) {
        return C.LENGTH_UNSET;
      }
  
      int layer = (headerData >>> 17) & 3;
      if (layer == 0) {
        return C.LENGTH_UNSET;
      }
  
      // Those header values are not used but are checked for consistency with the other methods
      int bitrateIndex = (headerData >>> 12) & 15;
      int samplingRateIndex = (headerData >>> 10) & 3;
      if (bitrateIndex == 0 || bitrateIndex == 0xF || samplingRateIndex == 3) {
        return C.LENGTH_UNSET;
      }
  
      return getFrameSizeInSamples(version, layer);
    }
  
    /**
     * Theoretical maximum frame size for an MPEG audio stream, which occurs when playing a Layer 2
     * MPEG 2.5 audio stream at 16 kb/s (with padding). The size is 1152 sample/frame * 160000 bit/s /
     * (8000 sample/s * 8 bit/byte) + 1 padding byte/frame = 2881 byte/frame. The next power of two
     * size is 4 KiB.
     */
    public static final int MAX_FRAME_SIZE_BYTES = 4096;
  
    /**
     * Maximum rate for an MPEG audio stream corresponding to MPEG-1 layer III (320 kbit/s), in bytes
     * per second.
     */
    public static final int MAX_RATE_BYTES_PER_SECOND = 320 * 1000 / 8;
  
    private static final String[] MIME_TYPE_BY_LAYER =
        new String[] {MimeTypes.AUDIO_MPEG_L1, MimeTypes.AUDIO_MPEG_L2, MimeTypes.AUDIO_MPEG};
    private static final int[] SAMPLING_RATE_V1 = {44100, 48000, 32000};
    private static final int[] BITRATE_V1_L1 = {
      32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000,
      416000, 448000
    };
    private static final int[] BITRATE_V2_L1 = {
      32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000,
      224000, 256000
    };
    private static final int[] BITRATE_V1_L2 = {
      32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000,
      320000, 384000
    };
    private static final int[] BITRATE_V1_L3 = {
      32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000,
      320000
    };
    private static final int[] BITRATE_V2 = {
      8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000,
      160000
    };
  
    private static final int SAMPLES_PER_FRAME_L1 = 384;
    private static final int SAMPLES_PER_FRAME_L2 = 1152;
    private static final int SAMPLES_PER_FRAME_L3_V1 = 1152;
    private static final int SAMPLES_PER_FRAME_L3_V2 = 576;
  
    private MpegAudioUtil() {}
  
    private static boolean isMagicPresent(int headerData) {
      return (headerData & 0xFFE00000) == 0xFFE00000;
    }
  
    private static int getFrameSizeInSamples(int version, int layer) {
      switch (layer) {
        case 1:
          return version == 3 ? SAMPLES_PER_FRAME_L3_V1 : SAMPLES_PER_FRAME_L3_V2; // Layer III
        case 2:
          return SAMPLES_PER_FRAME_L2; // Layer II
        case 3:
          return SAMPLES_PER_FRAME_L1; // Layer I
        default:
          throw new IllegalArgumentException();
      }
    }
  }
  