• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright 2017 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 package org.webrtc;
12 
13 import static org.webrtc.MediaCodecUtils.EXYNOS_PREFIX;
14 import static org.webrtc.MediaCodecUtils.QCOM_PREFIX;
15 
16 import android.media.MediaCodecInfo;
17 import android.media.MediaCodecInfo.CodecCapabilities;
18 import android.media.MediaCodecList;
19 import android.os.Build;
20 import androidx.annotation.Nullable;
21 import java.util.ArrayList;
22 import java.util.List;
23 
24 /** Factory for decoders backed by Android MediaCodec API. */
25 @SuppressWarnings("deprecation") // API level 16 requires use of deprecated methods.
26 class MediaCodecVideoDecoderFactory implements VideoDecoderFactory {
27   private static final String TAG = "MediaCodecVideoDecoderFactory";
28 
29   private final @Nullable EglBase.Context sharedContext;
30   private final @Nullable Predicate<MediaCodecInfo> codecAllowedPredicate;
31 
32   /**
33    * MediaCodecVideoDecoderFactory with support of codecs filtering.
34    *
35    * @param sharedContext The textures generated will be accessible from this context. May be null,
36    *                      this disables texture support.
37    * @param codecAllowedPredicate optional predicate to test if codec allowed. All codecs are
38    *                              allowed when predicate is not provided.
39    */
MediaCodecVideoDecoderFactory(@ullable EglBase.Context sharedContext, @Nullable Predicate<MediaCodecInfo> codecAllowedPredicate)40   public MediaCodecVideoDecoderFactory(@Nullable EglBase.Context sharedContext,
41       @Nullable Predicate<MediaCodecInfo> codecAllowedPredicate) {
42     this.sharedContext = sharedContext;
43     this.codecAllowedPredicate = codecAllowedPredicate;
44   }
45 
46   @Nullable
47   @Override
createDecoder(VideoCodecInfo codecType)48   public VideoDecoder createDecoder(VideoCodecInfo codecType) {
49     VideoCodecMimeType type = VideoCodecMimeType.valueOf(codecType.getName());
50     MediaCodecInfo info = findCodecForType(type);
51 
52     if (info == null) {
53       return null;
54     }
55 
56     CodecCapabilities capabilities = info.getCapabilitiesForType(type.mimeType());
57     return new AndroidVideoDecoder(new MediaCodecWrapperFactoryImpl(), info.getName(), type,
58         MediaCodecUtils.selectColorFormat(MediaCodecUtils.DECODER_COLOR_FORMATS, capabilities),
59         sharedContext);
60   }
61 
62   @Override
getSupportedCodecs()63   public VideoCodecInfo[] getSupportedCodecs() {
64     List<VideoCodecInfo> supportedCodecInfos = new ArrayList<VideoCodecInfo>();
65     // Generate a list of supported codecs in order of preference:
66     // VP8, VP9, H264 (high profile), and H264 (baseline profile).
67     for (VideoCodecMimeType type : new VideoCodecMimeType[] {VideoCodecMimeType.VP8,
68              VideoCodecMimeType.VP9, VideoCodecMimeType.H264, VideoCodecMimeType.AV1}) {
69       MediaCodecInfo codec = findCodecForType(type);
70       if (codec != null) {
71         String name = type.name();
72         if (type == VideoCodecMimeType.H264 && isH264HighProfileSupported(codec)) {
73           supportedCodecInfos.add(new VideoCodecInfo(
74               name, MediaCodecUtils.getCodecProperties(type, /* highProfile= */ true)));
75         }
76 
77         supportedCodecInfos.add(new VideoCodecInfo(
78             name, MediaCodecUtils.getCodecProperties(type, /* highProfile= */ false)));
79       }
80     }
81 
82     return supportedCodecInfos.toArray(new VideoCodecInfo[supportedCodecInfos.size()]);
83   }
84 
findCodecForType(VideoCodecMimeType type)85   private @Nullable MediaCodecInfo findCodecForType(VideoCodecMimeType type) {
86     for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) {
87       MediaCodecInfo info = null;
88       try {
89         info = MediaCodecList.getCodecInfoAt(i);
90       } catch (IllegalArgumentException e) {
91         Logging.e(TAG, "Cannot retrieve decoder codec info", e);
92       }
93 
94       if (info == null || info.isEncoder()) {
95         continue;
96       }
97 
98       if (isSupportedCodec(info, type)) {
99         return info;
100       }
101     }
102 
103     return null; // No support for this type.
104   }
105 
106   // Returns true if the given MediaCodecInfo indicates a supported encoder for the given type.
isSupportedCodec(MediaCodecInfo info, VideoCodecMimeType type)107   private boolean isSupportedCodec(MediaCodecInfo info, VideoCodecMimeType type) {
108     if (!MediaCodecUtils.codecSupportsType(info, type)) {
109       return false;
110     }
111     // Check for a supported color format.
112     if (MediaCodecUtils.selectColorFormat(
113             MediaCodecUtils.DECODER_COLOR_FORMATS, info.getCapabilitiesForType(type.mimeType()))
114         == null) {
115       return false;
116     }
117     return isCodecAllowed(info);
118   }
119 
isCodecAllowed(MediaCodecInfo info)120   private boolean isCodecAllowed(MediaCodecInfo info) {
121     if (codecAllowedPredicate == null) {
122       return true;
123     }
124     return codecAllowedPredicate.test(info);
125   }
126 
isH264HighProfileSupported(MediaCodecInfo info)127   private boolean isH264HighProfileSupported(MediaCodecInfo info) {
128     String name = info.getName();
129     // Support H.264 HP decoding on QCOM chips.
130     if (name.startsWith(QCOM_PREFIX)) {
131       return true;
132     }
133     // Support H.264 HP decoding on Exynos chips for Android M and above.
134     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && name.startsWith(EXYNOS_PREFIX)) {
135       return true;
136     }
137     return false;
138   }
139 }
140