001package com.baidu.cloud.media.player;
002
003import android.os.Bundle;
004import android.text.TextUtils;
005
006import java.util.ArrayList;
007import java.util.Locale;
008
009@SuppressWarnings("SameParameterValue")
010public class BDCloudMediaMeta {
011    // media meta
012    public static final String IJKM_KEY_FORMAT = "format";
013    public static final String IJKM_KEY_DURATION_US = "duration_us";
014    public static final String IJKM_KEY_START_US = "start_us";
015    public static final String IJKM_KEY_BITRATE = "bitrate";
016    public static final String IJKM_KEY_VIDEO_STREAM = "video";
017    public static final String IJKM_KEY_AUDIO_STREAM = "audio";
018
019    // stream meta
020    public static final String IJKM_KEY_TYPE = "type";
021    public static final String IJKM_VAL_TYPE__VIDEO = "video";
022    public static final String IJKM_VAL_TYPE__AUDIO = "audio";
023    public static final String IJKM_VAL_TYPE__TIMEDTEXT = "timedtext";
024    public static final String IJKM_VAL_TYPE__UNKNOWN = "unknown";
025    public static final String IJKM_KEY_LANGUAGE = "language";
026
027    public static final String IJKM_KEY_CODEC_NAME = "codec_name";
028    public static final String IJKM_KEY_CODEC_PROFILE = "codec_profile";
029    public static final String IJKM_KEY_CODEC_LEVEL = "codec_level";
030    public static final String IJKM_KEY_CODEC_LONG_NAME = "codec_long_name";
031    public static final String IJKM_KEY_CODEC_PIXEL_FORMAT = "codec_pixel_format";
032    public static final String IJKM_KEY_CODEC_PROFILE_ID = "codec_profile_id";
033
034    // stream: video
035    public static final String IJKM_KEY_WIDTH = "width";
036    public static final String IJKM_KEY_HEIGHT = "height";
037    public static final String IJKM_KEY_FPS_NUM = "fps_num";
038    public static final String IJKM_KEY_FPS_DEN = "fps_den";
039    public static final String IJKM_KEY_TBR_NUM = "tbr_num";
040    public static final String IJKM_KEY_TBR_DEN = "tbr_den";
041    public static final String IJKM_KEY_SAR_NUM = "sar_num";
042    public static final String IJKM_KEY_SAR_DEN = "sar_den";
043    // stream: audio
044    public static final String IJKM_KEY_SAMPLE_RATE = "sample_rate";
045    public static final String IJKM_KEY_CHANNEL_LAYOUT = "channel_layout";
046
047    public static final String IJKM_KEY_STREAMS = "streams";
048
049    public static final long AV_CH_FRONT_LEFT = 0x00000001;
050    public static final long AV_CH_FRONT_RIGHT = 0x00000002;
051    public static final long AV_CH_FRONT_CENTER = 0x00000004;
052    public static final long AV_CH_LOW_FREQUENCY = 0x00000008;
053    public static final long AV_CH_BACK_LEFT = 0x00000010;
054    public static final long AV_CH_BACK_RIGHT = 0x00000020;
055    public static final long AV_CH_FRONT_LEFT_OF_CENTER = 0x00000040;
056    public static final long AV_CH_FRONT_RIGHT_OF_CENTER = 0x00000080;
057    public static final long AV_CH_BACK_CENTER = 0x00000100;
058    public static final long AV_CH_SIDE_LEFT = 0x00000200;
059    public static final long AV_CH_SIDE_RIGHT = 0x00000400;
060    public static final long AV_CH_TOP_CENTER = 0x00000800;
061    public static final long AV_CH_TOP_FRONT_LEFT = 0x00001000;
062    public static final long AV_CH_TOP_FRONT_CENTER = 0x00002000;
063    public static final long AV_CH_TOP_FRONT_RIGHT = 0x00004000;
064    public static final long AV_CH_TOP_BACK_LEFT = 0x00008000;
065    public static final long AV_CH_TOP_BACK_CENTER = 0x00010000;
066    public static final long AV_CH_TOP_BACK_RIGHT = 0x00020000;
067    public static final long AV_CH_STEREO_LEFT = 0x20000000;
068    public static final long AV_CH_STEREO_RIGHT = 0x40000000;
069    public static final long AV_CH_WIDE_LEFT = 0x0000000080000000L;
070    public static final long AV_CH_WIDE_RIGHT = 0x0000000100000000L;
071    public static final long AV_CH_SURROUND_DIRECT_LEFT = 0x0000000200000000L;
072    public static final long AV_CH_SURROUND_DIRECT_RIGHT = 0x0000000400000000L;
073    public static final long AV_CH_LOW_FREQUENCY_2 = 0x0000000800000000L;
074
075    public static final long AV_CH_LAYOUT_MONO = (AV_CH_FRONT_CENTER);
076    public static final long AV_CH_LAYOUT_STEREO = (AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT);
077    public static final long AV_CH_LAYOUT_2POINT1 = (AV_CH_LAYOUT_STEREO | AV_CH_LOW_FREQUENCY);
078    public static final long AV_CH_LAYOUT_2_1 = (AV_CH_LAYOUT_STEREO | AV_CH_BACK_CENTER);
079    public static final long AV_CH_LAYOUT_SURROUND = (AV_CH_LAYOUT_STEREO | AV_CH_FRONT_CENTER);
080    public static final long AV_CH_LAYOUT_3POINT1 = (AV_CH_LAYOUT_SURROUND | AV_CH_LOW_FREQUENCY);
081    public static final long AV_CH_LAYOUT_4POINT0 = (AV_CH_LAYOUT_SURROUND | AV_CH_BACK_CENTER);
082    public static final long AV_CH_LAYOUT_4POINT1 = (AV_CH_LAYOUT_4POINT0 | AV_CH_LOW_FREQUENCY);
083    public static final long AV_CH_LAYOUT_2_2 = (AV_CH_LAYOUT_STEREO | AV_CH_SIDE_LEFT | AV_CH_SIDE_RIGHT);
084    public static final long AV_CH_LAYOUT_QUAD = (AV_CH_LAYOUT_STEREO | AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT);
085    public static final long AV_CH_LAYOUT_5POINT0 = (AV_CH_LAYOUT_SURROUND | AV_CH_SIDE_LEFT | AV_CH_SIDE_RIGHT);
086    public static final long AV_CH_LAYOUT_5POINT1 = (AV_CH_LAYOUT_5POINT0 | AV_CH_LOW_FREQUENCY);
087    public static final long AV_CH_LAYOUT_5POINT0_BACK = (AV_CH_LAYOUT_SURROUND | AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT);
088    public static final long AV_CH_LAYOUT_5POINT1_BACK = (AV_CH_LAYOUT_5POINT0_BACK | AV_CH_LOW_FREQUENCY);
089    public static final long AV_CH_LAYOUT_6POINT0 = (AV_CH_LAYOUT_5POINT0 | AV_CH_BACK_CENTER);
090    public static final long AV_CH_LAYOUT_6POINT0_FRONT = (AV_CH_LAYOUT_2_2 | AV_CH_FRONT_LEFT_OF_CENTER
091            | AV_CH_FRONT_RIGHT_OF_CENTER);
092    public static final long AV_CH_LAYOUT_HEXAGONAL = (AV_CH_LAYOUT_5POINT0_BACK | AV_CH_BACK_CENTER);
093    public static final long AV_CH_LAYOUT_6POINT1 = (AV_CH_LAYOUT_5POINT1 | AV_CH_BACK_CENTER);
094    public static final long AV_CH_LAYOUT_6POINT1_BACK = (AV_CH_LAYOUT_5POINT1_BACK | AV_CH_BACK_CENTER);
095    public static final long AV_CH_LAYOUT_6POINT1_FRONT = (AV_CH_LAYOUT_6POINT0_FRONT | AV_CH_LOW_FREQUENCY);
096    public static final long AV_CH_LAYOUT_7POINT0 = (AV_CH_LAYOUT_5POINT0 | AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT);
097    public static final long AV_CH_LAYOUT_7POINT0_FRONT = (AV_CH_LAYOUT_5POINT0 | AV_CH_FRONT_LEFT_OF_CENTER
098            | AV_CH_FRONT_RIGHT_OF_CENTER);
099    public static final long AV_CH_LAYOUT_7POINT1 = (AV_CH_LAYOUT_5POINT1 | AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT);
100    public static final long AV_CH_LAYOUT_7POINT1_WIDE = (AV_CH_LAYOUT_5POINT1 | AV_CH_FRONT_LEFT_OF_CENTER
101            | AV_CH_FRONT_RIGHT_OF_CENTER);
102    public static final long AV_CH_LAYOUT_7POINT1_WIDE_BACK = (AV_CH_LAYOUT_5POINT1_BACK | AV_CH_FRONT_LEFT_OF_CENTER
103            | AV_CH_FRONT_RIGHT_OF_CENTER);
104    public static final long AV_CH_LAYOUT_OCTAGONAL = (AV_CH_LAYOUT_5POINT0 | AV_CH_BACK_LEFT | AV_CH_BACK_CENTER
105            | AV_CH_BACK_RIGHT);
106    public static final long AV_CH_LAYOUT_STEREO_DOWNMIX = (AV_CH_STEREO_LEFT | AV_CH_STEREO_RIGHT);
107
108    public static final int FF_PROFILE_H264_CONSTRAINED = (1 << 9); // 8+1;
109                                                                    // constraint_set1_flag
110    public static final int FF_PROFILE_H264_INTRA = (1 << 11); // 8+3;
111                                                               // constraint_set3_flag
112
113    public static final int FF_PROFILE_H264_BASELINE = 66;
114    public static final int FF_PROFILE_H264_CONSTRAINED_BASELINE = (66 | FF_PROFILE_H264_CONSTRAINED);
115    public static final int FF_PROFILE_H264_MAIN = 77;
116    public static final int FF_PROFILE_H264_EXTENDED = 88;
117    public static final int FF_PROFILE_H264_HIGH = 100;
118    public static final int FF_PROFILE_H264_HIGH_10 = 110;
119    public static final int FF_PROFILE_H264_HIGH_10_INTRA = (110 | FF_PROFILE_H264_INTRA);
120    public static final int FF_PROFILE_H264_HIGH_422 = 122;
121    public static final int FF_PROFILE_H264_HIGH_422_INTRA = (122 | FF_PROFILE_H264_INTRA);
122    public static final int FF_PROFILE_H264_HIGH_444 = 144;
123    public static final int FF_PROFILE_H264_HIGH_444_PREDICTIVE = 244;
124    public static final int FF_PROFILE_H264_HIGH_444_INTRA = (244 | FF_PROFILE_H264_INTRA);
125    public static final int FF_PROFILE_H264_CAVLC_444 = 44;
126
127    public Bundle mMediaMeta;
128
129    public String mFormat;
130    public long mDurationUS;
131    public long mStartUS;
132    public long mBitrate;
133
134    public final ArrayList<BDCloudStreamMeta> mStreams = new ArrayList<BDCloudStreamMeta>();
135    public BDCloudStreamMeta mVideoStream;
136    public BDCloudStreamMeta mAudioStream;
137
138    public String getString(String key) {
139        return mMediaMeta.getString(key);
140    }
141
142    public int getInt(String key) {
143        return getInt(key, 0);
144    }
145
146    public int getInt(String key, int defaultValue) {
147        String value = getString(key);
148        if (TextUtils.isEmpty(value)) {
149            return defaultValue;
150        }
151
152        try {
153            return Integer.parseInt(value);
154        } catch (NumberFormatException e) {
155            return defaultValue;
156        }
157    }
158
159    public long getLong(String key) {
160        return getLong(key, 0);
161    }
162
163    public long getLong(String key, long defaultValue) {
164        String value = getString(key);
165        if (TextUtils.isEmpty(value)) {
166            return defaultValue;
167        }
168
169        try {
170            return Long.parseLong(value);
171        } catch (NumberFormatException e) {
172            return defaultValue;
173        }
174    }
175
176    public ArrayList<Bundle> getParcelableArrayList(String key) {
177        return mMediaMeta.getParcelableArrayList(key);
178    }
179
180    public String getDurationInline() {
181        long duration = mDurationUS + 5000;
182        long secs = duration / 1000000;
183        long mins = secs / 60;
184        secs %= 60;
185        long hours = mins / 60;
186        mins %= 60;
187        return String.format(Locale.US, "%02d:%02d:%02d", hours, mins, secs);
188    }
189
190    public static BDCloudMediaMeta parse(Bundle mediaMeta) {
191        if (mediaMeta == null) {
192            return null;
193        }
194
195        BDCloudMediaMeta meta = new BDCloudMediaMeta();
196        meta.mMediaMeta = mediaMeta;
197
198        meta.mFormat = meta.getString(IJKM_KEY_FORMAT);
199        meta.mDurationUS = meta.getLong(IJKM_KEY_DURATION_US);
200        meta.mStartUS = meta.getLong(IJKM_KEY_START_US);
201        meta.mBitrate = meta.getLong(IJKM_KEY_BITRATE);
202
203        int videoStreamIndex = meta.getInt(IJKM_KEY_VIDEO_STREAM, -1);
204        int audioStreamIndex = meta.getInt(IJKM_KEY_AUDIO_STREAM, -1);
205
206        ArrayList<Bundle> streams = meta.getParcelableArrayList(IJKM_KEY_STREAMS);
207        if (streams == null) {
208            return meta;
209        }
210
211        int index = -1;
212        for (Bundle streamBundle : streams) {
213            index++;
214
215            if (streamBundle == null) {
216                continue;
217            }
218
219            BDCloudStreamMeta streamMeta = new BDCloudStreamMeta(index);
220            streamMeta.mMeta = streamBundle;
221            streamMeta.mType = streamMeta.getString(IJKM_KEY_TYPE);
222            streamMeta.mLanguage = streamMeta.getString(IJKM_KEY_LANGUAGE);
223            if (TextUtils.isEmpty(streamMeta.mType)) {
224                continue;
225            }
226
227            streamMeta.mCodecName = streamMeta.getString(IJKM_KEY_CODEC_NAME);
228            streamMeta.mCodecProfile = streamMeta.getString(IJKM_KEY_CODEC_PROFILE);
229            streamMeta.mCodecLongName = streamMeta.getString(IJKM_KEY_CODEC_LONG_NAME);
230            streamMeta.mBitrate = streamMeta.getInt(IJKM_KEY_BITRATE);
231
232            if (streamMeta.mType.equalsIgnoreCase(IJKM_VAL_TYPE__VIDEO)) {
233                streamMeta.mWidth = streamMeta.getInt(IJKM_KEY_WIDTH);
234                streamMeta.mHeight = streamMeta.getInt(IJKM_KEY_HEIGHT);
235                streamMeta.mFpsNum = streamMeta.getInt(IJKM_KEY_FPS_NUM);
236                streamMeta.mFpsDen = streamMeta.getInt(IJKM_KEY_FPS_DEN);
237                streamMeta.mTbrNum = streamMeta.getInt(IJKM_KEY_TBR_NUM);
238                streamMeta.mTbrDen = streamMeta.getInt(IJKM_KEY_TBR_DEN);
239                streamMeta.mSarNum = streamMeta.getInt(IJKM_KEY_SAR_NUM);
240                streamMeta.mSarDen = streamMeta.getInt(IJKM_KEY_SAR_DEN);
241
242                if (videoStreamIndex == index) {
243                    meta.mVideoStream = streamMeta;
244                }
245            } else if (streamMeta.mType.equalsIgnoreCase(IJKM_VAL_TYPE__AUDIO)) {
246                streamMeta.mSampleRate = streamMeta.getInt(IJKM_KEY_SAMPLE_RATE);
247                streamMeta.mChannelLayout = streamMeta.getLong(IJKM_KEY_CHANNEL_LAYOUT);
248
249                if (audioStreamIndex == index) {
250                    meta.mAudioStream = streamMeta;
251                }
252            }
253            meta.mStreams.add(streamMeta);
254        }
255
256        return meta;
257    }
258
259    public static class BDCloudStreamMeta {
260        public Bundle mMeta;
261
262        public final int mIndex;
263        public String mType;
264        public String mLanguage;
265
266        // common
267        public String mCodecName;
268        public String mCodecProfile;
269        public String mCodecLongName;
270        public long mBitrate;
271
272        // video
273        public int mWidth;
274        public int mHeight;
275        public int mFpsNum;
276        public int mFpsDen;
277        public int mTbrNum;
278        public int mTbrDen;
279        public int mSarNum;
280        public int mSarDen;
281
282        // audio
283        public int mSampleRate;
284        public long mChannelLayout;
285
286        public BDCloudStreamMeta(int index) {
287            mIndex = index;
288        }
289
290        public String getString(String key) {
291            return mMeta.getString(key);
292        }
293
294        public int getInt(String key) {
295            return getInt(key, 0);
296        }
297
298        public int getInt(String key, int defaultValue) {
299            String value = getString(key);
300            if (TextUtils.isEmpty(value)) {
301                return defaultValue;
302            }
303
304            try {
305                return Integer.parseInt(value);
306            } catch (NumberFormatException e) {
307                return defaultValue;
308            }
309        }
310
311        public long getLong(String key) {
312            return getLong(key, 0);
313        }
314
315        public long getLong(String key, long defaultValue) {
316            String value = getString(key);
317            if (TextUtils.isEmpty(value)) {
318                return defaultValue;
319            }
320
321            try {
322                return Long.parseLong(value);
323            } catch (NumberFormatException e) {
324                return defaultValue;
325            }
326        }
327
328        public String getCodecLongNameInline() {
329            if (!TextUtils.isEmpty(mCodecLongName)) {
330                return mCodecLongName;
331            } else if (!TextUtils.isEmpty(mCodecName)) {
332                return mCodecName;
333            } else {
334                return "N/A";
335            }
336        }
337
338        public String getCodecShortNameInline() {
339            if (!TextUtils.isEmpty(mCodecName)) {
340                return mCodecName;
341            } else {
342                return "N/A";
343            }
344        }
345
346        public String getResolutionInline() {
347            if (mWidth <= 0 || mHeight <= 0) {
348                return "N/A";
349            } else if (mSarNum <= 0 || mSarDen <= 0) {
350                return String.format(Locale.US, "%d x %d", mWidth, mHeight);
351            } else {
352                return String.format(Locale.US, "%d x %d [SAR %d:%d]", mWidth, mHeight, mSarNum, mSarDen);
353            }
354        }
355
356        public String getFpsInline() {
357            if (mFpsNum <= 0 || mFpsDen <= 0) {
358                return "N/A";
359            } else {
360                return String.valueOf(((float) (mFpsNum)) / mFpsDen);
361            }
362        }
363
364        public String getBitrateInline() {
365            if (mBitrate <= 0) {
366                return "N/A";
367            } else if (mBitrate < 1000) {
368                return String.format(Locale.US, "%d bit/s", mBitrate);
369            } else {
370                return String.format(Locale.US, "%d kb/s", mBitrate / 1000);
371            }
372        }
373
374        public String getSampleRateInline() {
375            if (mSampleRate <= 0) {
376                return "N/A";
377            } else {
378                return String.format(Locale.US, "%d Hz", mSampleRate);
379            }
380        }
381
382        public String getChannelLayoutInline() {
383            if (mChannelLayout <= 0) {
384                return "N/A";
385            } else {
386                if (mChannelLayout == AV_CH_LAYOUT_MONO) {
387                    return "mono";
388                } else if (mChannelLayout == AV_CH_LAYOUT_STEREO) {
389                    return "stereo";
390                } else {
391                    return String.format(Locale.US, "%x", mChannelLayout);
392                }
393            }
394        }
395    }
396}