content: package tv.danmaku.ijk.media.player;
  
  import android.os.Bundle;
  import android.text.TextUtils;
  
  import java.util.ArrayList;
  import java.util.Locale;
  
  @SuppressWarnings("SameParameterValue")
  public class IjkMediaMeta {
      // media meta
      public static final String IJKM_KEY_FORMAT = "format";
      public static final String IJKM_KEY_DURATION_US = "duration_us";
      public static final String IJKM_KEY_START_US = "start_us";
      public static final String IJKM_KEY_BITRATE = "bitrate";
      public static final String IJKM_KEY_VIDEO_STREAM = "video";
      public static final String IJKM_KEY_AUDIO_STREAM = "audio";
      public static final String IJKM_KEY_TIMEDTEXT_STREAM = "timedtext";
  
      // stream meta
      public static final String IJKM_KEY_TYPE = "type";
      public static final String IJKM_VAL_TYPE__VIDEO = "video";
      public static final String IJKM_VAL_TYPE__AUDIO = "audio";
      public static final String IJKM_VAL_TYPE__TIMEDTEXT = "timedtext";
      public static final String IJKM_VAL_TYPE__UNKNOWN = "unknown";
      public static final String IJKM_KEY_LANGUAGE = "language";
  
      public static final String IJKM_KEY_CODEC_NAME = "codec_name";
      public static final String IJKM_KEY_CODEC_PROFILE = "codec_profile";
      public static final String IJKM_KEY_CODEC_LEVEL = "codec_level";
      public static final String IJKM_KEY_CODEC_LONG_NAME = "codec_long_name";
      public static final String IJKM_KEY_CODEC_PIXEL_FORMAT = "codec_pixel_format";
      public static final String IJKM_KEY_CODEC_PROFILE_ID = "codec_profile_id";
  
      // stream: video
      public static final String IJKM_KEY_WIDTH = "width";
      public static final String IJKM_KEY_HEIGHT = "height";
      public static final String IJKM_KEY_FPS_NUM = "fps_num";
      public static final String IJKM_KEY_FPS_DEN = "fps_den";
      public static final String IJKM_KEY_TBR_NUM = "tbr_num";
      public static final String IJKM_KEY_TBR_DEN = "tbr_den";
      public static final String IJKM_KEY_SAR_NUM = "sar_num";
      public static final String IJKM_KEY_SAR_DEN = "sar_den";
      // stream: audio
      public static final String IJKM_KEY_SAMPLE_RATE = "sample_rate";
      public static final String IJKM_KEY_CHANNEL_LAYOUT = "channel_layout";
  
      public static final String IJKM_KEY_STREAMS = "streams";
  
      public static final long AV_CH_FRONT_LEFT = 0x00000001;
      public static final long AV_CH_FRONT_RIGHT = 0x00000002;
      public static final long AV_CH_FRONT_CENTER = 0x00000004;
      public static final long AV_CH_LOW_FREQUENCY = 0x00000008;
      public static final long AV_CH_BACK_LEFT = 0x00000010;
      public static final long AV_CH_BACK_RIGHT = 0x00000020;
      public static final long AV_CH_FRONT_LEFT_OF_CENTER = 0x00000040;
      public static final long AV_CH_FRONT_RIGHT_OF_CENTER = 0x00000080;
      public static final long AV_CH_BACK_CENTER = 0x00000100;
      public static final long AV_CH_SIDE_LEFT = 0x00000200;
      public static final long AV_CH_SIDE_RIGHT = 0x00000400;
      public static final long AV_CH_TOP_CENTER = 0x00000800;
      public static final long AV_CH_TOP_FRONT_LEFT = 0x00001000;
      public static final long AV_CH_TOP_FRONT_CENTER = 0x00002000;
      public static final long AV_CH_TOP_FRONT_RIGHT = 0x00004000;
      public static final long AV_CH_TOP_BACK_LEFT = 0x00008000;
      public static final long AV_CH_TOP_BACK_CENTER = 0x00010000;
      public static final long AV_CH_TOP_BACK_RIGHT = 0x00020000;
      public static final long AV_CH_STEREO_LEFT = 0x20000000;
      public static final long AV_CH_STEREO_RIGHT = 0x40000000;
      public static final long AV_CH_WIDE_LEFT = 0x0000000080000000L;
      public static final long AV_CH_WIDE_RIGHT = 0x0000000100000000L;
      public static final long AV_CH_SURROUND_DIRECT_LEFT = 0x0000000200000000L;
      public static final long AV_CH_SURROUND_DIRECT_RIGHT = 0x0000000400000000L;
      public static final long AV_CH_LOW_FREQUENCY_2 = 0x0000000800000000L;
  
      public static final long AV_CH_LAYOUT_MONO = (AV_CH_FRONT_CENTER);
      public static final long AV_CH_LAYOUT_STEREO = (AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT);
      public static final long AV_CH_LAYOUT_2POINT1 = (AV_CH_LAYOUT_STEREO | AV_CH_LOW_FREQUENCY);
      public static final long AV_CH_LAYOUT_2_1 = (AV_CH_LAYOUT_STEREO | AV_CH_BACK_CENTER);
      public static final long AV_CH_LAYOUT_SURROUND = (AV_CH_LAYOUT_STEREO | AV_CH_FRONT_CENTER);
      public static final long AV_CH_LAYOUT_3POINT1 = (AV_CH_LAYOUT_SURROUND | AV_CH_LOW_FREQUENCY);
      public static final long AV_CH_LAYOUT_4POINT0 = (AV_CH_LAYOUT_SURROUND | AV_CH_BACK_CENTER);
      public static final long AV_CH_LAYOUT_4POINT1 = (AV_CH_LAYOUT_4POINT0 | AV_CH_LOW_FREQUENCY);
      public static final long AV_CH_LAYOUT_2_2 = (AV_CH_LAYOUT_STEREO
              | AV_CH_SIDE_LEFT | AV_CH_SIDE_RIGHT);
      public static final long AV_CH_LAYOUT_QUAD = (AV_CH_LAYOUT_STEREO
              | AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT);
      public static final long AV_CH_LAYOUT_5POINT0 = (AV_CH_LAYOUT_SURROUND
              | AV_CH_SIDE_LEFT | AV_CH_SIDE_RIGHT);
      public static final long AV_CH_LAYOUT_5POINT1 = (AV_CH_LAYOUT_5POINT0 | AV_CH_LOW_FREQUENCY);
      public static final long AV_CH_LAYOUT_5POINT0_BACK = (AV_CH_LAYOUT_SURROUND
              | AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT);
      public static final long AV_CH_LAYOUT_5POINT1_BACK = (AV_CH_LAYOUT_5POINT0_BACK | AV_CH_LOW_FREQUENCY);
      public static final long AV_CH_LAYOUT_6POINT0 = (AV_CH_LAYOUT_5POINT0 | AV_CH_BACK_CENTER);
      public static final long AV_CH_LAYOUT_6POINT0_FRONT = (AV_CH_LAYOUT_2_2
              | AV_CH_FRONT_LEFT_OF_CENTER | AV_CH_FRONT_RIGHT_OF_CENTER);
      public static final long AV_CH_LAYOUT_HEXAGONAL = (AV_CH_LAYOUT_5POINT0_BACK | AV_CH_BACK_CENTER);
      public static final long AV_CH_LAYOUT_6POINT1 = (AV_CH_LAYOUT_5POINT1 | AV_CH_BACK_CENTER);
      public static final long AV_CH_LAYOUT_6POINT1_BACK = (AV_CH_LAYOUT_5POINT1_BACK | AV_CH_BACK_CENTER);
      public static final long AV_CH_LAYOUT_6POINT1_FRONT = (AV_CH_LAYOUT_6POINT0_FRONT | AV_CH_LOW_FREQUENCY);
      public static final long AV_CH_LAYOUT_7POINT0 = (AV_CH_LAYOUT_5POINT0
              | AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT);
      public static final long AV_CH_LAYOUT_7POINT0_FRONT = (AV_CH_LAYOUT_5POINT0
              | AV_CH_FRONT_LEFT_OF_CENTER | AV_CH_FRONT_RIGHT_OF_CENTER);
      public static final long AV_CH_LAYOUT_7POINT1 = (AV_CH_LAYOUT_5POINT1
              | AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT);
      public static final long AV_CH_LAYOUT_7POINT1_WIDE = (AV_CH_LAYOUT_5POINT1
              | AV_CH_FRONT_LEFT_OF_CENTER | AV_CH_FRONT_RIGHT_OF_CENTER);
      public static final long AV_CH_LAYOUT_7POINT1_WIDE_BACK = (AV_CH_LAYOUT_5POINT1_BACK
              | AV_CH_FRONT_LEFT_OF_CENTER | AV_CH_FRONT_RIGHT_OF_CENTER);
      public static final long AV_CH_LAYOUT_OCTAGONAL = (AV_CH_LAYOUT_5POINT0
              | AV_CH_BACK_LEFT | AV_CH_BACK_CENTER | AV_CH_BACK_RIGHT);
      public static final long AV_CH_LAYOUT_STEREO_DOWNMIX = (AV_CH_STEREO_LEFT | AV_CH_STEREO_RIGHT);
  
      public static final int FF_PROFILE_H264_CONSTRAINED = (1<<9);  // 8+1; constraint_set1_flag
      public static final int FF_PROFILE_H264_INTRA = (1<<11);       // 8+3; constraint_set3_flag
  
      public static final int FF_PROFILE_H264_BASELINE = 66;
      public static final int FF_PROFILE_H264_CONSTRAINED_BASELINE = (66|FF_PROFILE_H264_CONSTRAINED);
      public static final int FF_PROFILE_H264_MAIN = 77;
      public static final int FF_PROFILE_H264_EXTENDED = 88;
      public static final int FF_PROFILE_H264_HIGH = 100;
      public static final int FF_PROFILE_H264_HIGH_10 = 110;
      public static final int FF_PROFILE_H264_HIGH_10_INTRA = (110|FF_PROFILE_H264_INTRA);
      public static final int FF_PROFILE_H264_HIGH_422 = 122;
      public static final int FF_PROFILE_H264_HIGH_422_INTRA = (122|FF_PROFILE_H264_INTRA);
      public static final int FF_PROFILE_H264_HIGH_444 = 144;
      public static final int FF_PROFILE_H264_HIGH_444_PREDICTIVE = 244;
      public static final int FF_PROFILE_H264_HIGH_444_INTRA = (244|FF_PROFILE_H264_INTRA);
      public static final int FF_PROFILE_H264_CAVLC_444 = 44;
  
      public Bundle mMediaMeta;
  
      public String mFormat;
      public long mDurationUS;
      public long mStartUS;
      public long mBitrate;
  
      public final ArrayList<IjkStreamMeta> mStreams = new ArrayList<IjkStreamMeta>();
      public IjkStreamMeta mVideoStream;
      public IjkStreamMeta mAudioStream;
  
      public String getString(String key) {
          return mMediaMeta.getString(key);
      }
  
      public int getInt(String key) {
          return getInt(key, 0);
      }
  
      public int getInt(String key, int defaultValue) {
          String value = getString(key);
          if (TextUtils.isEmpty(value))
              return defaultValue;
  
          try {
              return Integer.parseInt(value);
          } catch (NumberFormatException e) {
              return defaultValue;
          }
      }
  
      public long getLong(String key) {
          return getLong(key, 0);
      }
  
      public long getLong(String key, long defaultValue) {
          String value = getString(key);
          if (TextUtils.isEmpty(value))
              return defaultValue;
  
          try {
              return Long.parseLong(value);
          } catch (NumberFormatException e) {
              return defaultValue;
          }
      }
  
      public ArrayList<Bundle> getParcelableArrayList(String key) {
          return mMediaMeta.getParcelableArrayList(key);
      }
  
      public String getDurationInline() {
          long duration = mDurationUS + 5000;
          long secs = duration / 1000000;
          long mins = secs / 60;
          secs %= 60;
          long hours = mins / 60;
          mins %= 60;
          return String.format(Locale.US, "%02d:%02d:%02d", hours, mins, secs);
      }
  
      public static IjkMediaMeta parse(Bundle mediaMeta) {
          if (mediaMeta == null)
              return null;
  
          IjkMediaMeta meta = new IjkMediaMeta();
          meta.mMediaMeta = mediaMeta;
  
          meta.mFormat = meta.getString(IJKM_KEY_FORMAT);
          meta.mDurationUS = meta.getLong(IJKM_KEY_DURATION_US);
          meta.mStartUS = meta.getLong(IJKM_KEY_START_US);
          meta.mBitrate = meta.getLong(IJKM_KEY_BITRATE);
  
          int videoStreamIndex = meta.getInt(IJKM_KEY_VIDEO_STREAM, -1);
          int audioStreamIndex = meta.getInt(IJKM_KEY_AUDIO_STREAM, -1);
          int subtitleStreamIndex = meta.getInt(IJKM_KEY_TIMEDTEXT_STREAM, -1);
  
          ArrayList<Bundle> streams = meta
                  .getParcelableArrayList(IJKM_KEY_STREAMS);
          if (streams == null)
              return meta;
  
          int index = -1;
          for (Bundle streamBundle : streams) {
              index++;
  
              if (streamBundle == null) {
                  continue;
              }
  
              IjkStreamMeta streamMeta = new IjkStreamMeta(index);
              streamMeta.mMeta = streamBundle;
              streamMeta.mType = streamMeta.getString(IJKM_KEY_TYPE);
              streamMeta.mLanguage = streamMeta.getString(IJKM_KEY_LANGUAGE);
              if (TextUtils.isEmpty(streamMeta.mType))
                  continue;
  
              streamMeta.mCodecName = streamMeta.getString(IJKM_KEY_CODEC_NAME);
              streamMeta.mCodecProfile = streamMeta
                      .getString(IJKM_KEY_CODEC_PROFILE);
              streamMeta.mCodecLongName = streamMeta
                      .getString(IJKM_KEY_CODEC_LONG_NAME);
              streamMeta.mBitrate = streamMeta.getInt(IJKM_KEY_BITRATE);
  
              if (streamMeta.mType.equalsIgnoreCase(IJKM_VAL_TYPE__VIDEO)) {
                  streamMeta.mWidth = streamMeta.getInt(IJKM_KEY_WIDTH);
                  streamMeta.mHeight = streamMeta.getInt(IJKM_KEY_HEIGHT);
                  streamMeta.mFpsNum = streamMeta.getInt(IJKM_KEY_FPS_NUM);
                  streamMeta.mFpsDen = streamMeta.getInt(IJKM_KEY_FPS_DEN);
                  streamMeta.mTbrNum = streamMeta.getInt(IJKM_KEY_TBR_NUM);
                  streamMeta.mTbrDen = streamMeta.getInt(IJKM_KEY_TBR_DEN);
                  streamMeta.mSarNum = streamMeta.getInt(IJKM_KEY_SAR_NUM);
                  streamMeta.mSarDen = streamMeta.getInt(IJKM_KEY_SAR_DEN);
  
                  if (videoStreamIndex == index) {
                      meta.mVideoStream = streamMeta;
                  }
              } else if (streamMeta.mType.equalsIgnoreCase(IJKM_VAL_TYPE__AUDIO)) {
                  streamMeta.mSampleRate = streamMeta
                          .getInt(IJKM_KEY_SAMPLE_RATE);
                  streamMeta.mChannelLayout = streamMeta
                          .getLong(IJKM_KEY_CHANNEL_LAYOUT);
  
                  if (audioStreamIndex == index) {
                      meta.mAudioStream = streamMeta;
                  }
              }
              meta.mStreams.add(streamMeta);
          }
  
          return meta;
      }
  
      public static class IjkStreamMeta {
          public Bundle mMeta;
  
          public final int mIndex;
          public String mType;
          public String mLanguage;
  
          // common
          public String mCodecName;
          public String mCodecProfile;
          public String mCodecLongName;
          public long mBitrate;
  
          // video
          public int mWidth;
          public int mHeight;
          public int mFpsNum;
          public int mFpsDen;
          public int mTbrNum;
          public int mTbrDen;
          public int mSarNum;
          public int mSarDen;
  
          // audio
          public int mSampleRate;
          public long mChannelLayout;
  
          public IjkStreamMeta(int index) {
              mIndex = index;
          }
  
          public String getString(String key) {
              return mMeta.getString(key);
          }
  
          public int getInt(String key) {
              return getInt(key, 0);
          }
  
          public int getInt(String key, int defaultValue) {
              String value = getString(key);
              if (TextUtils.isEmpty(value))
                  return defaultValue;
  
              try {
                  return Integer.parseInt(value);
              } catch (NumberFormatException e) {
                  return defaultValue;
              }
          }
  
          public long getLong(String key) {
              return getLong(key, 0);
          }
  
          public long getLong(String key, long defaultValue) {
              String value = getString(key);
              if (TextUtils.isEmpty(value))
                  return defaultValue;
  
              try {
                  return Long.parseLong(value);
              } catch (NumberFormatException e) {
                  return defaultValue;
              }
          }
  
          public String getCodecLongNameInline() {
              if (!TextUtils.isEmpty(mCodecLongName)) {
                  return mCodecLongName;
              } else if (!TextUtils.isEmpty(mCodecName)) {
                  return mCodecName;
              } else {
                  return "N/A";
              }
          }
  
          public String getCodecShortNameInline() {
              if (!TextUtils.isEmpty(mCodecName)) {
                  return mCodecName;
              } else {
                  return "N/A";
              }
          }
  
          public String getResolutionInline() {
              if (mWidth <= 0 || mHeight <= 0) {
                  return "N/A";
              } else if (mSarNum <= 0 || mSarDen <= 0) {
                  return String.format(Locale.US, "%d x %d", mWidth, mHeight);
              } else {
                  return String.format(Locale.US, "%d x %d [SAR %d:%d]", mWidth,
                          mHeight, mSarNum, mSarDen);
              }
          }
  
          public String getFpsInline() {
              if (mFpsNum <= 0 || mFpsDen <= 0) {
                  return "N/A";
              } else {
                  return String.valueOf(((float) (mFpsNum)) / mFpsDen);
              }
          }
  
          public String getBitrateInline() {
              if (mBitrate <= 0) {
                  return "N/A";
              } else if (mBitrate < 1000) {
                  return String.format(Locale.US, "%d bit/s", mBitrate);
              } else {
                  return String.format(Locale.US, "%d kb/s", mBitrate / 1000);
              }
          }
  
          public String getSampleRateInline() {
              if (mSampleRate <= 0) {
                  return "N/A";
              } else {
                  return String.format(Locale.US, "%d Hz", mSampleRate);
              }
          }
  
          public String getChannelLayoutInline() {
              if (mChannelLayout <= 0) {
                  return "N/A";
              } else {
                  if (mChannelLayout == AV_CH_LAYOUT_MONO) {
                      return "mono";
                  } else if (mChannelLayout == AV_CH_LAYOUT_STEREO) {
                      return "stereo";
                  } else {
                      return String.format(Locale.US, "%x", mChannelLayout);
                  }
              }
          }
      }
  }
  