1. Description of the phenomenon
There is a project at work that uses CameraView , a third-party camera library, to call the camera.
CameraView
Encapsulated Camera1
and Camera2
, many functions are encapsulated inside, and API
it is relatively simple to use.
After connecting in App
the middle, the video can be recorded normally on the mobile phone, and there seems to be no problem. But when I installed it on the car
with great joy , I found that the error shown in the picture below will be reported when recording a video on the car:App
java.lang.illegalStateException: at MediaCodec.native_dequeueOutputBuffer
Why is this? The reason for this problem will be explored below.
2. MediaCodec related API description
First, let's introduce the relevantMediaCodec API
2.1 Get all supported encoders
MediaCodecInfo[] array = new MediaCodecList(MediaCodecList.REGULAR_CODECS).getCodecInfos();
The list of encoders obtained on the car, you can see that the supported video/avc
hard codes are OMX.qcom.video.encoder.avc
, and the soft codes areOMX.google.h264.encoder
codecName:OMX.google.aac.encoder supportedTypes:audio/mp4a-latm
codecName:OMX.google.amrnb.encoder supportedTypes:audio/3gpp
codecName:OMX.google.amrwb.encoder supportedTypes:audio/amr-wb
codecName:OMX.google.flac.encoder supportedTypes:audio/flac
codecName:OMX.qcom.video.encoder.avc supportedTypes:video/avc
codecName:OMX.google.h264.encoder supportedTypes:video/avc
codecName:OMX.qcom.video.encoder.h263sw supportedTypes:video/3gpp
codecName:OMX.google.h263.encoder supportedTypes:video/3gpp
codecName:OMX.qcom.video.encoder.hevc supportedTypes:video/hevc
codecName:OMX.qcom.video.encoder.mpeg4sw supportedTypes:video/mp4v-es
codecName:OMX.google.mpeg4.encoder supportedTypes:video/mp4v-es
codecName:OMX.qcom.video.encoder.vp8 supportedTypes:video/x-vnd.on2.vp8
codecName:OMX.google.vp8.encoder supportedTypes:video/x-vnd.on2.vp8
codecName:OMX.google.vp9.encoder supportedTypes:video/x-vnd.on2.vp9
MediaCodecList.REGULAR_CODECS
Indicates that only standard, stable codecs are fetched. Using this enumeration value can ensure that the obtained codecs are sorted according to the standard and stability of the Android platform, which can avoid pitfalls in audio and video development to a certain extent.MediaCodecList.ALL_CODECS
Means to get all available codecs, including possibly non-standard, unstable codecs. Using this enumeration value can get more codec information, but there may be some unstable factors.
2.2 Create MediaCodec by specifying an encoder
In this way, a MediaCodec with a specified encoder will be created
MediaCodec codec = MediaCodec.createEncoderByType("OMX.google.h264.encoder")
2.3 Create MediaCodec by specifying Mine
mimeType
In this way, an encoder that is most recommended by the system under that category is created .
MediaCodec codec = MediaCodec.createEncoderByType("video/avc");
3. Which encoder to choose?
At this point, you may be wondering, which one should be selected with so many encoders?
First of all miniType
, we video/avc
just need to choose video recording, that is h264
.
video/avc
The encoder in the car machine has OMX.qcom.video.encoder.avc
and OMX.google.h264.encoder
.
Here, let's learn about hard coding and soft coding.
3.1 Hardcoding and softcoding
- Soft decoding is to use the computing power of the CPU to decode. If the CPU is not very strong, the decoding speed will be slower, and the phone will also heat up. However, due to the use of a unified algorithm, the compatibility will be very good.
- Hard decoding is to use the special decoding chip on the mobile phone to speed up the decoding. Generally, the speed of hard decoding is much faster, but because hard decoding is implemented by various manufacturers, the quality is uneven, and compatibility problems are prone to occur.
advantage | shortcoming | |
---|---|---|
soft coding | 1. Strong compatibility, low requirements on the system version, and less errors. 2. In terms of decoding, the color of soft decoding is generally softer than that of hard decoding. 3. The encoding space is relatively large and the degree of freedom is high. |
1. High CPU consumption 2. The machine tends to heat up 3. High power consumption |
hard decoding | Low power consumption, high execution efficiency | 1. Different types of chips implement codecs differently, and there is no guarantee that the decoding effect is the same as other models or error-free. 2. Poor controllability, relying on the underlying codec implementation 3. Difficult to upgrade and maintain |
3.2 How to judge hard coding and soft coding
In general, the encoders omx.google.
at the beginning and the beginning are soft encodings.c2.android.
In addition, all encoders that do not start with and can also be classified as soft encoding omx.
.c2.
Others can be considered as implicit coding, and the specific judgment code is as follows.
boolean isHardwareEncoder(@NonNull String encoder) {
encoder = encoder.toLowerCase();
boolean isSoftwareEncoder = encoder.startsWith("omx.google.")
|| encoder.startsWith("c2.android.")
|| (!encoder.startsWith("omx.") && !encoder.startsWith("c2."));
return !isSoftwareEncoder;
}
3.3 Solve recording crash
At this point, we know that we can use soft decoders, that is, OMX.google.h264.encoder
to reduce compatibility issues.
Indeed, we tried to use OMX.google.h264.encoder
this soft coding, and the video can be recorded normally on the car.
But what exactly is this for?
4. Third-party library CameraView source code analysis
Next, let's explore CameraView
the source code. The following CameraView
source code analysis is based on CameraView 2.7.2
the version
4.1 Initialize MediaCodec
Let's first look at MediaCodec
when it was initialized.
You can find the initialization method MediaCodec
in VideoMediaEncoder.java
this classonPrepare
This method will create exactly one encoder if you pass create mConfig.encoder
without it .NULL
createByCodecName
MediaCodec
Otherwise, through createEncoderByType
creation , the system will create a most recommended one MediaCodec
based on the incoming one .mineType
MediaCodec
if (mConfig.encoder != null) {
mMediaCodec = MediaCodec.createByCodecName(mConfig.encoder);
} else {
mMediaCodec = MediaCodec.createEncoderByType(mConfig.mimeType); //mineType默认为video/avc
}
Let's take a look at mConfig.encoder
where it is assigned
SnapshotVideoRecorder#onRendererFrame()
There is such a line in the trace
videoConfig.encoder = deviceEncoders.getVideoEncoder();
instead getVideoEncoder
of callingmVideoEncoder.getName()
public String getVideoEncoder() {
if (mVideoEncoder != null) {
return mVideoEncoder.getName();
} else {
return null;
}
}
4.2 When is mVideoEncoder assigned
Where did this mVideoEncoder
come from?
It can be found that it is assigned DeviceEncoders
in the constructormVideoEncoder
List<MediaCodecInfo> encoders = getDeviceEncoders();
mVideoEncoder = findDeviceEncoder(encoders, videoType, mode, videoOffset);
4.3 findDeviceEncoder parameter analysis
Take a look at parameter one encoders
, which is getDeviceEncoders
used to obtain all supported encoders
//调用MediaCodecList.getCodecInfos(),获取所有支持的编码器
List<MediaCodecInfo> getDeviceEncoders() {
ArrayList<MediaCodecInfo> results = new ArrayList<>();
MediaCodecInfo[] array = new MediaCodecList(MediaCodecList.REGULAR_CODECS).getCodecInfos();
for (MediaCodecInfo info : array) {
if (info.isEncoder()) results.add(info);
}
return results;
}
The second parameter videoType
is by defaultvideo/avc
Parameter three has two options
-
MODE_RESPECT_ORDER
: Look at the literal meaning, respect the order provided by the system -
MODE_PREFER_HARDWARE
: Literally, it is more inclined to hardcode
Parameter four videoOffset
, by default 0
, only the outer layer try-catch
is abnormal, videoOffset++
try the next decoder.
4.4 Internal implementation of findDeviceEncoder
Next, let's look at findDeviceEncoder
the encoder used to select the final
- It will be matched first
nimeType
, and the same will be added to the waiting list - If
mode
yesmode == MODE_PREFER_HARDWARE
, the candidate list will be sorted according to the rules of hard-coded priority videoOffset
Finally, take out the corresponding encoder from the list to be selected according to this index
//选定最终的编码器
MediaCodecInfo findDeviceEncoder(@NonNull List<MediaCodecInfo> encoders,
@NonNull String mimeType,
int mode,
int offset) {
ArrayList<MediaCodecInfo> results = new ArrayList<>();
for (MediaCodecInfo encoder : encoders) {
String[] types = encoder.getSupportedTypes();
for (String type : types) {
if (type.equalsIgnoreCase(mimeType)) {
results.add(encoder);
break;
}
}
}
LOG.i("findDeviceEncoder -", "type:", mimeType, "encoders:", results.size());
if (mode == MODE_PREFER_HARDWARE) {
Collections.sort(results, new Comparator<MediaCodecInfo>() {
@Override
public int compare(MediaCodecInfo o1, MediaCodecInfo o2) {
boolean hw1 = isHardwareEncoder(o1.getName());
boolean hw2 = isHardwareEncoder(o2.getName());
return Boolean.compare(hw2, hw1);
}
});
}
if (results.size() < offset + 1) {
// This should not be a VideoException or AudioException - we want the process
// to crash here.
throw new RuntimeException("No encoders for type:" + mimeType);
}
return results.get(offset);
}
At this point, the acquisition order of the parser is clear, so VideoMediaEncoder
when is it initialized?
4.5 When is VideoMediaEncoder initialized
There are two places to be called
SnapshotVideoRecorder
Constructor -> The specific calling method is:takeVideo -> onTakeVideo -> 创建SnapshotVideoRecorder并调用其start()
FullVideoRecorder
Constructor -> The specific calling method is:takeVideoSnapshot -> onTakeVideoSnapshot -> 创建FullVideoRecorder并调用其start()
4.6 Bugs in CameraView
SnapshotVideoRecorder
Created twice in , DeviceEncoders
and the final effective one is DeviceEncoders.MODE_PREFER_HARDWARE
(preferably hard-coded) DeviceEncoders
, which causes the call takeVideoSnapshot
(record video with filter) to use hard-coded first.
However, FullVideoRecorder
it is only created once in DeviceEncoders
, and the final effect is DeviceEncoders.MODE_RESPECT_ORDER
(according to the order provided by the system), by takeVideo
calling (recording video without filter).
At the same time, due to OMX.qcom.video.encoder.avc
the problem of this hard coding, there is also a problem with the function of selecting the appropriate video resolution. The selected resolution is width=192px,height=108px
, but the actual one should be 1920*1080
.
5. Summary
At this point, we understand why an error is reported directly when recording a video on the car.
Because the car machine system itself does not support OMX.qcom.video.encoder.avc
this hard coding, but it is returned in the support list OMX.qcom.video.encoder.avc
, and it is placed in video/avc
the highest priority sequence. This is the most important and fundamental reason.
Secondly, when CameraView
this library is recording a video with a filter ( ), because it was created twice , the hard code was finally selected , which led to a direct crash.takeVideoSnapshot
BUG
DeviceEncoders
OMX.qcom.video.encoder.avc
App
When recording a video without a filter ( takeVideo
), it is not created twice DeviceEncoders
, BUG
but because video/avc
the highest priority sequence provided by the system is OMX.qcom.video.encoder.avc
, it App
still crashes.