001/*
002 * Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.baidu.cloud.media.player.misc;
018
019import android.annotation.TargetApi;
020import android.os.Build;
021import android.text.TextUtils;
022
023import java.util.HashMap;
024import java.util.Locale;
025import java.util.Map;
026
027import com.baidu.cloud.media.player.BDCloudMediaMeta;
028
029public class BDCloudMediaFormat implements IMediaFormat {
030    // Common
031    public static final String KEY_IJK_CODEC_LONG_NAME_UI = "ijk-codec-long-name-ui";
032    public static final String KEY_IJK_CODEC_NAME_UI = "ijk-codec-name-ui";
033    public static final String KEY_IJK_BIT_RATE_UI = "ijk-bit-rate-ui";
034
035    // Video
036    public static final String KEY_IJK_CODEC_PROFILE_LEVEL_UI = "ijk-profile-level-ui";
037    public static final String KEY_IJK_CODEC_PIXEL_FORMAT_UI = "ijk-pixel-format-ui";
038    public static final String KEY_IJK_RESOLUTION_UI = "ijk-resolution-ui";
039    public static final String KEY_IJK_FRAME_RATE_UI = "ijk-frame-rate-ui";
040
041    // Audio
042    public static final String KEY_IJK_SAMPLE_RATE_UI = "ijk-sample-rate-ui";
043    public static final String KEY_IJK_CHANNEL_UI = "ijk-channel-ui";
044
045    // Codec
046    public static final String CODEC_NAME_H264 = "h264";
047
048    public final BDCloudMediaMeta.BDCloudStreamMeta mMediaFormat;
049
050    public BDCloudMediaFormat(BDCloudMediaMeta.BDCloudStreamMeta streamMeta) {
051        mMediaFormat = streamMeta;
052    }
053
054    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
055    @Override
056    public int getInteger(String name) {
057        if (mMediaFormat == null) {
058            return 0;
059        }
060
061        return mMediaFormat.getInt(name);
062    }
063
064    @Override
065    public String getString(String name) {
066        if (mMediaFormat == null) {
067            return null;
068        }
069
070        if (sFormatterMap.containsKey(name)) {
071            Formatter formatter = sFormatterMap.get(name);
072            return formatter.format(this);
073        }
074
075        return mMediaFormat.getString(name);
076    }
077
078    // -------------------------
079    // Formatter
080    // -------------------------
081
082    private abstract static class Formatter {
083        public String format(BDCloudMediaFormat mediaFormat) {
084            String value = doFormat(mediaFormat);
085            if (TextUtils.isEmpty(value)) {
086                return getDefaultString();
087            }
088            return value;
089        }
090
091        protected abstract String doFormat(BDCloudMediaFormat mediaFormat);
092
093        @SuppressWarnings("SameReturnValue")
094        protected String getDefaultString() {
095            return "N/A";
096        }
097    }
098
099    private static final Map<String, Formatter> sFormatterMap = new HashMap<String, Formatter>();
100
101    {
102        sFormatterMap.put(KEY_IJK_CODEC_LONG_NAME_UI, new Formatter() {
103            @Override
104            public String doFormat(BDCloudMediaFormat mediaFormat) {
105                return mMediaFormat.getString(BDCloudMediaMeta.IJKM_KEY_CODEC_LONG_NAME);
106            }
107        });
108        sFormatterMap.put(KEY_IJK_CODEC_NAME_UI, new Formatter() {
109            @Override
110            public String doFormat(BDCloudMediaFormat mediaFormat) {
111                return mMediaFormat.getString(BDCloudMediaMeta.IJKM_KEY_CODEC_NAME);
112            }
113        });
114        sFormatterMap.put(KEY_IJK_BIT_RATE_UI, new Formatter() {
115            @Override
116            protected String doFormat(BDCloudMediaFormat mediaFormat) {
117                int bitRate = mediaFormat.getInteger(BDCloudMediaMeta.IJKM_KEY_BITRATE);
118                if (bitRate <= 0) {
119                    return null;
120                } else if (bitRate < 1000) {
121                    return String.format(Locale.US, "%d bit/s", bitRate);
122                } else {
123                    return String.format(Locale.US, "%d kb/s", bitRate / 1000);
124                }
125            }
126        });
127        sFormatterMap.put(KEY_IJK_CODEC_PROFILE_LEVEL_UI, new Formatter() {
128            @Override
129            protected String doFormat(BDCloudMediaFormat mediaFormat) {
130                int profileIndex = mediaFormat.getInteger(BDCloudMediaMeta.IJKM_KEY_CODEC_PROFILE_ID);
131                String profile;
132                switch (profileIndex) {
133                    case BDCloudMediaMeta.FF_PROFILE_H264_BASELINE:
134                        profile = "Baseline";
135                        break;
136                    case BDCloudMediaMeta.FF_PROFILE_H264_CONSTRAINED_BASELINE:
137                        profile = "Constrained Baseline";
138                        break;
139                    case BDCloudMediaMeta.FF_PROFILE_H264_MAIN:
140                        profile = "Main";
141                        break;
142                    case BDCloudMediaMeta.FF_PROFILE_H264_EXTENDED:
143                        profile = "Extended";
144                        break;
145                    case BDCloudMediaMeta.FF_PROFILE_H264_HIGH:
146                        profile = "High";
147                        break;
148                    case BDCloudMediaMeta.FF_PROFILE_H264_HIGH_10:
149                        profile = "High 10";
150                        break;
151                    case BDCloudMediaMeta.FF_PROFILE_H264_HIGH_10_INTRA:
152                        profile = "High 10 Intra";
153                        break;
154                    case BDCloudMediaMeta.FF_PROFILE_H264_HIGH_422:
155                        profile = "High 4:2:2";
156                        break;
157                    case BDCloudMediaMeta.FF_PROFILE_H264_HIGH_422_INTRA:
158                        profile = "High 4:2:2 Intra";
159                        break;
160                    case BDCloudMediaMeta.FF_PROFILE_H264_HIGH_444:
161                        profile = "High 4:4:4";
162                        break;
163                    case BDCloudMediaMeta.FF_PROFILE_H264_HIGH_444_PREDICTIVE:
164                        profile = "High 4:4:4 Predictive";
165                        break;
166                    case BDCloudMediaMeta.FF_PROFILE_H264_HIGH_444_INTRA:
167                        profile = "High 4:4:4 Intra";
168                        break;
169                    case BDCloudMediaMeta.FF_PROFILE_H264_CAVLC_444:
170                        profile = "CAVLC 4:4:4";
171                        break;
172                    default:
173                        return null;
174                }
175
176                StringBuilder sb = new StringBuilder();
177                sb.append(profile);
178
179                String codecName = mediaFormat.getString(BDCloudMediaMeta.IJKM_KEY_CODEC_NAME);
180                if (!TextUtils.isEmpty(codecName) && codecName.equalsIgnoreCase(CODEC_NAME_H264)) {
181                    int level = mediaFormat.getInteger(BDCloudMediaMeta.IJKM_KEY_CODEC_LEVEL);
182                    if (level < 10) {
183                        return sb.toString();
184                    }
185
186                    sb.append(" Profile Level ");
187                    sb.append((level / 10) % 10);
188                    if ((level % 10) != 0) {
189                        sb.append(".");
190                        sb.append(level % 10);
191                    }
192                }
193
194                return sb.toString();
195            }
196        });
197        sFormatterMap.put(KEY_IJK_CODEC_PIXEL_FORMAT_UI, new Formatter() {
198            @Override
199            protected String doFormat(BDCloudMediaFormat mediaFormat) {
200                return mediaFormat.getString(BDCloudMediaMeta.IJKM_KEY_CODEC_PIXEL_FORMAT);
201            }
202        });
203        sFormatterMap.put(KEY_IJK_RESOLUTION_UI, new Formatter() {
204            @Override
205            protected String doFormat(BDCloudMediaFormat mediaFormat) {
206                int width = mediaFormat.getInteger(KEY_WIDTH);
207                int height = mediaFormat.getInteger(KEY_HEIGHT);
208                int sarNum = mediaFormat.getInteger(BDCloudMediaMeta.IJKM_KEY_SAR_NUM);
209                int sarDen = mediaFormat.getInteger(BDCloudMediaMeta.IJKM_KEY_SAR_DEN);
210
211                if (width <= 0 || height <= 0) {
212                    return null;
213                } else if (sarNum <= 0 || sarDen <= 0) {
214                    return String.format(Locale.US, "%d x %d", width, height);
215                } else {
216                    return String.format(Locale.US, "%d x %d [SAR %d:%d]", width, height, sarNum, sarDen);
217                }
218            }
219        });
220        sFormatterMap.put(KEY_IJK_FRAME_RATE_UI, new Formatter() {
221            @Override
222            protected String doFormat(BDCloudMediaFormat mediaFormat) {
223                int fpsNum = mediaFormat.getInteger(BDCloudMediaMeta.IJKM_KEY_FPS_NUM);
224                int fpsDen = mediaFormat.getInteger(BDCloudMediaMeta.IJKM_KEY_FPS_DEN);
225                if (fpsNum <= 0 || fpsDen <= 0) {
226                    return null;
227                } else {
228                    return String.valueOf(((float) (fpsNum)) / fpsDen);
229                }
230            }
231        });
232        sFormatterMap.put(KEY_IJK_SAMPLE_RATE_UI, new Formatter() {
233            @Override
234            protected String doFormat(BDCloudMediaFormat mediaFormat) {
235                int sampleRate = mediaFormat.getInteger(BDCloudMediaMeta.IJKM_KEY_SAMPLE_RATE);
236                if (sampleRate <= 0) {
237                    return null;
238                } else {
239                    return String.format(Locale.US, "%d Hz", sampleRate);
240                }
241            }
242        });
243        sFormatterMap.put(KEY_IJK_CHANNEL_UI, new Formatter() {
244            @Override
245            protected String doFormat(BDCloudMediaFormat mediaFormat) {
246                int channelLayout = mediaFormat.getInteger(BDCloudMediaMeta.IJKM_KEY_CHANNEL_LAYOUT);
247                if (channelLayout <= 0) {
248                    return null;
249                } else {
250                    if (channelLayout == BDCloudMediaMeta.AV_CH_LAYOUT_MONO) {
251                        return "mono";
252                    } else if (channelLayout == BDCloudMediaMeta.AV_CH_LAYOUT_STEREO) {
253                        return "stereo";
254                    } else {
255                        return String.format(Locale.US, "%x", channelLayout);
256                    }
257                }
258            }
259        });
260    }
261}