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