Android端WebRTC啟用H264編碼


Android端WebRTC啟用H264編碼

目前Android的使用WebRTC僅支持硬件上 H.264 解碼和編碼,並且僅支持部分芯片組。因此,如果設備不支持硬件 H.264 或具有不受支持的芯片組,您將只能使用 VP8、VP9。支持的芯片組僅有OMX.qcom.OMX.Exynos.**,不支持的要自行添加。

這里也是在createOffersdp中沒有H264信息的原因

VideoEncoderFactory創建

在創建PeerConnectionFactory,可以設VideoEncoderFactory

val encoderFactory = DefaultVideoEncoderFactory(eglBaseContext, true, true)
val peerConnectionFactory = PeerConnectionFactory.builder()
            .setVideoEncoderFactory(encoderFactory)
            .createPeerConnectionFactory()

DefaultVideoEncoderFactory類

public class DefaultVideoEncoderFactory implements VideoEncoderFactory {
	/**
     * 硬解件編碼工廠
     */
    private final VideoEncoderFactory hardwareVideoEncoderFactory;
    /**
     * 軟件編碼工廠
     */
    private final VideoEncoderFactory softwareVideoEncoderFactory = new SoftwareVideoEncoderFactory();

    public DefaultVideoEncoderFactory(Context eglContext, boolean enableIntelVp8Encoder, boolean enableH264HighProfile) {
    	//創建硬解編碼工廠
        this.hardwareVideoEncoderFactory = new HardwareVideoEncoderFactory(eglContext, enableIntelVp8Encoder, enableH264HighProfile);
    }
	/**
     * 注意這個構造方法僅包可見
     */
    DefaultVideoEncoderFactory(VideoEncoderFactory hardwareVideoEncoderFactory) {
        this.hardwareVideoEncoderFactory = hardwareVideoEncoderFactory;
    }
    ...
}

DefaultVideoEncoderFactory、HardwareVideoEncoderFactory、SoftwareVideoEncoderFactory類均實現了VideoEncoderFactory接口

VideoEncoderFactory

/**
 * 用於創建視頻編碼器工廠
 */
public interface VideoEncoderFactory {
	/** 
	 * 為給定的視頻編解碼器創建一個編碼器。
	 */
    @Nullable
    @CalledByNative
    VideoEncoder createEncoder(VideoCodecInfo var1);
	/**
     * 枚舉支持的視頻編解碼器列表。這個方法只會被調用一次,結果將被緩存。
     */
    @CalledByNative
    VideoCodecInfo[] getSupportedCodecs();

    @CalledByNative
    default VideoCodecInfo[] getImplementations() {
        return this.getSupportedCodecs();
    }

    @CalledByNative
    default VideoEncoderFactory.VideoEncoderSelector getEncoderSelector() {
        return null;
    }
}

關鍵在於getSupportedCodecs()HardwareVideoEncoderFactory中是如何實現的?

@Override
public VideoCodecInfo[] getSupportedCodecs() {
	// Android19以下不支持硬解編碼.
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
        return new VideoCodecInfo[0];
    }

    List<VideoCodecInfo> supportedCodecInfos = new ArrayList<>();
    // 按優先順序生成支持的編解碼器列表:
    // VP8, VP9, H264 (high profile), and H264 (baseline profile).
    for (VideoCodecType type : new VideoCodecType[]{VideoCodecType.VP8, VideoCodecType.VP9, VideoCodecType.H264}) {
		//查找編解碼器類型,這里是關鍵
		MediaCodecInfo codec = findCodecForType(type);
		if (codec != null) {
			String name = type.name();
            // supported by the decoder.
            if (type == VideoCodecType.H264 && isH264HighProfileSupported(codec)) {
                supportedCodecInfos.add(new VideoCodecInfo(
                            name, MediaCodecUtils.getCodecProperties(type, /* highProfile= */ true)));
            }
			supportedCodecInfos.add(new VideoCodecInfo(
                        name, MediaCodecUtils.getCodecProperties(type, /* highProfile= */ false)));
		}
    }
	return supportedCodecInfos.toArray(new VideoCodecInfo[0]);
}

findCodecForType(VideoCodecType),根據類型查找支持的編解碼器

private MediaCodecInfo findCodecForType(VideoCodecType type) {
    for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) {
        MediaCodecInfo info = null;
        try {
            info = MediaCodecList.getCodecInfoAt(i);
        } catch (IllegalArgumentException e) {
        	//無法檢索編碼器編解碼器信息
            Logging.e(TAG, "Cannot retrieve encoder codec info", e);
        }
        //編解器信息為null,或者不是編解碼器不是編碼器
		if (info == null || !info.isEncoder()) {
            continue;
        }
        //判斷編解碼器是否支持,這里就會去判斷不同的芯片組是否支持
		if (isSupportedCodec(info, type)) {
			return info;
		}
	}
    return null; // 不支持的類型
}

isSupportedCodec(MediaCodecInfo, VideoCodecType):判斷MediaCodecInfo和VideoCodecType結合設備芯片組信息是否支持

private boolean isSupportedCodec(MediaCodecInfo info, VideoCodecType type) {
	if (!MediaCodecUtils.codecSupportsType(info, type)) {
		return false;
	}
	// Check for a supported color format.
	if (MediaCodecUtils.selectColorFormat(
                MediaCodecUtils.ENCODER_COLOR_FORMATS, /*這一步其實就可以判斷編解碼器是否支持了給定的類型了,如果不拋異常的話*/info.getCapabilitiesForType(type.mimeType()))
                == null) {
		return false;
	}
	return isHardwareSupportedInCurrentSdk(info, type) && isMediaCodecAllowed(info);
}

/**
 * 結合當前的sdk,再次判斷是否支持
 */
private boolean isHardwareSupportedInCurrentSdk(MediaCodecInfo info, VideoCodecType type) {
	switch (type) {
		case VP8:
			return isHardwareSupportedInCurrentSdkVp8(info);
        case VP9:
			return isHardwareSupportedInCurrentSdkVp9(info);
		case H264:
			return isHardwareSupportedInCurrentSdkH264(info);
	}
	return false;
}

private boolean isHardwareSupportedInCurrentSdkH264(MediaCodecInfo info) {
	//H264 硬件在此類型設備上可能表現不佳。"SAMSUNG-SGH-I337", "Nexus 7", "Nexus 4"
	if (H264_HW_EXCEPTION_MODELS.contains(Build.MODEL)) {
		return false;
	} else {
		String name = info.getName();
		//問題就在這,寫死的僅支持的硬件編碼器解碼器組件名稱的前綴。
		//所以要在后面自行追加我們自己設備支持H264名稱信息。
		return name.startsWith("OMX.qcom.") && VERSION.SDK_INT >= 19 || name.startsWith("OMX.Exynos.") && VERSION.SDK_INT >= 21;
	}
}

至此,分析流程結束,如有錯誤或其它更好的方法,歡迎指正。

Github傳送門


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM