• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.mediav2.common.cts;
18 
19 import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface;
20 import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
21 import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar;
22 import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar;
23 import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUVP010;
24 
25 import android.media.AudioFormat;
26 import android.media.MediaFormat;
27 
28 import androidx.annotation.NonNull;
29 
30 /**
31  * Class to hold encoder configuration settings.
32  */
33 public class EncoderConfigParams {
34     public static final String TOKEN_SEPARATOR = "<>";
35 
36     public final boolean mIsAudio;
37     public final String mMediaType;
38 
39     // video params
40     public final int mWidth;
41     public final int mHeight;
42     public final int mFrameRate;
43     public final float mKeyFrameInterval;
44     public final int mMaxBFrames;
45     public final int mBitRateMode;
46     public final int mLevel;
47     public final int mColorFormat;
48     public final int mInputBitDepth;
49     public final int mRange;
50     public final int mStandard;
51     public final int mTransfer;
52 
53     // audio params
54     public final int mSampleRate;
55     public final int mChannelCount;
56     public final int mCompressionLevel;
57     public final int mPcmEncoding;
58 
59     // common params
60     public final int mProfile;
61     public final int mBitRate;
62 
63     Builder mBuilder;
64     MediaFormat mFormat;
65     StringBuilder mMsg;
66 
EncoderConfigParams(Builder cfg)67     private EncoderConfigParams(Builder cfg) {
68         if (cfg.mMediaType == null) {
69             throw new IllegalArgumentException("null media type");
70         }
71         mIsAudio = cfg.mMediaType.startsWith("audio/");
72         boolean mIsVideo = cfg.mMediaType.startsWith("video/");
73         if (mIsAudio == mIsVideo) {
74             throw new IllegalArgumentException("invalid media type, it is neither audio nor video");
75         }
76         mMediaType = cfg.mMediaType;
77         if (mIsAudio) {
78             if (cfg.mSampleRate <= 0 || cfg.mChannelCount <= 0) {
79                 throw new IllegalArgumentException("bad config params for audio component");
80             }
81             mSampleRate = cfg.mSampleRate;
82             mChannelCount = cfg.mChannelCount;
83             if (mMediaType.equals(MediaFormat.MIMETYPE_AUDIO_FLAC)) {
84                 if (cfg.mCompressionLevel < 0 || cfg.mCompressionLevel > 8) {
85                     throw new IllegalArgumentException("bad compression level for flac component");
86                 }
87                 mCompressionLevel = cfg.mCompressionLevel;
88                 mBitRate = -1;
89             } else {
90                 if (cfg.mBitRate <= 0) {
91                     throw new IllegalArgumentException("bad bitrate value for audio component");
92                 }
93                 mBitRate = cfg.mBitRate;
94                 mCompressionLevel = -1;
95             }
96             if (cfg.mPcmEncoding != AudioFormat.ENCODING_PCM_FLOAT
97                     && cfg.mPcmEncoding != AudioFormat.ENCODING_PCM_16BIT) {
98                 throw new IllegalArgumentException("bad input pcm encoding for audio component");
99             }
100             if (cfg.mInputBitDepth != -1) {
101                 throw new IllegalArgumentException(
102                         "use pcm encoding to signal input attributes, don't use bitdepth");
103             }
104             mPcmEncoding = cfg.mPcmEncoding;
105             mProfile = cfg.mProfile;
106 
107             // satisfy Variable '*' might not have been initialized, unused by this media type
108             mWidth = 352;
109             mHeight = 288;
110             mFrameRate = -1;
111             mBitRateMode = -1;
112             mKeyFrameInterval = 1.0f;
113             mMaxBFrames = 0;
114             mLevel = -1;
115             mColorFormat = COLOR_FormatYUV420Flexible;
116             mInputBitDepth = -1;
117             mRange = -1;
118             mStandard = -1;
119             mTransfer = -1;
120         } else {
121             if (cfg.mWidth <= 0 || cfg.mHeight <= 0) {
122                 throw new IllegalArgumentException("bad config params for video component");
123             }
124             mWidth = cfg.mWidth;
125             mHeight = cfg.mHeight;
126             if (cfg.mFrameRate <= 0) {
127                 if (mMediaType.equals(MediaFormat.MIMETYPE_VIDEO_MPEG4)) {
128                     mFrameRate = 12;
129                 } else if (mMediaType.equals(MediaFormat.MIMETYPE_VIDEO_H263)) {
130                     mFrameRate = 12;
131                 } else {
132                     mFrameRate = 30;
133                 }
134             } else {
135                 mFrameRate = cfg.mFrameRate;
136             }
137             if (cfg.mBitRate <= 0) {
138                 throw new IllegalArgumentException("bad bitrate value for video component");
139             }
140             mBitRate = cfg.mBitRate;
141             mKeyFrameInterval = cfg.mKeyFrameInterval;
142             mMaxBFrames = cfg.mMaxBFrames;
143             mBitRateMode = cfg.mBitRateMode;
144             mProfile = cfg.mProfile;
145             mLevel = cfg.mLevel;
146             if (cfg.mColorFormat != COLOR_FormatYUV420Flexible
147                     && cfg.mColorFormat != COLOR_FormatYUVP010
148                     && cfg.mColorFormat != COLOR_FormatSurface
149                     && cfg.mColorFormat != COLOR_FormatYUV420SemiPlanar
150                     && cfg.mColorFormat != COLOR_FormatYUV420Planar) {
151                 throw new IllegalArgumentException("bad color format config for video component");
152             }
153             mColorFormat = cfg.mColorFormat;
154             if (cfg.mInputBitDepth != -1) {
155                 if (cfg.mColorFormat == COLOR_FormatYUV420Flexible && cfg.mInputBitDepth != 8) {
156                     throw new IllegalArgumentException(
157                             "bad bit depth configuration for COLOR_FormatYUV420Flexible");
158                 } else if (cfg.mColorFormat == COLOR_FormatYUV420SemiPlanar
159                         && cfg.mInputBitDepth != 8) {
160                     throw new IllegalArgumentException(
161                             "bad bit depth configuration for COLOR_FormatYUV420SemiPlanar");
162                 } else if (cfg.mColorFormat == COLOR_FormatYUV420Planar
163                         && cfg.mInputBitDepth != 8) {
164                     throw new IllegalArgumentException(
165                             "bad bit depth configuration for COLOR_FormatYUV420Planar");
166                 } else if (cfg.mColorFormat == COLOR_FormatYUVP010 && cfg.mInputBitDepth != 10) {
167                     throw new IllegalArgumentException(
168                             "bad bit depth configuration for COLOR_FormatYUVP010");
169                 } else if (cfg.mColorFormat == COLOR_FormatSurface && cfg.mInputBitDepth != 8
170                         && cfg.mInputBitDepth != 10) {
171                     throw new IllegalArgumentException(
172                             "bad bit depth configuration for COLOR_FormatSurface");
173                 }
174                 mInputBitDepth = cfg.mInputBitDepth;
175             } else if (cfg.mColorFormat == COLOR_FormatYUVP010) {
176                 mInputBitDepth = 10;
177             } else {
178                 mInputBitDepth = 8;
179             }
180             if (mProfile == -1) {
181                 if ((mColorFormat == COLOR_FormatSurface && mInputBitDepth == 10) || (mColorFormat
182                         == COLOR_FormatYUVP010)) {
183                     throw new IllegalArgumentException("If color format is configured to "
184                             + "COLOR_FormatSurface and bitdepth is set to 10 or color format is "
185                             + "configured to COLOR_FormatYUVP010 then profile needs to be"
186                             + " configured");
187                 }
188             }
189             mRange = cfg.mRange;
190             mStandard = cfg.mStandard;
191             mTransfer = cfg.mTransfer;
192 
193             // satisfy Variable '*' might not have been initialized, unused by this media type
194             mSampleRate = 8000;
195             mChannelCount = 1;
196             mCompressionLevel = 5;
197             mPcmEncoding = AudioFormat.ENCODING_INVALID;
198         }
199         mBuilder = cfg;
200     }
201 
getBuilder()202     public Builder getBuilder() throws CloneNotSupportedException {
203         return mBuilder.clone();
204     }
205 
getFormat()206     public MediaFormat getFormat() {
207         if (mFormat != null) return new MediaFormat(mFormat);
208 
209         mFormat = new MediaFormat();
210         mFormat.setString(MediaFormat.KEY_MIME, mMediaType);
211         if (mIsAudio) {
212             mFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, mSampleRate);
213             mFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, mChannelCount);
214             if (mMediaType.equals(MediaFormat.MIMETYPE_AUDIO_FLAC)) {
215                 mFormat.setInteger(MediaFormat.KEY_FLAC_COMPRESSION_LEVEL, mCompressionLevel);
216             } else {
217                 mFormat.setInteger(MediaFormat.KEY_BIT_RATE, mBitRate);
218             }
219             if (mProfile >= 0 && mMediaType.equals(MediaFormat.MIMETYPE_AUDIO_AAC)) {
220                 mFormat.setInteger(MediaFormat.KEY_PROFILE, mProfile);
221                 mFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, mProfile);
222             }
223             mFormat.setInteger(MediaFormat.KEY_PCM_ENCODING, mPcmEncoding);
224         } else {
225             mFormat.setInteger(MediaFormat.KEY_WIDTH, mWidth);
226             mFormat.setInteger(MediaFormat.KEY_HEIGHT, mHeight);
227             mFormat.setInteger(MediaFormat.KEY_FRAME_RATE, mFrameRate);
228             mFormat.setInteger(MediaFormat.KEY_BIT_RATE, mBitRate);
229             mFormat.setFloat(MediaFormat.KEY_I_FRAME_INTERVAL, mKeyFrameInterval);
230             mFormat.setInteger(MediaFormat.KEY_MAX_B_FRAMES, mMaxBFrames);
231             if (mBitRateMode >= 0) mFormat.setInteger(MediaFormat.KEY_BITRATE_MODE, mBitRateMode);
232             if (mProfile >= 0) mFormat.setInteger(MediaFormat.KEY_PROFILE, mProfile);
233             if (mLevel >= 0) mFormat.setInteger(MediaFormat.KEY_LEVEL, mLevel);
234             mFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, mColorFormat);
235             if (mRange >= 0) mFormat.setInteger(MediaFormat.KEY_COLOR_RANGE, mRange);
236             if (mStandard >= 0) mFormat.setInteger(MediaFormat.KEY_COLOR_STANDARD, mStandard);
237             if (mTransfer >= 0) mFormat.setInteger(MediaFormat.KEY_COLOR_TRANSFER, mTransfer);
238         }
239         return new MediaFormat(mFormat);
240     }
241 
242     /**
243      * Converts MediaFormat object to a string. All Keys, ValueTypes, Values are concatenated with
244      * a separator and sent for further usage.
245      */
serializeMediaFormat(MediaFormat format)246     public static String serializeMediaFormat(MediaFormat format) {
247         StringBuilder msg = new StringBuilder();
248         java.util.Set<String> keys = format.getKeys();
249         for (String key : keys) {
250             int valueTypeForKey = format.getValueTypeForKey(key);
251             msg.append(key).append(TOKEN_SEPARATOR);
252             msg.append(valueTypeForKey).append(TOKEN_SEPARATOR);
253             if (valueTypeForKey == MediaFormat.TYPE_INTEGER) {
254                 msg.append(format.getInteger(key)).append(TOKEN_SEPARATOR);
255             } else if (valueTypeForKey == MediaFormat.TYPE_FLOAT) {
256                 msg.append(format.getFloat(key)).append(TOKEN_SEPARATOR);
257             } else if (valueTypeForKey == MediaFormat.TYPE_STRING) {
258                 msg.append(format.getString(key)).append(TOKEN_SEPARATOR);
259             } else {
260                 throw new RuntimeException("unrecognized Type for Key: " + key);
261             }
262         }
263         return msg.toString();
264     }
265 
266     @NonNull
267     @Override
toString()268     public String toString() {
269         if (mMsg != null) return mMsg.toString();
270 
271         mMsg = new StringBuilder();
272         mMsg.append(String.format("media type : %s, ", mMediaType));
273         if (mIsAudio) {
274             mMsg.append(String.format("Sample rate : %d, ", mSampleRate));
275             mMsg.append(String.format("Channel count : %d, ", mChannelCount));
276             if (mMediaType.equals(MediaFormat.MIMETYPE_AUDIO_FLAC)) {
277                 mMsg.append(String.format("Compression level : %d, ", mCompressionLevel));
278             } else {
279                 mMsg.append(String.format("Bitrate : %d, ", mBitRate));
280             }
281             if (mMediaType.equals(MediaFormat.MIMETYPE_AUDIO_AAC) && mProfile != -1) {
282                 mMsg.append(String.format("Profile : %d, ", mProfile));
283             }
284             mMsg.append(String.format("encoding : %d, ", mPcmEncoding));
285         } else {
286             mMsg.append(String.format("Width : %d, ", mWidth));
287             mMsg.append(String.format("Height : %d, ", mHeight));
288             mMsg.append(String.format("Frame rate : %d, ", mFrameRate));
289             mMsg.append(String.format("Bit rate : %d, ", mBitRate));
290             mMsg.append(String.format("key frame interval : %f, ", mKeyFrameInterval));
291             mMsg.append(String.format("max b frames : %d, ", mMaxBFrames));
292             if (mBitRateMode >= 0) mMsg.append(String.format("bitrate mode : %d, ", mBitRateMode));
293             if (mProfile >= 0) mMsg.append(String.format("profile : %x, ", mProfile));
294             if (mLevel >= 0) mMsg.append(String.format("level : %x, ", mLevel));
295             mMsg.append(String.format("color format : %x, ", mColorFormat));
296             if (mColorFormat == COLOR_FormatSurface) {
297                 mMsg.append(String.format("bit depth : %d, ", mInputBitDepth));
298             }
299             if (mRange >= 0) mMsg.append(String.format("color range : %d, ", mRange));
300             if (mStandard >= 0) mMsg.append(String.format("color standard : %d, ", mStandard));
301             if (mTransfer >= 0) mMsg.append(String.format("color transfer : %d, ", mTransfer));
302         }
303         mMsg.append("\n");
304         return mMsg.toString();
305     }
306 
307     public static class Builder implements Cloneable {
308         public String mMediaType;
309 
310         // video params
311         public int mWidth = 352;
312         public int mHeight = 288;
313         public int mFrameRate = -1;
314         public int mBitRateMode = -1;
315         public float mKeyFrameInterval = 1.0f;
316         public int mMaxBFrames = 0;
317         public int mLevel = -1;
318         public int mColorFormat = COLOR_FormatYUV420Flexible;
319         public int mInputBitDepth = -1;
320         public int mRange = -1;
321         public int mStandard = -1;
322         public int mTransfer = -1;
323 
324         // audio params
325         public int mSampleRate = 8000;
326         public int mChannelCount = 1;
327         public int mCompressionLevel = 5;
328         public int mPcmEncoding = AudioFormat.ENCODING_PCM_16BIT;
329 
330         // common params
331         public int mProfile = -1;
332         public int mBitRate = 256000;
333 
Builder(String mediaType)334         public Builder(String mediaType) {
335             mMediaType = mediaType;
336         }
337 
setWidth(int width)338         public Builder setWidth(int width) {
339             this.mWidth = width;
340             return this;
341         }
342 
setHeight(int height)343         public Builder setHeight(int height) {
344             this.mHeight = height;
345             return this;
346         }
347 
setFrameRate(int frameRate)348         public Builder setFrameRate(int frameRate) {
349             this.mFrameRate = frameRate;
350             return this;
351         }
352 
setBitRateMode(int bitRateMode)353         public Builder setBitRateMode(int bitRateMode) {
354             this.mBitRateMode = bitRateMode;
355             return this;
356         }
357 
setKeyFrameInterval(float keyFrameInterval)358         public Builder setKeyFrameInterval(float keyFrameInterval) {
359             this.mKeyFrameInterval = keyFrameInterval;
360             return this;
361         }
362 
setMaxBFrames(int maxBFrames)363         public Builder setMaxBFrames(int maxBFrames) {
364             this.mMaxBFrames = maxBFrames;
365             return this;
366         }
367 
setLevel(int level)368         public Builder setLevel(int level) {
369             this.mLevel = level;
370             return this;
371         }
372 
setColorFormat(int colorFormat)373         public Builder setColorFormat(int colorFormat) {
374             this.mColorFormat = colorFormat;
375             return this;
376         }
377 
setInputBitDepth(int inputBitDepth)378         public Builder setInputBitDepth(int inputBitDepth) {
379             this.mInputBitDepth = inputBitDepth;
380             return this;
381         }
382 
setRange(int range)383         public Builder setRange(int range) {
384             this.mRange = range;
385             return this;
386         }
387 
setStandard(int standard)388         public Builder setStandard(int standard) {
389             this.mStandard = standard;
390             return this;
391         }
392 
setTransfer(int transfer)393         public Builder setTransfer(int transfer) {
394             this.mTransfer = transfer;
395             return this;
396         }
397 
setSampleRate(int sampleRate)398         public Builder setSampleRate(int sampleRate) {
399             this.mSampleRate = sampleRate;
400             return this;
401         }
402 
setChannelCount(int channelCount)403         public Builder setChannelCount(int channelCount) {
404             this.mChannelCount = channelCount;
405             return this;
406         }
407 
setCompressionLevel(int compressionLevel)408         public Builder setCompressionLevel(int compressionLevel) {
409             this.mCompressionLevel = compressionLevel;
410             return this;
411         }
412 
setPcmEncoding(int pcmEncoding)413         public Builder setPcmEncoding(int pcmEncoding) {
414             this.mPcmEncoding = pcmEncoding;
415             return this;
416         }
417 
setProfile(int profile)418         public Builder setProfile(int profile) {
419             this.mProfile = profile;
420             return this;
421         }
422 
setBitRate(int bitRate)423         public Builder setBitRate(int bitRate) {
424             this.mBitRate = bitRate;
425             return this;
426         }
427 
build()428         public EncoderConfigParams build() {
429             return new EncoderConfigParams(this);
430         }
431 
432         @NonNull
clone()433         public Builder clone() throws CloneNotSupportedException {
434             return (Builder) super.clone();
435         }
436     }
437 }
438