1 /* 2 * Copyright (C) 2020 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 package com.android.media.samplevideoencoder; 17 18 import android.media.MediaCodec; 19 import android.media.MediaCodecInfo; 20 import android.media.MediaCodecList; 21 import android.media.MediaFormat; 22 import android.os.Build; 23 import android.util.Log; 24 import android.util.Pair; 25 26 import java.util.ArrayList; 27 import java.util.LinkedList; 28 import java.util.concurrent.locks.Condition; 29 import java.util.concurrent.locks.Lock; 30 import java.util.concurrent.locks.ReentrantLock; 31 32 class CodecAsyncHandler extends MediaCodec.Callback { 33 private static final String TAG = CodecAsyncHandler.class.getSimpleName(); 34 private final Lock mLock = new ReentrantLock(); 35 private final Condition mCondition = mLock.newCondition(); 36 private final LinkedList<Pair<Integer, MediaCodec.BufferInfo>> mCbInputQueue; 37 private final LinkedList<Pair<Integer, MediaCodec.BufferInfo>> mCbOutputQueue; 38 private volatile boolean mSignalledError; 39 CodecAsyncHandler()40 CodecAsyncHandler() { 41 mCbInputQueue = new LinkedList<>(); 42 mCbOutputQueue = new LinkedList<>(); 43 mSignalledError = false; 44 } 45 clearQueues()46 void clearQueues() { 47 mLock.lock(); 48 mCbInputQueue.clear(); 49 mCbOutputQueue.clear(); 50 mLock.unlock(); 51 } 52 resetContext()53 void resetContext() { 54 clearQueues(); 55 mSignalledError = false; 56 } 57 58 @Override onInputBufferAvailable(MediaCodec codec, int bufferIndex)59 public void onInputBufferAvailable(MediaCodec codec, int bufferIndex) { 60 mLock.lock(); 61 mCbInputQueue.add(new Pair<>(bufferIndex, (MediaCodec.BufferInfo) null)); 62 mCondition.signalAll(); 63 mLock.unlock(); 64 } 65 66 @Override onOutputBufferAvailable(MediaCodec codec, int bufferIndex, MediaCodec.BufferInfo info)67 public void onOutputBufferAvailable(MediaCodec codec, int bufferIndex, 68 MediaCodec.BufferInfo info) { 69 mLock.lock(); 70 mCbOutputQueue.add(new Pair<>(bufferIndex, info)); 71 mCondition.signalAll(); 72 mLock.unlock(); 73 } 74 75 @Override onError(MediaCodec codec, MediaCodec.CodecException e)76 public void onError(MediaCodec codec, MediaCodec.CodecException e) { 77 mLock.lock(); 78 mSignalledError = true; 79 mCondition.signalAll(); 80 mLock.unlock(); 81 Log.e(TAG, "Received media codec error : " + e.getMessage()); 82 } 83 84 @Override onOutputFormatChanged(MediaCodec codec, MediaFormat format)85 public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) { 86 Log.i(TAG, "Output format changed: " + format.toString()); 87 } 88 setCallBack(MediaCodec codec, boolean isCodecInAsyncMode)89 void setCallBack(MediaCodec codec, boolean isCodecInAsyncMode) { 90 if (isCodecInAsyncMode) { 91 codec.setCallback(this); 92 } 93 } 94 getOutput()95 Pair<Integer, MediaCodec.BufferInfo> getOutput() throws InterruptedException { 96 Pair<Integer, MediaCodec.BufferInfo> element = null; 97 mLock.lock(); 98 while (!mSignalledError) { 99 if (mCbOutputQueue.isEmpty()) { 100 mCondition.await(); 101 } else { 102 element = mCbOutputQueue.remove(0); 103 break; 104 } 105 } 106 mLock.unlock(); 107 return element; 108 } 109 getWork()110 Pair<Integer, MediaCodec.BufferInfo> getWork() throws InterruptedException { 111 Pair<Integer, MediaCodec.BufferInfo> element = null; 112 mLock.lock(); 113 while (!mSignalledError) { 114 if (mCbInputQueue.isEmpty() && mCbOutputQueue.isEmpty()) { 115 mCondition.await(); 116 } else { 117 if (!mCbOutputQueue.isEmpty()) { 118 element = mCbOutputQueue.remove(0); 119 break; 120 } 121 if (!mCbInputQueue.isEmpty()) { 122 element = mCbInputQueue.remove(0); 123 break; 124 } 125 } 126 } 127 mLock.unlock(); 128 return element; 129 } 130 hasSeenError()131 boolean hasSeenError() { 132 return mSignalledError; 133 } 134 } 135 136 abstract public class MediaCodecBase { selectCodecs(String mime, ArrayList<MediaFormat> formats, String[] features, boolean isEncoder, boolean isSoftware)137 static ArrayList<String> selectCodecs(String mime, ArrayList<MediaFormat> formats, 138 String[] features, boolean isEncoder, 139 boolean isSoftware) { 140 141 MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS); 142 MediaCodecInfo[] codecInfos = codecList.getCodecInfos(); 143 ArrayList<String> listOfCodecs = new ArrayList<>(); 144 for (MediaCodecInfo codecInfo : codecInfos) { 145 if (isEncoder) { 146 if (!codecInfo.isEncoder()) continue; 147 } else { 148 if (codecInfo.isEncoder()) continue; 149 } 150 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && codecInfo.isAlias()) continue; 151 String[] types = codecInfo.getSupportedTypes(); 152 for (String type : types) { 153 if (type.equalsIgnoreCase(mime)) { 154 boolean isOk = true; 155 MediaCodecInfo.CodecCapabilities codecCapabilities = 156 codecInfo.getCapabilitiesForType(type); 157 if (formats != null) { 158 for (MediaFormat format : formats) { 159 if (!codecCapabilities.isFormatSupported(format)) { 160 isOk = false; 161 break; 162 } 163 } 164 } 165 if (features != null) { 166 for (String feature : features) { 167 if (!codecCapabilities.isFeatureSupported(feature)) { 168 isOk = false; 169 break; 170 } 171 } 172 } 173 if (isSoftware) { 174 if (codecInfo.getName().contains("software") || 175 codecInfo.getName().contains("android") || 176 codecInfo.getName().contains("google")) { 177 if (isOk) listOfCodecs.add(codecInfo.getName()); 178 } 179 } else { 180 if (codecInfo.getName().contains("software") || 181 codecInfo.getName().contains("android") || 182 codecInfo.getName().contains("google")) { 183 continue; 184 } else { 185 if (isOk) listOfCodecs.add(codecInfo.getName()); 186 } 187 } 188 } 189 } 190 } 191 return listOfCodecs; 192 } 193 } 194