diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc deleted file mode 100644 index 98ed27cc..00000000 --- a/media/base/media_switches.cc +++ /dev/null @@ -1,1111 +0,0 @@ -// Copyright (c) 2022 The Chromium Authors and Alex313031. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "media/base/media_switches.h" - -#include "base/command_line.h" -#include "build/build_config.h" -#include "build/chromeos_buildflags.h" -#include "components/system_media_controls/linux/buildflags/buildflags.h" -#include "media/media_buildflags.h" - -#if BUILDFLAG(IS_LINUX) -#include "base/cpu.h" -#endif - -namespace switches { - -// Allow users to specify a custom buffer size for debugging purpose. -const char kAudioBufferSize[] = "audio-buffer-size"; - -// Set a timeout (in milliseconds) for the audio service to quit if there are no -// client connections to it. If the value is negative the service never quits. -const char kAudioServiceQuitTimeoutMs[] = "audio-service-quit-timeout-ms"; - -// Command line flag name to set the autoplay policy. -const char kAutoplayPolicy[] = "autoplay-policy"; - -const char kDisableAudioOutput[] = "disable-audio-output"; - -// Causes the AudioManager to fail creating audio streams. Used when testing -// various failure cases. -const char kFailAudioStreamCreation[] = "fail-audio-stream-creation"; - -// Set number of threads to use for video decoding. -const char kVideoThreads[] = "video-threads"; - -// Do not immediately suspend media in background tabs. -const char kDisableBackgroundMediaSuspend[] = - "disable-background-media-suspend"; - -// Force to report VP9 as an unsupported MIME type. -const char kReportVp9AsAnUnsupportedMimeType[] = - "report-vp9-as-an-unsupported-mime-type"; - -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FREEBSD) || \ - BUILDFLAG(IS_SOLARIS) -// The Alsa device to use when opening an audio input stream. -const char kAlsaInputDevice[] = "alsa-input-device"; -// The Alsa device to use when opening an audio stream. -const char kAlsaOutputDevice[] = "alsa-output-device"; -#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || - // BUILDFLAG(IS_FREEBSD) || BUILDFLAG(IS_SOLARIS) - -#if BUILDFLAG(IS_WIN) -// Use exclusive mode audio streaming for Windows Vista and higher. -// Leads to lower latencies for audio streams which uses the -// AudioParameters::AUDIO_PCM_LOW_LATENCY audio path. -// See http://msdn.microsoft.com/en-us/library/windows/desktop/dd370844.aspx -// for details. -const char kEnableExclusiveAudio[] = "enable-exclusive-audio"; - -// Use Windows WaveOut/In audio API even if Core Audio is supported. -const char kForceWaveAudio[] = "force-wave-audio"; - -// Instead of always using the hardware channel layout, check if a driver -// supports the source channel layout. Avoids outputting empty channels and -// permits drivers to enable stereo to multichannel expansion. Kept behind a -// flag since some drivers lie about supported layouts and hang when used. See -// http://crbug.com/259165 for more details. -const char kTrySupportedChannelLayouts[] = "try-supported-channel-layouts"; - -// Number of buffers to use for WaveOut. -const char kWaveOutBuffers[] = "waveout-buffers"; - -// Emulates audio capture timestamps instead of using timestamps from the actual -// audio device. -// See crbug.com/1315231 for more details. -const char kUseFakeAudioCaptureTimestamps[] = - "use-fake-audio-capture-timestamps"; -#endif // BUILDFLAG(IS_WIN) - -#if BUILDFLAG(IS_FUCHSIA) -// Enables protected buffers for encrypted video streams. -const char kEnableProtectedVideoBuffers[] = "enable-protected-video-buffers"; - -// Forces protected memory for all output video buffers generated by -// FuchsiaVideoDecoder, including unencrypted streams. Ignored unless -// --enable-protected-video-buffers is also specified. -const char kForceProtectedVideoOutputBuffers[] = - "force-protected-video-output-buffers"; - -const char kDisableAudioInput[] = "disable-audio-input"; - -// Present video content as overlays. -const char kUseOverlaysForVideo[] = "use-overlays-for-video"; - -// Forces AudioManagerFuchsia to assume that the AudioCapturer implements echo -// cancellation. -// TODO(crbug.com/852834): Remove this once AudioManagerFuchsia is updated to -// get this information from AudioCapturerFactory. -const char kAudioCapturerWithEchoCancellation[] = - "audio-capturer-with-echo-cancellation"; -#endif // BUILDFLAG(IS_FUCHSIA) - -#if defined(USE_CRAS) -// Use CRAS, the ChromeOS audio server. -const char kUseCras[] = "use-cras"; -#endif // defined(USE_CRAS) - -// For automated testing of protected content, this switch allows specific -// domains (e.g. example.com) to always allow the permission to share the -// protected media identifier. In this context, domain does not include the -// port number. User's content settings will not be affected by enabling this -// switch. -// Reference: http://crbug.com/718608 -// Example: -// --unsafely-allow-protected-media-identifier-for-domain=a.com,b.ca -const char kUnsafelyAllowProtectedMediaIdentifierForDomain[] = - "unsafely-allow-protected-media-identifier-for-domain"; - -// Use fake device for Media Stream to replace actual camera and microphone. -// For the list of allowed parameters, see -// FakeVideoCaptureDeviceFactory::ParseFakeDevicesConfigFromOptionsString(). -const char kUseFakeDeviceForMediaStream[] = "use-fake-device-for-media-stream"; - -// Use an .y4m file to play as the webcam. See the comments in -// media/capture/video/file_video_capture_device.h for more details. -const char kUseFileForFakeVideoCapture[] = "use-file-for-fake-video-capture"; - -// Play a .wav file as the microphone. Note that for WebRTC calls we'll treat -// the bits as if they came from the microphone, which means you should disable -// audio processing (lest your audio file will play back distorted). The input -// file is converted to suit Chrome's audio buses if necessary, so most sane -// .wav files should work. You can pass either to play the file looping -// or %noloop to stop after playing the file to completion. -const char kUseFileForFakeAudioCapture[] = "use-file-for-fake-audio-capture"; - -// Use a fake device for accelerated decoding of MJPEG. This allows, for -// example, testing of the communication to the GPU service without requiring -// actual accelerator hardware to be present. -const char kUseFakeMjpegDecodeAccelerator[] = - "use-fake-mjpeg-decode-accelerator"; - -// Disable hardware acceleration of mjpeg decode for captured frame, where -// available. -const char kDisableAcceleratedMjpegDecode[] = - "disable-accelerated-mjpeg-decode"; - -// When running tests on a system without the required hardware or libraries, -// this flag will cause the tests to fail. Otherwise, they silently succeed. -const char kRequireAudioHardwareForTesting[] = - "require-audio-hardware-for-testing"; - -// Mutes audio sent to the audio device so it is not audible during -// automated testing. -const char kMuteAudio[] = "mute-audio"; - -// Allows clients to override the threshold for when the media renderer will -// declare the underflow state for the video stream when audio is present. -// TODO(dalecurtis): Remove once experiments for http://crbug.com/470940 finish. -const char kVideoUnderflowThresholdMs[] = "video-underflow-threshold-ms"; - -// Disables the new rendering algorithm for webrtc, which is designed to improve -// the rendering smoothness. -const char kDisableRTCSmoothnessAlgorithm[] = - "disable-rtc-smoothness-algorithm"; - -// Force media player using SurfaceView instead of SurfaceTexture on Android. -const char kForceVideoOverlays[] = "force-video-overlays"; - -// Allows explicitly specifying MSE audio/video buffer sizes as megabytes. -// Default values are 150M for video and 12M for audio. -const char kMSEAudioBufferSizeLimitMb[] = "mse-audio-buffer-size-limit-mb"; -const char kMSEVideoBufferSizeLimitMb[] = "mse-video-buffer-size-limit-mb"; - -// Specifies the path to the Clear Key CDM for testing, which is necessary to -// support External Clear Key key system when library CDM is enabled. Note that -// External Clear Key key system support is also controlled by feature -// kExternalClearKeyForTesting. -const char kClearKeyCdmPathForTesting[] = "clear-key-cdm-path-for-testing"; - -// Overrides the default enabled library CDM interface version(s) with the one -// specified with this switch, which will be the only version enabled. For -// example, on a build where CDM 8, CDM 9 and CDM 10 are all supported -// (implemented), but only CDM 8 and CDM 9 are enabled by default: -// --override-enabled-cdm-interface-version=8 : Only CDM 8 is enabled -// --override-enabled-cdm-interface-version=9 : Only CDM 9 is enabled -// --override-enabled-cdm-interface-version=10 : Only CDM 10 is enabled -// --override-enabled-cdm-interface-version=11 : No CDM interface is enabled -// This can be used for local testing and debugging. It can also be used to -// enable an experimental CDM interface (which is always disabled by default) -// for testing while it's still in development. -const char kOverrideEnabledCdmInterfaceVersion[] = - "override-enabled-cdm-interface-version"; - -// Overrides hardware secure codecs support for testing. If specified, real -// platform hardware secure codecs check will be skipped. Valid codecs are: -// - video: "vp8", "vp9", "avc1", "hevc", "dolbyvision" -// - audio: "mp4a", "vorbis" -// Codecs are separated by comma. For example: -// --override-hardware-secure-codecs-for-testing=vp8,vp9,vorbis -// --override-hardware-secure-codecs-for-testing=avc1,mp4a -// CENC encryption scheme is assumed to be supported for the specified codecs. -// If no valid codecs specified, no hardware secure codecs are supported. This -// can be used to disable hardware secure codecs support: -// --override-hardware-secure-codecs-for-testing -const char kOverrideHardwareSecureCodecsForTesting[] = - "override-hardware-secure-codecs-for-testing"; - -// Sets the default value for the kLiveCaptionEnabled preference to true. -const char kEnableLiveCaptionPrefForTesting[] = - "enable-live-caption-pref-for-testing"; - -#if BUILDFLAG(IS_CHROMEOS) -// These are flags passed from ash-chrome to lacros-chrome that correspond to -// buildflags for the platform we are running on. lacros-chrome only builds for -// x86/arm differences, so we unconditionally build in the below features into -// the relevant parts of lacros-chrome and then filter the functionality based -// on these command line flags. -MEDIA_EXPORT extern const char kLacrosEnablePlatformHevc[] = - "lacros-enable-platform-hevc"; -MEDIA_EXPORT extern const char kLacrosUseChromeosProtectedMedia[] = - "lacros-use-chromeos-protected-media"; -MEDIA_EXPORT extern const char kLacrosUseChromeosProtectedAv1[] = - "lacros-use-chromeos-protected-av1"; -#endif // BUILDFLAG(IS_CHROMEOS) - -namespace autoplay { - -// Autoplay policy that requires a document user activation. -const char kDocumentUserActivationRequiredPolicy[] = - "document-user-activation-required"; - -// Autoplay policy that does not require any user gesture. -const char kNoUserGestureRequiredPolicy[] = "no-user-gesture-required"; - -// Autoplay policy to require a user gesture in order to play. -const char kUserGestureRequiredPolicy[] = "user-gesture-required"; - -} // namespace autoplay - -#if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION) -// Some (Qualcomm only at the moment) V4L2 video decoders require setting the -// framerate so that the hardware decoder can scale the clocks efficiently. -// This provides a mechanism during testing to lock the decoder framerate -// to a specific value. -const char kHardwareVideoDecodeFrameRate[] = "hardware-video-decode-framerate"; -// Set the maximum number of decoder threads for hardware video decoders on -// ChromeOS. This is intended to be used for development only. -// TODO(b/195769334): Propagate this to Chrome utility process for -// Out-of-Process video decoding. -const char kMaxChromeOSDecoderThreads[] = "max-chromeos-decoder-threads"; -#endif -} // namespace switches - -namespace media { - -// Prefer FFmpeg to LibVPX for Vp8 decoding with opaque alpha mode. -const base::Feature kFFmpegDecodeOpaqueVP8{"FFmpegDecodeOpaqueVP8", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Only used for disabling overlay fullscreen (aka SurfaceView) in Clank. -const base::Feature kOverlayFullscreenVideo{"overlay-fullscreen-video", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Enables user control over muting tab audio from the tab strip. -const base::Feature kEnableTabMuting{"EnableTabMuting", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Enable Picture-in-Picture. -const base::Feature kPictureInPicture{"PictureInPicture", - base::FEATURE_ENABLED_BY_DEFAULT}; - -#if BUILDFLAG(ENABLE_PLATFORM_HEVC) -// Enables HEVC hardware accelerated decoding. -const base::Feature kPlatformHEVCDecoderSupport{ - "PlatformHEVCDecoderSupport", base::FEATURE_ENABLED_BY_DEFAULT}; -#endif // BUILDFLAG(ENABLE_PLATFORM_HEVC) - -// Only decode preload=metadata elements upon visibility. -// TODO(crbug.com/879406): Remove this after M76 ships to stable -const base::Feature kPreloadMetadataLazyLoad{"PreloadMetadataLazyLoad", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Let videos be resumed via remote controls (for example, the notification) -// when in background. -const base::Feature kResumeBackgroundVideo { - "resume-background-video", -#if BUILDFLAG(IS_ANDROID) - base::FEATURE_ENABLED_BY_DEFAULT -#else - base::FEATURE_DISABLED_BY_DEFAULT -#endif -}; - -// When enabled, MediaCapabilities will check with GPU Video Accelerator -// Factories to determine isPowerEfficient = true/false. -const base::Feature kMediaCapabilitiesQueryGpuFactories{ - "MediaCapabilitiesQueryGpuFactories", base::FEATURE_ENABLED_BY_DEFAULT}; - -// Enable Media Capabilities with finch-parameters. -const base::Feature kMediaCapabilitiesWithParameters{ - "MediaCapabilitiesWithParameters", base::FEATURE_ENABLED_BY_DEFAULT}; - -// Used to set a few tunable parameters for the WebRTC Media Capabilities -// implementation. -const base::Feature kWebrtcMediaCapabilitiesParameters{ - "WebrtcMediaCapabilitiesParameters", base::FEATURE_ENABLED_BY_DEFAULT}; - -// Display the Cast overlay button on the media controls. -const base::Feature kMediaCastOverlayButton{"MediaCastOverlayButton", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Use AndroidOverlay only if required for secure video playback. This requires -// that |kOverlayFullscreenVideo| is true, else it is ignored. -const base::Feature kUseAndroidOverlayForSecureOnly{ - "UseAndroidOverlayForSecureOnly", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Allows usage of OS-level (platform) audio encoders. -const base::Feature kPlatformAudioEncoder { - "PlatformAudioEncoder", -#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) - base::FEATURE_ENABLED_BY_DEFAULT -#else - base::FEATURE_DISABLED_BY_DEFAULT -#endif -}; - -// If enabled, RTCVideoDecoderAdapter will wrap a DecoderStream as a video -// decoder, rather than using MojoVideoDecoder. This causes the RTC external -// decoder to have all the decoder selection / fallback/forward logic of the -// non-RTC pipeline. -// TODO(liberato): This also causes the external decoder to use software -// decoding sometimes, which changes the interpretation of "ExternalDecoder". -const base::Feature kUseDecoderStreamForWebRTC{ - "UseDecoderStreamForWebRTC", base::FEATURE_DISABLED_BY_DEFAULT}; - -// If enabled, when RTCVideoDecoderAdapter is used then SW decoders will be -// exposed directly to WebRTC. -const base::Feature kExposeSwDecodersToWebRTC{ - "ExposeSwDecodersToWebRTC", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Let video without audio be paused when it is playing in the background. -const base::Feature kBackgroundVideoPauseOptimization{ - "BackgroundVideoPauseOptimization", base::FEATURE_ENABLED_BY_DEFAULT}; - -// CDM host verification is enabled by default. Can be disabled for testing. -// Has no effect if ENABLE_CDM_HOST_VERIFICATION buildflag is false. -const base::Feature kCdmHostVerification{"CdmHostVerification", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Use per-CDM-type, per-user and per-site CDM processes (for library CDM). If -// disabled, the CDM processes are only per-CDM-type, meaning different sites -// using the same CDM type would share one CDM process. -const base::Feature kCdmProcessSiteIsolation{"CdmProcessSiteIsolation", - base::FEATURE_ENABLED_BY_DEFAULT}; - -#if BUILDFLAG(CHROME_WIDE_ECHO_CANCELLATION) -// If echo cancellation for a mic signal is requested, mix and cancel all audio -// playback going to a specific output device in the audio service. -const base::Feature kChromeWideEchoCancellation{ - "ChromeWideEchoCancellation", base::FEATURE_DISABLED_BY_DEFAULT}; - -// If non-zero, audio processing is done on a dedicated processing thread which -// receives audio from the audio capture thread via a fifo of a specified size. -// Zero fifo size means the usage of such processing thread is disabled and -// processing is done on the audio capture thread itself. -const base::FeatureParam kChromeWideEchoCancellationProcessingFifoSize{ - &kChromeWideEchoCancellation, "processing_fifo_size", 0}; - -// When audio processing is done in the audio process, at the renderer side IPC -// is set up to receive audio at the processing sample rate. This is a -// kill-switch to fallback to receiving audio at the default sample rate of the -// audio capture device. -const base::FeatureParam kChromeWideEchoCancellationMinimizeResampling{ - &kChromeWideEchoCancellation, "minimize_resampling", true}; - -// Dynamically sets audio::SyncReader's timeout based off of a percentage of -// buffer duration, in an attempt to minimize glitches. -// The default negative value indicates that no experiment is running, and -// we shouldn't use a mixing specific timeout value. -// If the similar kDynamicAudioTimeout feature is enabled and this value is set, -// this parameter will override kDynamicAudioTimeout values when we are mixing. -const base::FeatureParam - kChromeWideEchoCancellationDynamicMixingTimeout{ - &kChromeWideEchoCancellation, "mixing_buffer_duration_percent", -1.0}; - -// Allows all sample rates to be used for audio processing. If disabled, only -// sample rates divisible by 100 are allowed; a request for a media stream with -// enabled audio processing will fail otherwise. For context see -// https://crbug.com/1332484. -const base::FeatureParam kChromeWideEchoCancellationAllowAllSampleRates{ - &kChromeWideEchoCancellation, "allow_all_sample_rates", true}; -#endif - -// Make MSE garbage collection algorithm more aggressive when we are under -// moderate or critical memory pressure. This will relieve memory pressure by -// releasing stale data from MSE buffers. -const base::Feature kMemoryPressureBasedSourceBufferGC{ - "MemoryPressureBasedSourceBufferGC", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Enable binding multiple shared images to a single GpuMemoryBuffer for video -// frames created by video capture. -const base::Feature kMultiPlaneVideoCaptureSharedImages { - "MultiPlaneVideoCaptureSharedImages", -#if BUILDFLAG(IS_MAC) - base::FEATURE_ENABLED_BY_DEFAULT -#else - base::FEATURE_DISABLED_BY_DEFAULT -#endif -}; - -// Controls whether the Open Screen libcast SenderSession is used for -// initializing and managing streaming sessions, or the legacy implementation. -const base::Feature kOpenscreenCastStreamingSession{ - "OpenscreenCastStreamingSession", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Approach original pre-REC MSE object URL autorevoking behavior, though await -// actual attempt to use the object URL for attachment to perform revocation. -// This will hopefully reduce runtime memory bloat for pages that do not -// explicitly detach their HTMLME+MSE object collections nor explicitly revoke -// the object URLs used to attach HTMLME+MSE. When disabled, revocation only -// occurs when application explicitly revokes the object URL, or upon the -// execution context teardown for the MediaSource object. When enabled, -// revocation occurs upon successful start of attachment of HTMLME to the object -// URL. Note, rather than immediately scheduling a task to revoke upon the URL's -// creation, as at least one other browser does and the original File API -// pattern used to follow, this delay until attachment start enables new -// scenarios that could use the object URL for attaching HTMLME+MSE cross-thread -// (MSE-in-workers), where there could be significant delay between the worker -// thread creation of the object URL and the main thread usage of the object URL -// for starting attachment to HTMLME. -const base::Feature kRevokeMediaSourceObjectURLOnAttach{ - "RevokeMediaSourceObjectURLOnAttach", base::FEATURE_ENABLED_BY_DEFAULT}; - -const base::Feature kD3D11VideoDecoderUseSharedHandle{ - "D3D11VideoDecoderUseSharedHandle", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Falls back to other decoders after audio/video decode error happens. The -// implementation may choose different strategies on when to fallback. See -// DecoderStream for details. When disabled, playback will fail immediately -// after a decode error happens. This can be useful in debugging and testing -// because the behavior is simpler and more predictable. -const base::Feature kFallbackAfterDecodeError{"FallbackAfterDecodeError", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Use Gav1VideoDecoder to decode AV1 streams. -const base::Feature kGav1VideoDecoder{"Gav1VideoDecoder", - base::FEATURE_DISABLED_BY_DEFAULT}; - -// Show toolbar button that opens dialog for controlling media sessions. -const base::Feature kGlobalMediaControls { - "GlobalMediaControls", -#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) - base::FEATURE_ENABLED_BY_DEFAULT -#else - base::FEATURE_DISABLED_BY_DEFAULT -#endif -}; - -// Auto-dismiss global media controls. -const base::Feature kGlobalMediaControlsAutoDismiss{ - "GlobalMediaControlsAutoDismiss", base::FEATURE_ENABLED_BY_DEFAULT}; - -// Allow Global Media Controls in system tray of CrOS. -const base::Feature kGlobalMediaControlsForChromeOS{ - "GlobalMediaControlsForChromeOS", base::FEATURE_ENABLED_BY_DEFAULT}; - -constexpr base::FeatureParam::Option - kCrosGlobalMediaControlsParamOptions[] = { - {kCrosGlobalMediaControlsPinOptions::kPin, "default-pinned"}, - {kCrosGlobalMediaControlsPinOptions::kNotPin, "default-unpinned"}, - {kCrosGlobalMediaControlsPinOptions::kHeuristic, "heuristic"}}; - -constexpr base::FeatureParam - kCrosGlobalMediaControlsPinParam( - &kGlobalMediaControlsForChromeOS, - "CrosGlobalMediaControlsPinParam", - kCrosGlobalMediaControlsPinOptions::kHeuristic, - &kCrosGlobalMediaControlsParamOptions); - -// Show picture-in-picture button in Global Media Controls. -const base::Feature kGlobalMediaControlsPictureInPicture { - "GlobalMediaControlsPictureInPicture", -#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ - BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_CHROMEOS_LACROS) - base::FEATURE_ENABLED_BY_DEFAULT -#else - base::FEATURE_DISABLED_BY_DEFAULT -#endif -}; - -// Enable selection of audio output device in Global Media Controls. -const base::Feature kGlobalMediaControlsSeamlessTransfer{ - "GlobalMediaControlsSeamlessTransfer", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Enable an updated version of the Global Media Controls UI. -const base::Feature kGlobalMediaControlsModernUI{ - "GlobalMediaControlsModernUI", base::FEATURE_DISABLED_BY_DEFAULT}; - -// CanPlayThrough issued according to standard. -const base::Feature kSpecCompliantCanPlayThrough{ - "SpecCompliantCanPlayThrough", base::FEATURE_ENABLED_BY_DEFAULT}; - -// Disables the real audio output stream after silent audio has been delivered -// for too long. Should save quite a bit of power in the muted video case. -const base::Feature kSuspendMutedAudio{"SuspendMutedAudio", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Enables using the media history store to store media engagement metrics. -const base::Feature kUseMediaHistoryStore{"UseMediaHistoryStore", - base::FEATURE_DISABLED_BY_DEFAULT}; - -// Use R16 texture for 9-16 bit channel instead of half-float conversion by CPU. -const base::Feature kUseR16Texture{"use-r16-texture", - base::FEATURE_DISABLED_BY_DEFAULT}; - -// Enables the Unified Autoplay policy by overriding the platform's default -// autoplay policy. -const base::Feature kUnifiedAutoplay{"UnifiedAutoplay", - base::FEATURE_ENABLED_BY_DEFAULT}; - -#if BUILDFLAG(IS_LINUX) -// Enable vaapi video decoding on linux. This is already enabled by default on -// chromeos, but needs an experiment on linux. -const base::Feature kVaapiVideoDecodeLinux{"VaapiVideoDecoder", - base::FEATURE_ENABLED_BY_DEFAULT}; - -const base::Feature kVaapiVideoEncodeLinux{"VaapiVideoEncoder", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Ignore the non-intel driver blacklist for VaapiVideoDecoder implementations. -// Intended for manual usage only in order to gague the status of newer driver -// implementations. -const base::Feature kVaapiIgnoreDriverChecks{"VaapiIgnoreDriverChecks", - base::FEATURE_ENABLED_BY_DEFAULT}; -#endif // BUILDFLAG(IS_LINUX) - -// Enable VA-API hardware decode acceleration for AV1. -const base::Feature kVaapiAV1Decoder{"VaapiAV1Decoder", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Enable VA-API hardware low power encoder for all codecs on intel Gen9x gpu. -const base::Feature kVaapiLowPowerEncoderGen9x{ - "VaapiLowPowerEncoderGen9x", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Reject creation of encode/decode VAContexts when the requested resolution is -// outside the enumerated minimum and maximum. TODO(b/171041334): Remove and -// enable by default once the ARC++ hw codecs issue is fixed. -const base::Feature kVaapiEnforceVideoMinMaxResolution{ - "VaapiEnforceVideoMinMaxResolution", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Ensure the advertised minimum supported resolution is larger than or equal to -// a given one (likely QVGA + 1) for certain codecs/modes and platforms, for -// performance reasons. This does not affect JPEG decoding. -const base::Feature kVaapiVideoMinResolutionForPerformance{ - "VaapiVideoMinResolutionForPerformance", base::FEATURE_ENABLED_BY_DEFAULT}; - -// Enable VA-API hardware encode acceleration for VP8. -const base::Feature kVaapiVP8Encoder{"VaapiVP8Encoder", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Enable VA-API hardware encode acceleration for VP9. -const base::Feature kVaapiVP9Encoder{"VaapiVP9Encoder", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Enable global VA-API lock. Disable this to use lock-free VA-API function -// calls for thread safe backends. -const base::Feature kGlobalVaapiLock{"GlobalVaapiLock", - base::FEATURE_ENABLED_BY_DEFAULT}; - -#if defined(ARCH_CPU_X86_FAMILY) && BUILDFLAG(IS_CHROMEOS) -// TODO(b/214589754): revisit the need for the BUILDFLAG(IS_CHROMEOS) guard (as -// opposed to BUILDFLAG(IS_CHROMEOS_ASH)) when the final design for HW -// encoding is implemented for lacros-chrome. -// Enable H264 temporal layer encoding with HW encoder on ChromeOS. -const base::Feature kVaapiH264TemporalLayerHWEncoding{ - "VaapiH264TemporalLayerEncoding", base::FEATURE_ENABLED_BY_DEFAULT}; -// Enable VP8 temporal layer encoding with HW encoder on ChromeOS. -const base::Feature kVaapiVp8TemporalLayerHWEncoding{ - "VaapiVp8TemporalLayerEncoding", base::FEATURE_DISABLED_BY_DEFAULT}; -// Enable VP9 k-SVC encoding with HW encoder for webrtc use case on ChromeOS. -const base::Feature kVaapiVp9kSVCHWEncoding{"VaapiVp9kSVCHWEncoding", - base::FEATURE_ENABLED_BY_DEFAULT}; -#endif // defined(ARCH_CPU_X86_FAMILY) && BUILDFLAG(IS_CHROMEOS) - -// Inform video blitter of video color space. -const base::Feature kVideoBlitColorAccuracy{"video-blit-color-accuracy", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Enable VP9 k-SVC decoding with HW decoder for webrtc use case. -const base::Feature kVp9kSVCHWDecoding { - "Vp9kSVCHWDecoding", -// TODO(crbug.com/1325698): Remove defined(ARCH_CPU_X86_FAMILY) once this is -// enabled by default on ChromeOS ARM devices. -#if defined(ARCH_CPU_X86_FAMILY) && BUILDFLAG(IS_CHROMEOS) - base::FEATURE_ENABLED_BY_DEFAULT -#else - base::FEATURE_ENABLED_BY_DEFAULT -#endif -}; - -// Takes a reference on a video frame, keeping it alive during the duration of a -// video.requestVideoFrameCallback call. Doesn't change anything to the API for -// now, as this is only used to measure the potential impact of keeping frames -// alive for longer. See crbug.com/1259784. -const base::Feature kKeepRvfcFrameAlive{"keep-rvfc-frame-alive", - base::FEATURE_DISABLED_BY_DEFAULT}; - -// Enables support for External Clear Key (ECK) key system for testing on -// supported platforms. On platforms that do not support ECK, this feature has -// no effect. -const base::Feature kExternalClearKeyForTesting{ - "ExternalClearKeyForTesting", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Enables the Live Caption feature on supported devices. -const base::Feature kLiveCaption{"LiveCaption", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Controls whether a "Share this tab instead" button should be shown for -// getDisplayMedia captures. Note: This flag does not control if the "Share this -// tab instead" button is shown for chrome.desktopCapture captures. -const base::Feature kShareThisTabInsteadButtonGetDisplayMedia{ - "ShareThisTabInsteadButtonGetDisplayMedia", - base::FEATURE_DISABLED_BY_DEFAULT}; - -// If kShareThisTabInsteadButtonGetDisplayMedia is ENABLED, this flag controls -// whether a "Share this tab instead" button should be enabled for -// getDisplayMedia captures with audio. -// If kShareThisTabInsteadButtonGetDisplayMedia is DISABLED, this flag has no -// effect. -// Note: This flag does not control if the "Share this tab instead" button is -// shown for chrome.desktopCapture captures. -const base::Feature kShareThisTabInsteadButtonGetDisplayMediaAudio{ - "ShareThisTabInsteadButtonGetDisplayMediaAudio", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Enable the Speaker Change Detection feature, which inserts a line break when -// the Speech On-Device API (SODA) detects a speaker change. -const base::Feature kSpeakerChangeDetection{"SpeakerChangeDetection", - base::FEATURE_DISABLED_BY_DEFAULT}; - -// Live Caption can be used in multiple languages, as opposed to just English. -const base::Feature kLiveCaptionMultiLanguage{ - "LiveCaptionMultiLanguage", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Live Caption runs system-wide on ChromeOS, as opposed to just in the browser. -const base::Feature kLiveCaptionSystemWideOnChromeOS{ - "LiveCaptionSystemWideOnChromeOS", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Prevents UrlProvisionFetcher from making a provisioning request. If -// specified, any provisioning request made will not be sent to the provisioning -// server, and the response will indicate a failure to communicate with the -// provisioning server. -const base::Feature kFailUrlProvisionFetcherForTesting{ - "FailUrlProvisionFetcherForTesting", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Enables hardware secure decryption if supported by hardware and CDM. -// TODO(xhwang): Currently this is only used for development of new features. -// Apply this to Android and ChromeOS as well where hardware secure decryption -// is already available. -const base::Feature kHardwareSecureDecryption{ - "HardwareSecureDecryption", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Same as `kHardwareSecureDecryption` above, but only enable experimental -// sub key systems. Which sub key system is experimental is key system specific. -const base::Feature kHardwareSecureDecryptionExperiment{ - "HardwareSecureDecryptionExperiment", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Allows automatically disabling hardware secure Content Decryption Module -// (CDM) after failures or crashes to fallback to software secure CDMs. If this -// feature is disabled, the fallback will never happen and users could be stuck -// in playback failures. -const base::Feature kHardwareSecureDecryptionFallback{ - "HardwareSecureDecryptionFallback", base::FEATURE_ENABLED_BY_DEFAULT}; - -const base::Feature kWakeLockOptimisationHiddenMuted{ - "kWakeLockOptimisationHiddenMuted", base::FEATURE_ENABLED_BY_DEFAULT}; - -// If active, enable HiDPI mode that increases the display scale factor -// while capturing a low-resolution tab. -const base::Feature kWebContentsCaptureHiDpi{"WebContentsCaptureHiDPI", - base::FEATURE_DISABLED_BY_DEFAULT}; - -// Enables handling of hardware media keys for controlling media. -const base::Feature kHardwareMediaKeyHandling { - "HardwareMediaKeyHandling", -#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || \ - BUILDFLAG(USE_MPRIS) - base::FEATURE_ENABLED_BY_DEFAULT -#else - base::FEATURE_DISABLED_BY_DEFAULT -#endif -}; - -// Enables a platform-specific resolution cutoff for prioritizing platform -// decoders over software decoders or vice-versa. -const base::Feature kResolutionBasedDecoderPriority{ - "ResolutionBasedDecoderPriority", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Forces use of hardware (platform) video decoders in -// `media::DecoderSelector`. -const base::Feature kForceHardwareVideoDecoders{ - "ForceHardwareVideoDecoders", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Forces use of hardware (platform) audio decoders in -// `media::DecoderSelector`. -const base::Feature kForceHardwareAudioDecoders{ - "ForceHardwareAudioDecoders", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Enables low-delay video rendering in media pipeline on "live" stream. -const base::Feature kLowDelayVideoRenderingOnLiveStream{ - "low-delay-video-rendering-on-live-stream", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Whether the autoplay policy should ignore Web Audio. When ignored, the -// autoplay policy will be hardcoded to be the legacy one on based on the -// platform -const base::Feature kAutoplayIgnoreWebAudio{"AutoplayIgnoreWebAudio", - base::FEATURE_DISABLED_BY_DEFAULT}; - -// Whether we should show a setting to disable autoplay policy. -const base::Feature kAutoplayDisableSettings{"AutoplayDisableSettings", - base::FEATURE_DISABLED_BY_DEFAULT}; - -#if BUILDFLAG(IS_ANDROID) -// Should we allow video playback to use an overlay if it's not needed for -// security? Normally, we'd always want to allow this, except as part of the -// power testing A/B experiment. https://crbug.com/1081346 . -const base::Feature kAllowNonSecureOverlays{"AllowNonSecureOverlays", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Enable a gesture to make the media controls expaned into the display cutout. -// TODO(beccahughes): Remove this. -const base::Feature kMediaControlsExpandGesture{ - "MediaControlsExpandGesture", base::FEATURE_ENABLED_BY_DEFAULT}; - -// An experimental feature to enable persistent-license type support in MediaDrm -// when using Encrypted Media Extensions (EME) API. -// TODO(xhwang): Remove this after feature launch. See http://crbug.com/493521 -const base::Feature kMediaDrmPersistentLicense{ - "MediaDrmPersistentLicense", base::FEATURE_ENABLED_BY_DEFAULT}; - -// Enables MediaDrmOriginIdManager to provide preprovisioned origin IDs for -// MediaDrmBridge. If disabled, MediaDrmBridge will get unprovisioned origin IDs -// which will trigger provisioning process after MediaDrmBridge is created. -const base::Feature kMediaDrmPreprovisioning{"MediaDrmPreprovisioning", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Determines if MediaDrmOriginIdManager should attempt to pre-provision origin -// IDs at startup (whenever a profile is loaded). Also used by tests that -// disable it so that the tests can setup before pre-provisioning is done. -// Note: Has no effect if kMediaDrmPreprovisioning feature is disabled. -const base::Feature kMediaDrmPreprovisioningAtStartup{ - "MediaDrmPreprovisioningAtStartup", base::FEATURE_ENABLED_BY_DEFAULT}; - -// Enable picture in picture web api for android. -const base::Feature kPictureInPictureAPI{"PictureInPictureAPI", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Enables CanPlayType() (and other queries) for HLS MIME types. Note that -// disabling this also causes navigation to .m3u8 files to trigger downloading -// instead of playback. -const base::Feature kCanPlayHls{"CanPlayHls", base::FEATURE_ENABLED_BY_DEFAULT}; - -// Enables the use of MediaPlayerRenderer for HLS playback. When disabled, -// HLS manifests will fail to load (triggering source fallback or load error). -const base::Feature kHlsPlayer{"HlsPlayer", base::FEATURE_ENABLED_BY_DEFAULT}; - -// When enabled, Playing media sessions will request audio focus from the -// Android system. -const base::Feature kRequestSystemAudioFocus{"RequestSystemAudioFocus", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Use the (hacky) AudioManager.getOutputLatency() call to get the estimated -// hardware latency for a stream for OpenSLES playback. This is normally not -// needed, except for some Android TV devices. -const base::Feature kUseAudioLatencyFromHAL{"UseAudioLatencyFromHAL", - base::FEATURE_DISABLED_BY_DEFAULT}; - -// Enable pooling of SharedImageVideo objects for use by MCVD, to save a hop to -// the GPU main thread during VideoFrame construction. -const base::Feature kUsePooledSharedImageVideoProvider{ - "UsePooledSharedImageVideoProvider", base::FEATURE_ENABLED_BY_DEFAULT}; - -// Historically we hardcoded sRGB for color space. This flags controls if we -// pass real color space to VideoFrame/SharedImages. -const base::Feature kUseRealColorSpaceForAndroidVideo{ - "UseRealColorSpaceForAndroidVideo", base::FEATURE_DISABLED_BY_DEFAULT}; - -#endif // BUILDFLAG(IS_ANDROID) - -#if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION) -// Enable Variable Bitrate encoding with hardware accelerated encoders on -// ChromeOS. -const base::Feature kChromeOSHWVBREncoding{"ChromeOSHWVBREncoding", - base::FEATURE_DISABLED_BY_DEFAULT}; - -// Enable the hardware-accelerated direct video decoder instead of the one -// needing the VdaVideoDecoder adapter. This flag is used mainly as a -// chrome:flag for developers debugging issues as well as to be able to -// experiment with direct VideoDecoder path on Linux Desktop. -// TODO(b/159825227): remove when the direct video decoder is fully launched. -const base::Feature kUseChromeOSDirectVideoDecoder{ - "UseChromeOSDirectVideoDecoder", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Limit the number of concurrent hardware decoder instances on ChromeOS. -const base::Feature kLimitConcurrentDecoderInstances{ - "LimitConcurrentDecoderInstances", base::FEATURE_ENABLED_BY_DEFAULT}; - -#if defined(ARCH_CPU_ARM_FAMILY) -// Some architectures have separate image processor hardware that -// can be used by Chromium's ImageProcessor to color convert/crop/etc. -// video buffers. Sometimes it is more efficient/performant/correct -// to use libYUV instead of the hardware to do this processing. -const base::Feature kPreferLibYuvImageProcessor{ - "PreferLibYUVImageProcessor", base::FEATURE_DISABLED_BY_DEFAULT}; -#endif // defined(ARCH_CPU_ARM_FAMILY) -#if BUILDFLAG(IS_CHROMEOS) -// ChromeOS has one of two VideoDecoder implementations active based on -// SoC/board specific configurations that are sent via command line flags. This -// switch allows using the non default implementation for testing. -// TODO(b/159825227): remove when the "old" video decoder is fully launched. -const base::Feature kUseAlternateVideoDecoderImplementation{ - "UseAlternateVideoDecoderImplementation", - base::FEATURE_DISABLED_BY_DEFAULT}; -#endif // BUILDFLAG(IS_CHROMEOS) -#endif // BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION) - -#if BUILDFLAG(IS_MAC) -// Enable binding multiple shared images to a single GpuMemoryBuffer for -// accelerated video decode using VideoToolbox. -const base::Feature kMultiPlaneVideoToolboxSharedImages{ - "MultiPlaneVideoToolboxSharedImages", base::FEATURE_ENABLED_BY_DEFAULT}; -#endif // BUILDFLAG(IS_MAC) - -#if BUILDFLAG(IS_WIN) -// Does NV12->NV12 video copy on the main thread right before the texture's -// used by GL. -const base::Feature kDelayCopyNV12Textures{"DelayCopyNV12Textures", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Enables DirectShow GetPhotoState implementation -// Created to act as a kill switch by disabling it, in the case of the -// resurgence of https://crbug.com/722038 -const base::Feature kDirectShowGetPhotoState{"DirectShowGetPhotoState", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Includes Infrared cameras in the list returned for EnumerateDevices() on -// Windows. -const base::Feature kIncludeIRCamerasInDeviceEnumeration{ - "IncludeIRCamerasInDeviceEnumeration", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Enables AV1 encode acceleration for Windows. -const base::Feature MEDIA_EXPORT kMediaFoundationAV1Encoding{ - "MediaFoundationAV1Encoding", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Enables H.264 CBP encode acceleration for Windows. -// For feature check of kMediaFoundationH264CbpEncoding at runtime, -// please use IsMediaFoundationH264CbpEncodingEnabled() instead. -const base::Feature MEDIA_EXPORT kMediaFoundationH264CbpEncoding{ - "MediaFoundationH264CbpEncoding", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Enables VP9 encode acceleration for Windows. -const base::Feature MEDIA_EXPORT kMediaFoundationVP9Encoding{ - "MediaFoundationVP9Encoding", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Enables MediaFoundation based video capture -const base::Feature kMediaFoundationVideoCapture{ - "MediaFoundationVideoCapture", base::FEATURE_ENABLED_BY_DEFAULT}; - -// Enables MediaFoundation based video capture with D3D11 -// For feature check of kMediaFoundationD3D11VideoCapture at runtime, -// please use IsMediaFoundationD3D11VideoCaptureEnabled() instead. -const base::Feature kMediaFoundationD3D11VideoCapture{ - "MediaFoundationD3D11VideoCapture", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Enables VP8 decode acceleration for Windows. -const base::Feature MEDIA_EXPORT kMediaFoundationVP8Decoding{ - "MediaFoundationVP8Decoding", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Enables the use of MediaFoundationRenderer for clear content on supported -// systems. -const base::Feature kMediaFoundationClearPlayback{ - "MediaFoundationClearPlayback", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Use the AUDCLNT_STREAMOPTIONS_RAW option on WASAPI input audio streams in -// combination with the IAudioClient2::SetClientProperties() API. -// The audio stream is a 'raw' stream that bypasses all signal processing except -// for endpoint specific, always-on processing in the Audio Processing Object -// (APO), driver, and hardware. -// https://docs.microsoft.com/en-us/windows/win32/api/audioclient/ne-audioclient-audclnt_streamoptions -const base::Feature MEDIA_EXPORT kWasapiRawAudioCapture{ - "WASAPIRawAudioCapture", base::FEATURE_ENABLED_BY_DEFAULT}; - -// Enable VP9 kSVC decoding with HW decoder for webrtc use case on Windows. -const base::Feature kD3D11Vp9kSVCHWDecoding{"D3D11Vp9kSVCHWDecoding", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// The Media Foundation Rendering Strategy determines which presentation mode -// Media Foundation Renderer should use for presenting clear content. This -// strategy has no impact for protected content, which must always use Direct -// Composition. -// -// The strategy may be one of the following options: -// 1.) Direct Composition: Media Foundation Renderer will use a Windowsless -// Swapchain to present directly to a Direct Composition surface. -// 2.) Frame Server: Media Foundation Renderer will produce Video Frames that -// may be passed through the Chromium video frame rendering pipeline. -// 3.) Dynamic: Media Foundation Renderer may freely switch between Direct -// Composition & Frame Server mode based on the current operating -// conditions. -// -// Command line invocation: -// --enable-features=MediaFoundationClearRendering:strategy/direct-composition -// --enable-features=MediaFoundationClearRendering:strategy/frame-server -// --enable-features=MediaFoundationClearRendering:strategy/dynamic -const base::Feature kMediaFoundationClearRendering = { - "MediaFoundationClearRendering", base::FEATURE_ENABLED_BY_DEFAULT}; - -constexpr base::FeatureParam::Option - kMediaFoundationClearRenderingStrategyOptions[] = { - {MediaFoundationClearRenderingStrategy::kDirectComposition, - "direct-composition"}, - {MediaFoundationClearRenderingStrategy::kFrameServer, "frame-server"}, - {MediaFoundationClearRenderingStrategy::kDynamic, "dynamic"}}; - -// TODO(crbug.com/1321817, wicarr): Media Foundation for Clear should operate in -// dynamic mode by default. However due to a bug with dual adapters when using -// Frame Serve mode we currently start in Direct Composition mode. -const base::FeatureParam - kMediaFoundationClearRenderingStrategyParam{ - &kMediaFoundationClearRendering, "strategy", - MediaFoundationClearRenderingStrategy::kDirectComposition, - &kMediaFoundationClearRenderingStrategyOptions}; -#endif // BUILDFLAG(IS_WIN) - -#if BUILDFLAG(IS_CHROMEOS) -const base::Feature MEDIA_EXPORT kDeprecateLowUsageCodecs{ - "DeprecateLowUsageCodecs", base::FEATURE_DISABLED_BY_DEFAULT}; -#endif // BUILDFLAG(IS_CHROMEOS) - -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) -// Spawn utility processes to perform hardware decode acceleration instead of -// using the GPU process. -const base::Feature MEDIA_EXPORT kUseOutOfProcessVideoDecoding{ - "UseOutOfProcessVideoDecoding", base::FEATURE_DISABLED_BY_DEFAULT}; -#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) - -std::string GetEffectiveAutoplayPolicy(const base::CommandLine& command_line) { - // Return the autoplay policy set in the command line, if any. - if (command_line.HasSwitch(switches::kAutoplayPolicy)) - return command_line.GetSwitchValueASCII(switches::kAutoplayPolicy); - - if (base::FeatureList::IsEnabled(media::kUnifiedAutoplay)) - return switches::autoplay::kDocumentUserActivationRequiredPolicy; - - if (base::CommandLine::ForCurrentProcess()->HasSwitch("no-autoplay")) - return switches::autoplay::kUserGestureRequiredPolicy; - -// The default value is platform dependent. -#if BUILDFLAG(IS_ANDROID) - return switches::autoplay::kUserGestureRequiredPolicy; -#else - return switches::autoplay::kNoUserGestureRequiredPolicy; -#endif -} - -// Enables Media Engagement Index recording. This data will be used to determine -// when to bypass autoplay policies. This is recorded on all platforms. -const base::Feature kRecordMediaEngagementScores{ - "RecordMediaEngagementScores", base::FEATURE_ENABLED_BY_DEFAULT}; - -// Enables Media Engagement Index recording for Web Audio playbacks. -const base::Feature kRecordWebAudioEngagement{"RecordWebAudioEngagement", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// The following Media Engagement flags are not enabled on mobile platforms: -// - MediaEngagementBypassAutoplayPolicies: enables the Media Engagement Index -// data to be esude to override autoplay policies. An origin with a high MEI -// will be allowed to autoplay. -// - PreloadMediaEngagementData: enables a list of origins to be considered as -// having a high MEI until there is enough local data to determine the user's -// preferred behaviour. -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) -const base::Feature kMediaEngagementBypassAutoplayPolicies{ - "MediaEngagementBypassAutoplayPolicies", base::FEATURE_DISABLED_BY_DEFAULT}; -const base::Feature kPreloadMediaEngagementData{ - "PreloadMediaEngagementData", base::FEATURE_DISABLED_BY_DEFAULT}; -#else -const base::Feature kMediaEngagementBypassAutoplayPolicies{ - "MediaEngagementBypassAutoplayPolicies", base::FEATURE_ENABLED_BY_DEFAULT}; -const base::Feature kPreloadMediaEngagementData{ - "PreloadMediaEngagementData", base::FEATURE_ENABLED_BY_DEFAULT}; -#endif - -const base::Feature kMediaEngagementHTTPSOnly{ - "MediaEngagementHTTPSOnly", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Enables experimental local learning for media. Used in the context of media -// capabilities only. Adds reporting only; does not change media behavior. -const base::Feature kMediaLearningExperiment{"MediaLearningExperiment", - base::FEATURE_DISABLED_BY_DEFAULT}; - -// Enables the general purpose media machine learning framework. Adds reporting -// only; does not change media behavior. -const base::Feature kMediaLearningFramework{"MediaLearningFramework", - base::FEATURE_DISABLED_BY_DEFAULT}; - -// Enables the smoothness prediction experiment. Requires -// kMediaLearningFramework to be enabled also, else it does nothing. -const base::Feature kMediaLearningSmoothnessExperiment{ - "MediaLearningSmoothnessExperiment", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Enable the prototype global optimization of tuneables via finch. See -// media/base/tuneable.h for how to create tuneable parameters. -const base::Feature kMediaOptimizer{"JointMediaOptimizer", - base::FEATURE_DISABLED_BY_DEFAULT}; - -// Enable aggregate power measurement for media playback. -const base::Feature kMediaPowerExperiment{"MediaPowerExperiment", - base::FEATURE_DISABLED_BY_DEFAULT}; - -// Enable WebRTC actions for the Media Session API. -const base::Feature kMediaSessionWebRTC{"MediaSessionWebRTC", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Enables flash to be ducked by audio focus. This is enabled on Chrome OS which -// has audio focus enabled. -const base::Feature kAudioFocusDuckFlash { - "AudioFocusDuckFlash", -#if BUILDFLAG(IS_CHROMEOS_ASH) - base::FEATURE_ENABLED_BY_DEFAULT -#else - base::FEATURE_DISABLED_BY_DEFAULT -#endif -}; - -// Only affects Android. Suspends a media session when audio focus is lost; when -// this setting is disabled, an Android media session will not be suspended when -// Audio focus is lost. This is used by Cast which sometimes needs to drive -// multiple media sessions. -const base::Feature kAudioFocusLossSuspendMediaSession{ - "AudioFocusMediaSession", base::FEATURE_ENABLED_BY_DEFAULT}; - -// Enables the internal Media Session logic without enabling the Media Session -// service. -const base::Feature kInternalMediaSession { - "InternalMediaSession", -#if BUILDFLAG(IS_ANDROID) - base::FEATURE_ENABLED_BY_DEFAULT -#else - base::FEATURE_DISABLED_BY_DEFAULT -#endif -}; - -// Keypress detection which serves as input to noise suppression methods -// in WebRTC clients. This functionality is enabled by default but it can be -// disabled experemantally by using --disable-features=KeyPressMonitoring. -const base::Feature kKeyPressMonitoring{"KeyPressMonitoring", - base::FEATURE_ENABLED_BY_DEFAULT}; - -const base::Feature kUseFakeDeviceForMediaStream{ - "use-fake-device-for-media-stream", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Makes VideoCadenceEstimator use Bresenham-like algorithm for frame cadence -// estimations. -const base::Feature kBresenhamCadence{"BresenhamCadence", - base::FEATURE_DISABLED_BY_DEFAULT}; - -bool IsChromeWideEchoCancellationEnabled() { -#if BUILDFLAG(CHROME_WIDE_ECHO_CANCELLATION) - return base::FeatureList::IsEnabled(kChromeWideEchoCancellation); -#else - return false; -#endif -} - -bool IsHardwareSecureDecryptionEnabled() { - return base::FeatureList::IsEnabled(kHardwareSecureDecryption) || - base::FeatureList::IsEnabled(kHardwareSecureDecryptionExperiment); -} - -bool IsVideoCaptureAcceleratedJpegDecodingEnabled() { - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableAcceleratedMjpegDecode)) { - return false; - } - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kUseFakeMjpegDecodeAccelerator)) { - return true; - } -#if BUILDFLAG(IS_CHROMEOS_ASH) - return true; -#else - return false; -#endif -} - -#if BUILDFLAG(IS_WIN) -bool IsMediaFoundationH264CbpEncodingEnabled() { - return base::FeatureList::IsEnabled(kMediaFoundationH264CbpEncoding); -} - -bool IsMediaFoundationD3D11VideoCaptureEnabled() { - return base::FeatureList::IsEnabled(kMediaFoundationD3D11VideoCapture); -} -#endif - -} // namespace media diff --git a/media/gpu/gpu_video_decode_accelerator_factory.cc b/media/gpu/gpu_video_decode_accelerator_factory.cc deleted file mode 100644 index bbb10729..00000000 --- a/media/gpu/gpu_video_decode_accelerator_factory.cc +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright 2022 The Chromium Authors and Alex313031. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "media/gpu/gpu_video_decode_accelerator_factory.h" - -#include - -#include "base/memory/ptr_util.h" -#include "base/threading/thread_task_runner_handle.h" -#include "build/build_config.h" -#include "gpu/config/gpu_preferences.h" -#include "media/base/media_switches.h" -#include "media/gpu/buildflags.h" -#include "media/gpu/gpu_video_accelerator_util.h" -#include "media/gpu/macros.h" -#include "media/gpu/media_gpu_export.h" -#include "media/media_buildflags.h" - -#if BUILDFLAG(IS_WIN) -#include "base/win/windows_version.h" -#include "media/gpu/windows/dxva_video_decode_accelerator_win.h" -#endif -#if BUILDFLAG(IS_MAC) -#include "media/gpu/mac/vt_video_decode_accelerator_mac.h" -#endif -#if BUILDFLAG(USE_VAAPI) -#include "media/gpu/vaapi/vaapi_video_decode_accelerator.h" -#include "ui/gl/gl_implementation.h" -#elif BUILDFLAG(USE_V4L2_CODEC) -#include "media/gpu/v4l2/v4l2_device.h" -#include "media/gpu/v4l2/v4l2_slice_video_decode_accelerator.h" -#include "media/gpu/v4l2/v4l2_video_decode_accelerator.h" -#include "ui/gl/gl_surface_egl.h" -#endif - -namespace media { - -namespace { - -gpu::VideoDecodeAcceleratorCapabilities GetDecoderCapabilitiesInternal( - const gpu::GpuPreferences& gpu_preferences, - const gpu::GpuDriverBugWorkarounds& workarounds) { - if (gpu_preferences.disable_accelerated_video_decode) - return gpu::VideoDecodeAcceleratorCapabilities(); - - // Query VDAs for their capabilities and construct a set of supported - // profiles for current platform. This must be done in the same order as in - // CreateVDA(), as we currently preserve additional capabilities (such as - // resolutions supported) only for the first VDA supporting the given codec - // profile (instead of calculating a superset). - // TODO(posciak,henryhsu): improve this so that we choose a superset of - // resolutions and other supported profile parameters. - VideoDecodeAccelerator::Capabilities capabilities; -#if BUILDFLAG(IS_WIN) - capabilities.supported_profiles = - DXVAVideoDecodeAccelerator::GetSupportedProfiles(gpu_preferences, - workarounds); -#elif BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION) -#if BUILDFLAG(USE_VAAPI) - capabilities.supported_profiles = - VaapiVideoDecodeAccelerator::GetSupportedProfiles(); -#elif BUILDFLAG(USE_V4L2_CODEC) - GpuVideoAcceleratorUtil::InsertUniqueDecodeProfiles( - V4L2VideoDecodeAccelerator::GetSupportedProfiles(), - &capabilities.supported_profiles); - GpuVideoAcceleratorUtil::InsertUniqueDecodeProfiles( - V4L2SliceVideoDecodeAccelerator::GetSupportedProfiles(), - &capabilities.supported_profiles); -#endif -#elif BUILDFLAG(IS_MAC) - capabilities.supported_profiles = - VTVideoDecodeAccelerator::GetSupportedProfiles(workarounds); -#endif - - return GpuVideoAcceleratorUtil::ConvertMediaToGpuDecodeCapabilities( - capabilities); -} - -} // namespace - -// static -MEDIA_GPU_EXPORT std::unique_ptr -GpuVideoDecodeAcceleratorFactory::Create( - const GpuVideoDecodeGLClient& gl_client) { - return base::WrapUnique(new GpuVideoDecodeAcceleratorFactory(gl_client)); -} - -// static -MEDIA_GPU_EXPORT gpu::VideoDecodeAcceleratorCapabilities -GpuVideoDecodeAcceleratorFactory::GetDecoderCapabilities( - const gpu::GpuPreferences& gpu_preferences, - const gpu::GpuDriverBugWorkarounds& workarounds) { - // Cache the capabilities so that they will not be computed more than once per - // GPU process. It is assumed that |gpu_preferences| and |workarounds| do not - // change between calls. - // TODO(sandersd): Move cache to GpuMojoMediaClient once - // |video_decode_accelerator_capabilities| is removed from GPUInfo. - static gpu::VideoDecodeAcceleratorCapabilities capabilities = - GetDecoderCapabilitiesInternal(gpu_preferences, workarounds); - -#if BUILDFLAG(USE_V4L2_CODEC) - // V4L2-only: the decoder devices may not be visible at the time the GPU - // process is starting. If the capabilities vector is empty, try to query the - // devices again in the hope that they will have appeared in the meantime. - // TODO(crbug.com/948147): trigger query when an device add/remove event - // (e.g. via udev) has happened instead. - if (capabilities.supported_profiles.empty()) { - VLOGF(1) << "Capabilities empty, querying again..."; - capabilities = GetDecoderCapabilitiesInternal(gpu_preferences, workarounds); - } -#endif - - return capabilities; -} - -MEDIA_GPU_EXPORT std::unique_ptr -GpuVideoDecodeAcceleratorFactory::CreateVDA( - VideoDecodeAccelerator::Client* client, - const VideoDecodeAccelerator::Config& config, - const gpu::GpuDriverBugWorkarounds& workarounds, - const gpu::GpuPreferences& gpu_preferences, - MediaLog* media_log) { - DCHECK(thread_checker_.CalledOnValidThread()); - - if (gpu_preferences.disable_accelerated_video_decode) - return nullptr; - - // Array of Create..VDA() function pointers, potentially usable on current - // platform. This list is ordered by priority, from most to least preferred, - // if applicable. This list must be in the same order as the querying order - // in GetDecoderCapabilities() above. - using CreateVDAFp = std::unique_ptr ( - GpuVideoDecodeAcceleratorFactory::*)(const gpu::GpuDriverBugWorkarounds&, - const gpu::GpuPreferences&, - MediaLog* media_log) const; - const CreateVDAFp create_vda_fps[] = { -#if BUILDFLAG(IS_WIN) - &GpuVideoDecodeAcceleratorFactory::CreateDXVAVDA, -#endif - - // Usually only one of USE_VAAPI or USE_V4L2_CODEC is defined on ChromeOS, - // except for Chromeboxes with companion video acceleration chips, which have - // both. In those cases prefer the VA creation function. -#if BUILDFLAG(USE_VAAPI) - &GpuVideoDecodeAcceleratorFactory::CreateVaapiVDA, -#elif BUILDFLAG(USE_V4L2_CODEC) - &GpuVideoDecodeAcceleratorFactory::CreateV4L2VDA, - &GpuVideoDecodeAcceleratorFactory::CreateV4L2SliceVDA, -#endif - -#if BUILDFLAG(IS_MAC) - &GpuVideoDecodeAcceleratorFactory::CreateVTVDA, -#endif - }; - - std::unique_ptr vda; - - for (const auto& create_vda_function : create_vda_fps) { - vda = (this->*create_vda_function)(workarounds, gpu_preferences, media_log); - if (vda && vda->Initialize(config, client)) - return vda; - else - LOG(ERROR) << "Initialization of one or more VDAs failed."; - } - - return nullptr; -} - -#if BUILDFLAG(IS_WIN) -std::unique_ptr -GpuVideoDecodeAcceleratorFactory::CreateDXVAVDA( - const gpu::GpuDriverBugWorkarounds& workarounds, - const gpu::GpuPreferences& gpu_preferences, - MediaLog* media_log) const { - std::unique_ptr decoder; - DVLOG(0) << "Initializing DXVA HW decoder for windows."; - decoder.reset(new DXVAVideoDecodeAccelerator( - gl_client_.get_context, gl_client_.make_context_current, - gl_client_.bind_image, workarounds, gpu_preferences, media_log)); - return decoder; -} -#endif - -#if BUILDFLAG(USE_VAAPI) -std::unique_ptr -GpuVideoDecodeAcceleratorFactory::CreateVaapiVDA( - const gpu::GpuDriverBugWorkarounds& /*workarounds*/, - const gpu::GpuPreferences& /*gpu_preferences*/, - MediaLog* /*media_log*/) const { - std::unique_ptr decoder; - decoder.reset(new VaapiVideoDecodeAccelerator(gl_client_.make_context_current, - gl_client_.bind_image)); - return decoder; -} -#elif BUILDFLAG(USE_V4L2_CODEC) -std::unique_ptr -GpuVideoDecodeAcceleratorFactory::CreateV4L2VDA( - const gpu::GpuDriverBugWorkarounds& /*workarounds*/, - const gpu::GpuPreferences& /*gpu_preferences*/, - MediaLog* /*media_log*/) const { - std::unique_ptr decoder; - scoped_refptr device = V4L2Device::Create(); - if (device.get()) { - decoder.reset(new V4L2VideoDecodeAccelerator( - gl::GLSurfaceEGL::GetGLDisplayEGL()->GetDisplay(), - gl_client_.get_context, gl_client_.make_context_current, device)); - } - return decoder; -} - -std::unique_ptr -GpuVideoDecodeAcceleratorFactory::CreateV4L2SliceVDA( - const gpu::GpuDriverBugWorkarounds& /*workarounds*/, - const gpu::GpuPreferences& /*gpu_preferences*/, - MediaLog* /*media_log*/) const { - std::unique_ptr decoder; - scoped_refptr device = V4L2Device::Create(); - if (device.get()) { - decoder.reset(new V4L2SliceVideoDecodeAccelerator( - device, gl::GLSurfaceEGL::GetGLDisplayEGL()->GetDisplay(), - gl_client_.bind_image, gl_client_.make_context_current)); - } - return decoder; -} -#endif - -#if BUILDFLAG(IS_MAC) -std::unique_ptr -GpuVideoDecodeAcceleratorFactory::CreateVTVDA( - const gpu::GpuDriverBugWorkarounds& workarounds, - const gpu::GpuPreferences& gpu_preferences, - MediaLog* media_log) const { - LOG(WARNING) << "Initializing VAAPI VDA."; - std::unique_ptr decoder; - decoder.reset( - new VTVideoDecodeAccelerator(gl_client_, workarounds, media_log)); - return decoder; -} -#endif - -GpuVideoDecodeAcceleratorFactory::GpuVideoDecodeAcceleratorFactory( - const GpuVideoDecodeGLClient& gl_client) - : gl_client_(gl_client) {} -GpuVideoDecodeAcceleratorFactory::~GpuVideoDecodeAcceleratorFactory() = default; - -} // namespace media diff --git a/media/gpu/ipc/service/gpu_video_decode_accelerator.cc b/media/gpu/ipc/service/gpu_video_decode_accelerator.cc deleted file mode 100644 index a7fa326e..00000000 --- a/media/gpu/ipc/service/gpu_video_decode_accelerator.cc +++ /dev/null @@ -1,624 +0,0 @@ -// Copyright (c) 2022 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "media/gpu/ipc/service/gpu_video_decode_accelerator.h" - -#include -#include - -#include "base/bind.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/memory/raw_ptr.h" -#include "base/memory/ref_counted.h" -#include "base/synchronization/waitable_event.h" -#include "base/task/bind_post_task.h" -#include "base/task/single_thread_task_runner.h" -#include "base/threading/thread_task_runner_handle.h" -#include "build/build_config.h" -#include "gpu/command_buffer/common/command_buffer.h" -#include "gpu/config/gpu_preferences.h" -#include "gpu/ipc/service/gpu_channel.h" -#include "gpu/ipc/service/gpu_channel_manager.h" -#include "ipc/ipc_message_macros.h" -#include "ipc/ipc_message_utils.h" -#include "ipc/message_filter.h" -#include "media/base/limits.h" -#include "media/gpu/gpu_video_accelerator_util.h" -#include "media/gpu/gpu_video_decode_accelerator_factory.h" -#include "mojo/public/cpp/bindings/associated_receiver.h" -#include "ui/gfx/geometry/size.h" -#include "ui/gl/gl_context.h" -#include "ui/gl/gl_image.h" - -namespace media { - -namespace { -static gl::GLContext* GetGLContext( - const base::WeakPtr& stub) { - if (!stub) { - DLOG(ERROR) << "Stub is gone; no GLContext."; - return nullptr; - } - - return stub->decoder_context()->GetGLContext(); -} - -static bool MakeDecoderContextCurrent( - const base::WeakPtr& stub) { - if (!stub) { - DLOG(ERROR) << "Stub is gone; won't MakeCurrent()."; - return false; - } - - if (!stub->decoder_context()->MakeCurrent()) { - DLOG(ERROR) << "Failed to MakeCurrent()"; - return false; - } - - return true; -} - -static bool BindImage(const base::WeakPtr& stub, - uint32_t client_texture_id, - uint32_t texture_target, - const scoped_refptr& image, - bool can_bind_to_sampler) { - if (!stub) { - DLOG(ERROR) << "Stub is gone; won't BindImage()."; - return false; - } - - gpu::DecoderContext* command_decoder = stub->decoder_context(); - command_decoder->BindImage(client_texture_id, texture_target, image.get(), - can_bind_to_sampler); - return true; -} - -static gpu::gles2::ContextGroup* GetContextGroup( - const base::WeakPtr& stub) { - if (!stub) { - DLOG(ERROR) << "Stub is gone; no DecoderContext."; - return nullptr; - } - - return stub->decoder_context()->GetContextGroup(); -} - -static std::unique_ptr CreateAbstractTexture( - const base::WeakPtr& stub, - GLenum target, - GLenum internal_format, - GLsizei width, - GLsizei height, - GLsizei depth, - GLint border, - GLenum format, - GLenum type) { - if (!stub) { - DLOG(ERROR) << "Stub is gone; no DecoderContext."; - return nullptr; - } - - return stub->decoder_context()->CreateAbstractTexture( - target, internal_format, width, height, depth, border, format, type); -} - -} // anonymous namespace - -// DebugAutoLock works like AutoLock but only acquires the lock when -// DCHECK is on. -#if DCHECK_IS_ON() -typedef base::AutoLock DebugAutoLock; -#else -class DebugAutoLock { - public: - explicit DebugAutoLock(base::Lock&) {} -}; -#endif - -// Receives incoming messages for the decoder. Operates exclusively on the IO -// thread, since sometimes we want to do decodes directly from there. -class GpuVideoDecodeAccelerator::MessageFilter - : public mojom::GpuAcceleratedVideoDecoder { - public: - MessageFilter(GpuVideoDecodeAccelerator* owner, - scoped_refptr owner_task_runner, - bool decode_on_io) - : owner_(owner), - owner_task_runner_(std::move(owner_task_runner)), - decode_on_io_(decode_on_io) {} - ~MessageFilter() override = default; - - // Called from the main thread. Posts to `io_task_runner` to do the binding - // and waits for completion before returning. This ensures the decoder's - // endpoint is established before the synchronous request to establish it is - // acknowledged to the client. - bool Bind(mojo::PendingAssociatedReceiver - receiver, - const scoped_refptr& io_task_runner) { - base::WaitableEvent bound_event; - if (!io_task_runner->PostTask( - FROM_HERE, base::BindOnce(&MessageFilter::BindOnIoThread, - base::Unretained(this), - std::move(receiver), &bound_event))) { - return false; - } - bound_event.Wait(); - return true; - } - - // Must be called on the IO thread. Posts back to the owner's task runner to - // destroy it. - void RequestShutdown() { - if (!owner_) - return; - - // Must be reset here on the IO thread before `this` is destroyed. - receiver_.reset(); - - GpuVideoDecodeAccelerator* owner = owner_; - owner_ = nullptr; - - // Invalidate any IO thread WeakPtrs which may be held by the - // VideoDecodeAccelerator, and post to delete our owner which will in turn - // delete us. Note that it is unsafe to access any members of `this` once - // the task below is posted. - owner->weak_factory_for_io_.InvalidateWeakPtrs(); - owner_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&GpuVideoDecodeAccelerator::DeleteSelfNow, - base::Unretained(owner))); - } - - // mojom::GpuAcceleratedVideoDecoder: - void Decode(BitstreamBuffer buffer) override; - void AssignPictureBuffers( - std::vector assignments) override; - void ReusePictureBuffer(int32_t picture_buffer_id) override; - void Flush(FlushCallback callback) override; - void Reset(ResetCallback callback) override; - void SetOverlayInfo(const OverlayInfo& overlay_info) override; - - private: - void BindOnIoThread(mojo::PendingAssociatedReceiver< - mojom::GpuAcceleratedVideoDecoder> receiver, - base::WaitableEvent* bound_event) { - receiver_.Bind(std::move(receiver)); - receiver_.set_disconnect_handler( - base::BindOnce(&MessageFilter::OnDisconnect, base::Unretained(this))); - bound_event->Signal(); - } - - void OnDisconnect() { - if (!owner_) - return; - - owner_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&GpuVideoDecodeAccelerator::OnDestroy, - base::Unretained(owner_))); - } - - raw_ptr owner_; - const scoped_refptr owner_task_runner_; - const bool decode_on_io_; - mojo::AssociatedReceiver receiver_{this}; -}; - -void GpuVideoDecodeAccelerator::MessageFilter::Decode(BitstreamBuffer buffer) { - if (!owner_) - return; - - if (decode_on_io_) { - owner_->OnDecode(std::move(buffer)); - } else { - owner_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&GpuVideoDecodeAccelerator::OnDecode, - base::Unretained(owner_), std::move(buffer))); - } -} - -void GpuVideoDecodeAccelerator::MessageFilter::AssignPictureBuffers( - std::vector assignments) { - if (!owner_) - return; - owner_task_runner_->PostTask( - FROM_HERE, - base::BindOnce(&GpuVideoDecodeAccelerator::OnAssignPictureBuffers, - base::Unretained(owner_), std::move(assignments))); -} - -void GpuVideoDecodeAccelerator::MessageFilter::ReusePictureBuffer( - int32_t picture_buffer_id) { - if (!owner_) - return; - owner_task_runner_->PostTask( - FROM_HERE, - base::BindOnce(&GpuVideoDecodeAccelerator::OnReusePictureBuffer, - base::Unretained(owner_), picture_buffer_id)); -} - -void GpuVideoDecodeAccelerator::MessageFilter::Flush(FlushCallback callback) { - if (!owner_) - return; - owner_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&GpuVideoDecodeAccelerator::OnFlush, - base::Unretained(owner_), std::move(callback))); -} - -void GpuVideoDecodeAccelerator::MessageFilter::Reset(ResetCallback callback) { - if (!owner_) - return; - owner_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&GpuVideoDecodeAccelerator::OnReset, - base::Unretained(owner_), std::move(callback))); -} - -void GpuVideoDecodeAccelerator::MessageFilter::SetOverlayInfo( - const OverlayInfo& overlay_info) { - if (!owner_) - return; - owner_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&GpuVideoDecodeAccelerator::OnSetOverlayInfo, - base::Unretained(owner_), overlay_info)); -} - -GpuVideoDecodeAccelerator::GpuVideoDecodeAccelerator( - gpu::CommandBufferStub* stub, - const scoped_refptr& io_task_runner, - const AndroidOverlayMojoFactoryCB& overlay_factory_cb) - : stub_(stub), - texture_target_(0), - pixel_format_(PIXEL_FORMAT_UNKNOWN), - textures_per_buffer_(0), - child_task_runner_(base::ThreadTaskRunnerHandle::Get()), - io_task_runner_(io_task_runner), - overlay_factory_cb_(overlay_factory_cb) { - DCHECK(stub_); - stub_->AddDestructionObserver(this); - gl_client_.get_context = - base::BindRepeating(&GetGLContext, stub_->AsWeakPtr()); - gl_client_.make_context_current = - base::BindRepeating(&MakeDecoderContextCurrent, stub_->AsWeakPtr()); - gl_client_.bind_image = base::BindRepeating(&BindImage, stub_->AsWeakPtr()); - gl_client_.get_context_group = - base::BindRepeating(&GetContextGroup, stub_->AsWeakPtr()); - gl_client_.create_abstract_texture = - base::BindRepeating(&CreateAbstractTexture, stub_->AsWeakPtr()); - gl_client_.is_passthrough = - stub_->decoder_context()->GetFeatureInfo()->is_passthrough_cmd_decoder(); - gl_client_.supports_arb_texture_rectangle = stub_->decoder_context() - ->GetFeatureInfo() - ->feature_flags() - .arb_texture_rectangle; -} - -GpuVideoDecodeAccelerator::~GpuVideoDecodeAccelerator() { - // This class can only be self-deleted from OnWillDestroyStub(), which means - // the VDA has already been destroyed in there. - DCHECK(!video_decode_accelerator_); -} - -void GpuVideoDecodeAccelerator::DeleteSelfNow() { - delete this; -} - -// static -gpu::VideoDecodeAcceleratorCapabilities -GpuVideoDecodeAccelerator::GetCapabilities( - const gpu::GpuPreferences& gpu_preferences, - const gpu::GpuDriverBugWorkarounds& workarounds) { - return GpuVideoDecodeAcceleratorFactory::GetDecoderCapabilities( - gpu_preferences, workarounds); -} - -void GpuVideoDecodeAccelerator::NotifyInitializationComplete( - DecoderStatus status) { - decoder_client_->OnInitializationComplete(status.is_ok()); -} - -void GpuVideoDecodeAccelerator::ProvidePictureBuffers( - uint32_t requested_num_of_buffers, - VideoPixelFormat format, - uint32_t textures_per_buffer, - const gfx::Size& dimensions, - uint32_t texture_target) { - if (dimensions.width() > limits::kMaxDimension || - dimensions.height() > limits::kMaxDimension || - dimensions.GetArea() > limits::kMaxCanvas) { - NotifyError(VideoDecodeAccelerator::PLATFORM_FAILURE); - return; - } - - texture_dimensions_ = dimensions; - textures_per_buffer_ = textures_per_buffer; - texture_target_ = texture_target; - pixel_format_ = format; - - decoder_client_->OnProvidePictureBuffers(requested_num_of_buffers, format, - textures_per_buffer, dimensions, - texture_target); -} - -void GpuVideoDecodeAccelerator::DismissPictureBuffer( - int32_t picture_buffer_id) { - // Notify client that picture buffer is now unused. - decoder_client_->OnDismissPictureBuffer(picture_buffer_id); - DebugAutoLock auto_lock(debug_uncleared_textures_lock_); - uncleared_textures_.erase(picture_buffer_id); -} - -void GpuVideoDecodeAccelerator::PictureReady(const Picture& picture) { - // VDA may call PictureReady on IO thread. SetTextureCleared should run on - // the child thread. VDA is responsible to call PictureReady on the child - // thread when a picture buffer is delivered the first time. - if (child_task_runner_->BelongsToCurrentThread()) { - SetTextureCleared(picture); - } else { - DCHECK(io_task_runner_->BelongsToCurrentThread()); - DebugAutoLock auto_lock(debug_uncleared_textures_lock_); - DCHECK_EQ(0u, uncleared_textures_.count(picture.picture_buffer_id())); - } - - auto params = mojom::PictureReadyParams::New(); - params->picture_buffer_id = picture.picture_buffer_id(); - params->bitstream_buffer_id = picture.bitstream_buffer_id(); - params->visible_rect = picture.visible_rect(); - params->color_space = picture.color_space(); - params->allow_overlay = picture.allow_overlay(); - params->read_lock_fences_enabled = picture.read_lock_fences_enabled(); - params->size_changed = picture.size_changed(); - params->surface_texture = picture.texture_owner(); - params->wants_promotion_hint = picture.wants_promotion_hint(); - decoder_client_->OnPictureReady(std::move(params)); -} - -void GpuVideoDecodeAccelerator::NotifyEndOfBitstreamBuffer( - int32_t bitstream_buffer_id) { - decoder_client_->OnBitstreamBufferProcessed(bitstream_buffer_id); -} - -void GpuVideoDecodeAccelerator::NotifyFlushDone() { - DCHECK(!pending_flushes_.empty()); - std::move(pending_flushes_.front()).Run(); - pending_flushes_.pop_front(); -} - -void GpuVideoDecodeAccelerator::NotifyResetDone() { - DCHECK(!pending_resets_.empty()); - std::move(pending_resets_.front()).Run(); - pending_resets_.pop_front(); -} - -void GpuVideoDecodeAccelerator::NotifyError( - VideoDecodeAccelerator::Error error) { - decoder_client_->OnError(error); -} - -void GpuVideoDecodeAccelerator::OnWillDestroyStub(bool have_context) { - // The stub is going away, so we have to stop and destroy VDA here, before - // returning, because the VDA may need the GL context to run and/or do its - // cleanup. We cannot destroy the VDA before the IO thread message filter is - // removed however, since we cannot service incoming messages with VDA gone. - // We cannot simply check for existence of VDA on IO thread though, because - // we don't want to synchronize the IO thread with the ChildThread. - // So we have to wait for the RemoveFilter callback here instead and remove - // the VDA after it arrives and before returning. - stub_->RemoveDestructionObserver(this); - if (filter_) { - io_task_runner_->PostTask(FROM_HERE, - base::BindOnce(&MessageFilter::RequestShutdown, - base::Unretained(filter_.get()))); - } - - video_decode_accelerator_.reset(); -} - -bool GpuVideoDecodeAccelerator::Initialize( - const VideoDecodeAccelerator::Config& config, - mojo::PendingAssociatedReceiver receiver, - mojo::PendingAssociatedRemote - client) { - DCHECK(!video_decode_accelerator_); - -#if !BUILDFLAG(IS_WIN) - // Ensure we will be able to get a GL context at all before initializing - // non-Windows VDAs. - if (!gl_client_.make_context_current.Run()) - return false; -#endif - - std::unique_ptr vda_factory = - GpuVideoDecodeAcceleratorFactory::Create(gl_client_); - if (!vda_factory) { - LOG(ERROR) << "Failed creating the VDA factory"; - return false; - } - LOG(WARNING) << "Created the VDA factory"; - - const gpu::GpuDriverBugWorkarounds& gpu_workarounds = - stub_->channel()->gpu_channel_manager()->gpu_driver_bug_workarounds(); - const gpu::GpuPreferences& gpu_preferences = - stub_->channel()->gpu_channel_manager()->gpu_preferences(); - - if (config.output_mode != - VideoDecodeAccelerator::Config::OutputMode::ALLOCATE) { - DLOG(ERROR) << "Only ALLOCATE mode is supported"; - return false; - } - - video_decode_accelerator_ = - vda_factory->CreateVDA(this, config, gpu_workarounds, gpu_preferences); - if (!video_decode_accelerator_) { - LOG(ERROR) << "HW video decode not available for profile " - << GetProfileName(config.profile) - << (config.is_encrypted() ? " with encryption" : ""); - return false; - } - LOG(WARNING) << "Created VDA"; - - decoder_client_.Bind(std::move(client), io_task_runner_); - - // Attempt to set up performing decoding tasks on IO thread, if supported by - // the VDA. - bool decode_on_io = - video_decode_accelerator_->TryToSetupDecodeOnSeparateThread( - weak_factory_for_io_.GetWeakPtr(), io_task_runner_); - - // Bind the receiver on the IO thread. We wait here for it to be bound - // before returning and signaling that the decoder has been created. - filter_ = - std::make_unique(this, stub_->task_runner(), decode_on_io); - return filter_->Bind(std::move(receiver), io_task_runner_); -} - -// Runs on IO thread if VDA::TryToSetupDecodeOnSeparateThread() succeeded, -// otherwise on the main thread. -void GpuVideoDecodeAccelerator::OnDecode(BitstreamBuffer bitstream_buffer) { - DCHECK(video_decode_accelerator_); - video_decode_accelerator_->Decode(std::move(bitstream_buffer)); -} - -void GpuVideoDecodeAccelerator::OnAssignPictureBuffers( - std::vector assignments) { - gpu::DecoderContext* decoder_context = stub_->decoder_context(); - gpu::gles2::TextureManager* texture_manager = - stub_->decoder_context()->GetContextGroup()->texture_manager(); - - std::vector buffers; - std::vector>> textures; - for (const auto& assignment : assignments) { - if (assignment->buffer_id < 0) { - DLOG(ERROR) << "Buffer id " << assignment->buffer_id << " out of range"; - NotifyError(VideoDecodeAccelerator::INVALID_ARGUMENT); - return; - } - std::vector> current_textures; - PictureBuffer::TextureIds buffer_texture_ids = assignment->texture_ids; - PictureBuffer::TextureIds service_ids; - if (buffer_texture_ids.size() != textures_per_buffer_) { - DLOG(ERROR) << "Requested " << textures_per_buffer_ - << " textures per picture buffer, got " - << buffer_texture_ids.size(); - NotifyError(VideoDecodeAccelerator::INVALID_ARGUMENT); - return; - } - for (size_t j = 0; j < textures_per_buffer_; j++) { - gpu::TextureBase* texture_base = - decoder_context->GetTextureBase(buffer_texture_ids[j]); - if (!texture_base) { - DLOG(ERROR) << "Failed to find texture id " << buffer_texture_ids[j]; - NotifyError(VideoDecodeAccelerator::INVALID_ARGUMENT); - return; - } - - if (texture_base->target() != texture_target_) { - DLOG(ERROR) << "Texture target mismatch for texture id " - << buffer_texture_ids[j]; - NotifyError(VideoDecodeAccelerator::INVALID_ARGUMENT); - return; - } - - if (texture_manager) { - gpu::gles2::TextureRef* texture_ref = - texture_manager->GetTexture(buffer_texture_ids[j]); - if (texture_ref) { - gpu::gles2::Texture* info = texture_ref->texture(); - if (texture_target_ == GL_TEXTURE_EXTERNAL_OES || - texture_target_ == GL_TEXTURE_RECTANGLE_ARB) { - // These textures have their dimensions defined by the underlying - // storage. - // Use |texture_dimensions_| for this size. - texture_manager->SetLevelInfo( - texture_ref, texture_target_, 0, GL_RGBA, - texture_dimensions_.width(), texture_dimensions_.height(), 1, 0, - GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect()); - } else { - // For other targets, texture dimensions should already be defined. - GLsizei width = 0, height = 0; - info->GetLevelSize(texture_target_, 0, &width, &height, nullptr); - if (width != texture_dimensions_.width() || - height != texture_dimensions_.height()) { - DLOG(ERROR) << "Size mismatch for texture id " - << buffer_texture_ids[j]; - NotifyError(VideoDecodeAccelerator::INVALID_ARGUMENT); - return; - } - - // TODO(dshwang): after moving to D3D11, remove this. - // https://crbug.com/438691 - GLenum format = - video_decode_accelerator_->GetSurfaceInternalFormat(); - if (format != GL_RGBA) { - DCHECK(format == GL_BGRA_EXT); - texture_manager->SetLevelInfo(texture_ref, texture_target_, 0, - format, width, height, 1, 0, format, - GL_UNSIGNED_BYTE, gfx::Rect()); - } - } - current_textures.push_back(texture_ref); - } - } - service_ids.push_back(texture_base->service_id()); - } - textures.push_back(current_textures); - buffers.emplace_back(assignment->buffer_id, texture_dimensions_, - buffer_texture_ids, service_ids, texture_target_, - pixel_format_); - } - { - DebugAutoLock auto_lock(debug_uncleared_textures_lock_); - for (uint32_t i = 0; i < assignments.size(); ++i) - uncleared_textures_[assignments[i]->buffer_id] = textures[i]; - } - video_decode_accelerator_->AssignPictureBuffers(buffers); -} - -void GpuVideoDecodeAccelerator::OnReusePictureBuffer( - int32_t picture_buffer_id) { - DCHECK(video_decode_accelerator_); - video_decode_accelerator_->ReusePictureBuffer(picture_buffer_id); -} - -void GpuVideoDecodeAccelerator::OnFlush(base::OnceClosure callback) { - DCHECK(video_decode_accelerator_); - pending_flushes_.push_back( - base::BindPostTask(io_task_runner_, std::move(callback))); - video_decode_accelerator_->Flush(); -} - -void GpuVideoDecodeAccelerator::OnReset(base::OnceClosure callback) { - DCHECK(video_decode_accelerator_); - pending_resets_.push_back( - base::BindPostTask(io_task_runner_, std::move(callback))); - video_decode_accelerator_->Reset(); -} - -void GpuVideoDecodeAccelerator::OnSetOverlayInfo( - const OverlayInfo& overlay_info) { - DCHECK(video_decode_accelerator_); - video_decode_accelerator_->SetOverlayInfo(overlay_info); -} - -void GpuVideoDecodeAccelerator::OnDestroy() { - DCHECK(video_decode_accelerator_); - OnWillDestroyStub(false); -} - -void GpuVideoDecodeAccelerator::SetTextureCleared(const Picture& picture) { - DCHECK(child_task_runner_->BelongsToCurrentThread()); - DebugAutoLock auto_lock(debug_uncleared_textures_lock_); - auto it = uncleared_textures_.find(picture.picture_buffer_id()); - if (it == uncleared_textures_.end()) - return; // the texture has been cleared - - for (auto texture_ref : it->second) { - GLenum target = texture_ref->texture()->target(); - gpu::gles2::TextureManager* texture_manager = - stub_->decoder_context()->GetContextGroup()->texture_manager(); - texture_manager->SetLevelCleared(texture_ref.get(), target, 0, true); - } - uncleared_textures_.erase(it); -} - -} // namespace media diff --git a/media/gpu/vaapi/vaapi_picture_factory.cc b/media/gpu/vaapi/vaapi_picture_factory.cc deleted file mode 100644 index 1c806bfc..00000000 --- a/media/gpu/vaapi/vaapi_picture_factory.cc +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2022 The Chromium Authors and Alex313031. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "media/gpu/vaapi/vaapi_picture_factory.h" - -#include "base/containers/contains.h" -#include "media/gpu/vaapi/vaapi_wrapper.h" -#include "media/video/picture.h" -#include "ui/gl/gl_bindings.h" - -#if defined(USE_OZONE) -#include "media/gpu/vaapi/vaapi_picture_native_pixmap_ozone.h" -#endif // defined(USE_OZONE) -#if BUILDFLAG(USE_VAAPI_X11) -#include "media/gpu/vaapi/vaapi_picture_native_pixmap_angle.h" -#include "media/gpu/vaapi/vaapi_picture_tfp.h" -#endif // BUILDFLAG(USE_VAAPI_X11) -#if defined(USE_EGL) -#include "media/gpu/vaapi/vaapi_picture_native_pixmap_egl.h" -#endif - -namespace media { - -namespace { - -template -std::unique_ptr CreateVaapiPictureNativeImpl( - scoped_refptr vaapi_wrapper, - const MakeGLContextCurrentCallback& make_context_current_cb, - const BindGLImageCallback& bind_image_cb, - const PictureBuffer& picture_buffer, - const gfx::Size& visible_size, - uint32_t client_texture_id, - uint32_t service_texture_id) { - return std::make_unique( - std::move(vaapi_wrapper), make_context_current_cb, bind_image_cb, - picture_buffer.id(), picture_buffer.size(), visible_size, - service_texture_id, client_texture_id, picture_buffer.texture_target()); -} - -} // namespace - -VaapiPictureFactory::VaapiPictureFactory() { - vaapi_impl_pairs_.insert( - std::make_pair(gl::kGLImplementationEGLGLES2, - VaapiPictureFactory::kVaapiImplementationDrm)); -#if BUILDFLAG(USE_VAAPI_X11) - vaapi_impl_pairs_.insert( - std::make_pair(gl::kGLImplementationEGLANGLE, - VaapiPictureFactory::kVaapiImplementationAngle)); - vaapi_impl_pairs_.insert( - std::make_pair(gl::kGLImplementationDesktopGL, - VaapiPictureFactory::kVaapiImplementationX11)); -#elif defined(USE_OZONE) - vaapi_impl_pairs_.insert( - std::make_pair(gl::kGLImplementationEGLANGLE, - VaapiPictureFactory::kVaapiImplementationDrm)); -#endif - - DeterminePictureCreationAndDownloadingMechanism(); -} - -VaapiPictureFactory::~VaapiPictureFactory() = default; - -std::unique_ptr VaapiPictureFactory::Create( - scoped_refptr vaapi_wrapper, - const MakeGLContextCurrentCallback& make_context_current_cb, - const BindGLImageCallback& bind_image_cb, - const PictureBuffer& picture_buffer, - const gfx::Size& visible_size) { - // ARC++ sends |picture_buffer| with no texture_target(). - DCHECK(picture_buffer.texture_target() == GetGLTextureTarget() || - picture_buffer.texture_target() == 0u); - - // |client_texture_ids| and |service_texture_ids| are empty from ARC++. - const uint32_t client_texture_id = - !picture_buffer.client_texture_ids().empty() - ? picture_buffer.client_texture_ids()[0] - : 0; - const uint32_t service_texture_id = - !picture_buffer.service_texture_ids().empty() - ? picture_buffer.service_texture_ids()[0] - : 0; - - // Select DRM(egl) / TFP(glx) at runtime with --use-gl=egl / --use-gl=desktop - return CreateVaapiPictureNative(vaapi_wrapper, make_context_current_cb, - bind_image_cb, picture_buffer, visible_size, - client_texture_id, service_texture_id); -} - -VaapiPictureFactory::VaapiImplementation -VaapiPictureFactory::GetVaapiImplementation(gl::GLImplementation gl_impl) { - if (base::Contains(vaapi_impl_pairs_, gl_impl)) - return vaapi_impl_pairs_[gl_impl]; - return kVaapiImplementationNone; -} - -uint32_t VaapiPictureFactory::GetGLTextureTarget() { -#if BUILDFLAG(USE_VAAPI_X11) - return GL_TEXTURE_2D; -#else - return GL_TEXTURE_EXTERNAL_OES; -#endif -} - -gfx::BufferFormat VaapiPictureFactory::GetBufferFormat() { -#if BUILDFLAG(IS_LINUX) - return gfx::BufferFormat::RGBX_8888; -#else - return gfx::BufferFormat::YUV_420_BIPLANAR; -#endif -} - -void VaapiPictureFactory::DeterminePictureCreationAndDownloadingMechanism() { - switch (GetVaapiImplementation(gl::GetGLImplementation())) { -#if defined(USE_OZONE) - // We can be called without GL initialized, which is valid if we use Ozone. - case kVaapiImplementationNone: - create_picture_cb_ = base::BindRepeating( - &CreateVaapiPictureNativeImpl); - needs_vpp_for_downloading_ = true; - break; -#endif // defined(USE_OZONE) -#if BUILDFLAG(USE_VAAPI_X11) - case kVaapiImplementationX11: - create_picture_cb_ = - base::BindRepeating(&CreateVaapiPictureNativeImpl); - // Neither VaapiTFPPicture or VaapiPictureNativePixmapAngle needs the VPP. - needs_vpp_for_downloading_ = false; - break; - case kVaapiImplementationAngle: - create_picture_cb_ = base::BindRepeating( - &CreateVaapiPictureNativeImpl); - // Neither VaapiTFPPicture or VaapiPictureNativePixmapAngle needs the VPP. - needs_vpp_for_downloading_ = false; - break; -#endif // BUILDFLAG(USE_VAAPI_X11) - case kVaapiImplementationDrm: -#if defined(USE_OZONE) - create_picture_cb_ = base::BindRepeating( - &CreateVaapiPictureNativeImpl); - needs_vpp_for_downloading_ = true; - break; -#elif defined(USE_EGL) - create_picture_cb_ = base::BindRepeating( - &CreateVaapiPictureNativeImpl); - needs_vpp_for_downloading_ = true; - break; -#else - // ozone or egl must be used to use the DRM implementation. - [[fallthrough]]; -#endif - default: - NOTREACHED(); - break; - } -} - -bool VaapiPictureFactory::NeedsProcessingPipelineForDownloading() const { - return needs_vpp_for_downloading_; -} - -std::unique_ptr VaapiPictureFactory::CreateVaapiPictureNative( - scoped_refptr vaapi_wrapper, - const MakeGLContextCurrentCallback& make_context_current_cb, - const BindGLImageCallback& bind_image_cb, - const PictureBuffer& picture_buffer, - const gfx::Size& visible_size, - uint32_t client_texture_id, - uint32_t service_texture_id) { - CHECK(create_picture_cb_); - return create_picture_cb_.Run( - std::move(vaapi_wrapper), make_context_current_cb, bind_image_cb, - picture_buffer, visible_size, client_texture_id, service_texture_id); -} - -} // namespace media diff --git a/media/gpu/vaapi/vaapi_picture_native_pixmap.cc b/media/gpu/vaapi/vaapi_picture_native_pixmap.cc deleted file mode 100644 index 325b23f1..00000000 --- a/media/gpu/vaapi/vaapi_picture_native_pixmap.cc +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2022 The Chromium Authors and Alex313031. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "media/gpu/vaapi/vaapi_picture_native_pixmap.h" - -#include "media/gpu/macros.h" -#include "media/gpu/vaapi/va_surface.h" -#include "media/gpu/vaapi/vaapi_wrapper.h" -#include "ui/gfx/buffer_format_util.h" -#include "ui/gfx/gpu_memory_buffer.h" -#include "ui/gfx/linux/native_pixmap_dmabuf.h" -#include "ui/gfx/native_pixmap.h" -#include "ui/gl/gl_bindings.h" -#include "ui/gl/gl_image_native_pixmap.h" - -namespace media { - -VaapiPictureNativePixmap::VaapiPictureNativePixmap( - scoped_refptr vaapi_wrapper, - const MakeGLContextCurrentCallback& make_context_current_cb, - const BindGLImageCallback& bind_image_cb, - int32_t picture_buffer_id, - const gfx::Size& size, - const gfx::Size& visible_size, - uint32_t texture_id, - uint32_t client_texture_id, - uint32_t texture_target) - : VaapiPicture(std::move(vaapi_wrapper), - make_context_current_cb, - bind_image_cb, - picture_buffer_id, - size, - visible_size, - texture_id, - client_texture_id, - texture_target) {} - -VaapiPictureNativePixmap::~VaapiPictureNativePixmap() = default; - -bool VaapiPictureNativePixmap::DownloadFromSurface( - scoped_refptr va_surface) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (!vaapi_wrapper_->SyncSurface(va_surface->id())) { - VLOGF(1) << "Cannot sync VPP input surface"; - return false; - } - if (!vaapi_wrapper_->BlitSurface(*va_surface, *va_surface_)) { - VLOGF(1) << "Cannot convert decoded image into output buffer"; - return false; - } - - // Sync target surface since the buffer is returning to client. - if (!vaapi_wrapper_->SyncSurface(va_surface_->id())) { - VLOGF(1) << "Cannot sync VPP output surface"; - return false; - } - return true; -} - -bool VaapiPictureNativePixmap::AllowOverlay() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return true; -} - -VASurfaceID VaapiPictureNativePixmap::va_surface_id() const { - return va_surface_->id(); -} - -} // namespace media diff --git a/media/gpu/vaapi/vaapi_video_decode_accelerator.cc b/media/gpu/vaapi/vaapi_video_decode_accelerator.cc deleted file mode 100644 index c9208dd0..00000000 --- a/media/gpu/vaapi/vaapi_video_decode_accelerator.cc +++ /dev/null @@ -1,1320 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "media/gpu/vaapi/vaapi_video_decode_accelerator.h" - -#include -#include - -#include - -#include "base/bind.h" -#include "base/callback_helpers.h" -#include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" -#include "base/cpu.h" -#include "base/files/scoped_file.h" -#include "base/json/json_writer.h" -#include "base/logging.h" -#include "base/metrics/histogram_macros.h" -#include "base/numerics/safe_conversions.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/synchronization/waitable_event.h" -#include "base/threading/thread_task_runner_handle.h" -#include "base/trace_event/memory_dump_manager.h" -#include "base/trace_event/process_memory_dump.h" -#include "base/trace_event/trace_event.h" -#include "build/build_config.h" -#include "gpu/ipc/service/gpu_channel.h" -#include "media/base/bind_to_current_loop.h" -#include "media/base/format_utils.h" -#include "media/base/media_log.h" -#include "media/base/video_util.h" -#include "media/gpu/accelerated_video_decoder.h" -#include "media/gpu/h264_decoder.h" -#include "media/gpu/macros.h" -#include "media/gpu/vaapi/h264_vaapi_video_decoder_delegate.h" -#include "media/gpu/vaapi/vaapi_common.h" -#include "media/gpu/vaapi/vaapi_picture.h" -#include "media/gpu/vaapi/vaapi_utils.h" -#include "media/gpu/vaapi/vp8_vaapi_video_decoder_delegate.h" -#include "media/gpu/vaapi/vp9_vaapi_video_decoder_delegate.h" -#include "media/gpu/vp8_decoder.h" -#include "media/gpu/vp9_decoder.h" -#include "media/video/picture.h" - -namespace media { - -namespace { - -// Returns the preferred VA_RT_FORMAT for the given |profile|. -unsigned int GetVaFormatForVideoCodecProfile(VideoCodecProfile profile) { - if (profile == VP9PROFILE_PROFILE2 || profile == VP9PROFILE_PROFILE3) - return VA_RT_FORMAT_YUV420_10BPP; - return VA_RT_FORMAT_YUV420; -} - -// Returns true if the CPU is an Intel Gemini Lake or later (including Kaby -// Lake) Cpu platform id's are referenced from the following file in kernel -// source arch/x86/include/asm/intel-family.h -bool IsGeminiLakeOrLater() { - constexpr int kPentiumAndLaterFamily = 0x06; - constexpr int kGeminiLakeModelId = 0x7A; - static base::CPU cpuid; - static bool is_geminilake_or_later = - cpuid.family() == kPentiumAndLaterFamily && - cpuid.model() >= kGeminiLakeModelId; - return is_geminilake_or_later; -} - -} // namespace - -#define RETURN_AND_NOTIFY_ON_FAILURE(result, log, error_code, ret) \ - do { \ - if (!(result)) { \ - LOG(ERROR) << log; \ - NotifyError(error_code); \ - return ret; \ - } \ - } while (0) - -#define RETURN_AND_NOTIFY_ON_STATUS(status, ret) \ - do { \ - if (!status.is_ok()) { \ - NotifyStatus(status); \ - return ret; \ - } \ - } while (0) - -class VaapiVideoDecodeAccelerator::InputBuffer { - public: - InputBuffer() : buffer_(nullptr) {} - InputBuffer(int32_t id, - scoped_refptr buffer, - base::OnceCallback release_cb) - : id_(id), - buffer_(std::move(buffer)), - release_cb_(std::move(release_cb)) {} - - InputBuffer(const InputBuffer&) = delete; - InputBuffer& operator=(const InputBuffer&) = delete; - - ~InputBuffer() { - DVLOGF(4) << "id = " << id_; - if (release_cb_) - std::move(release_cb_).Run(id_); - } - - // Indicates this is a dummy buffer for flush request. - bool IsFlushRequest() const { return !buffer_; } - int32_t id() const { return id_; } - const scoped_refptr& buffer() const { return buffer_; } - - private: - const int32_t id_ = -1; - const scoped_refptr buffer_; - base::OnceCallback release_cb_; -}; - -void VaapiVideoDecodeAccelerator::NotifyStatus(VaapiStatus status) { - DCHECK(!status.is_ok()); - // Send a platform notification error - NotifyError(PLATFORM_FAILURE); - - // TODO(crbug.com/1103510) there is no MediaLog here, we should change that. - std::string output_str; - base::JSONWriter::Write(MediaSerialize(status), &output_str); - DLOG(ERROR) << output_str; -} - -void VaapiVideoDecodeAccelerator::NotifyError(Error error) { - if (!task_runner_->BelongsToCurrentThread()) { - DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); - task_runner_->PostTask( - FROM_HERE, base::BindOnce(&VaapiVideoDecodeAccelerator::NotifyError, - weak_this_, error)); - return; - } - - VLOGF(1) << "Notifying of error " << error; - if (client_) { - client_->NotifyError(error); - client_ptr_factory_.reset(); - } -} - -VaapiVideoDecodeAccelerator::VaapiVideoDecodeAccelerator( - const MakeGLContextCurrentCallback& make_context_current_cb, - const BindGLImageCallback& bind_image_cb) - : state_(kUninitialized), - input_ready_(&lock_), - buffer_allocation_mode_(BufferAllocationMode::kNormal), - surfaces_available_(&lock_), - va_surface_format_(VA_INVALID_ID), - task_runner_(base::ThreadTaskRunnerHandle::Get()), - decoder_thread_("VaapiDecoderThread"), - finish_flush_pending_(false), - awaiting_va_surfaces_recycle_(false), - requested_num_pics_(0), - requested_num_reference_frames_(0), - previously_requested_num_reference_frames_(0), - profile_(VIDEO_CODEC_PROFILE_UNKNOWN), - make_context_current_cb_(make_context_current_cb), - bind_image_cb_(bind_image_cb), - weak_this_factory_(this) { - weak_this_ = weak_this_factory_.GetWeakPtr(); - va_surface_recycle_cb_ = BindToCurrentLoop(base::BindRepeating( - &VaapiVideoDecodeAccelerator::RecycleVASurface, weak_this_)); - base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( - this, "media::VaapiVideoDecodeAccelerator", - base::ThreadTaskRunnerHandle::Get()); -} - -VaapiVideoDecodeAccelerator::~VaapiVideoDecodeAccelerator() { - DCHECK(task_runner_->BelongsToCurrentThread()); - base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( - this); -} - -bool VaapiVideoDecodeAccelerator::Initialize(const Config& config, - Client* client) { - DCHECK(task_runner_->BelongsToCurrentThread()); - - vaapi_picture_factory_ = std::make_unique(); - - if (config.is_encrypted()) { - NOTREACHED() << "Encrypted streams are not supported for this VDA"; - return false; - } - - client_ptr_factory_.reset(new base::WeakPtrFactory(client)); - client_ = client_ptr_factory_->GetWeakPtr(); - - VideoCodecProfile profile = config.profile; - - base::AutoLock auto_lock(lock_); - DCHECK_EQ(state_, kUninitialized); - VLOGF(2) << "Initializing VAVDA, profile: " << GetProfileName(profile); - - vaapi_wrapper_ = VaapiWrapper::CreateForVideoCodec( - VaapiWrapper::kDecode, profile, EncryptionScheme::kUnencrypted, - base::BindRepeating(&ReportVaapiErrorToUMA, - "Media.VaapiVideoDecodeAccelerator.VAAPIError"), - /*enforce_sequence_affinity=*/false); - - UMA_HISTOGRAM_BOOLEAN("Media.VAVDA.VaapiWrapperCreationSuccess", - vaapi_wrapper_.get()); - if (!vaapi_wrapper_.get()) { - VLOGF(1) << "Failed initializing VAAPI for profile " - << GetProfileName(profile); - return false; - } - - if (profile >= H264PROFILE_MIN && profile <= H264PROFILE_MAX) { - auto accelerator = - std::make_unique(this, vaapi_wrapper_); - decoder_delegate_ = accelerator.get(); - decoder_.reset(new H264Decoder(std::move(accelerator), profile, - config.container_color_space)); - } else if (profile >= VP8PROFILE_MIN && profile <= VP8PROFILE_MAX) { - auto accelerator = - std::make_unique(this, vaapi_wrapper_); - decoder_delegate_ = accelerator.get(); - decoder_.reset(new VP8Decoder(std::move(accelerator))); - } else if (profile >= VP9PROFILE_MIN && profile <= VP9PROFILE_MAX) { - auto accelerator = - std::make_unique(this, vaapi_wrapper_); - decoder_delegate_ = accelerator.get(); - decoder_.reset(new VP9Decoder(std::move(accelerator), profile, - config.container_color_space)); - } else { - VLOGF(1) << "Unsupported profile " << GetProfileName(profile); - return false; - } - - CHECK(decoder_thread_.Start()); - decoder_thread_task_runner_ = decoder_thread_.task_runner(); - - state_ = kIdle; - profile_ = profile; - output_mode_ = config.output_mode; - buffer_allocation_mode_ = DecideBufferAllocationMode(); - previously_requested_num_reference_frames_ = 0; - return true; -} - -void VaapiVideoDecodeAccelerator::OutputPicture( - scoped_refptr va_surface, - int32_t input_id, - gfx::Rect visible_rect, - const VideoColorSpace& picture_color_space) { - DCHECK(task_runner_->BelongsToCurrentThread()); - - const VASurfaceID va_surface_id = va_surface->id(); - - VaapiPicture* picture = nullptr; - { - base::AutoLock auto_lock(lock_); - int32_t picture_buffer_id = available_picture_buffers_.front(); - if (buffer_allocation_mode_ == BufferAllocationMode::kNone) { - // Find the |pictures_| entry matching |va_surface_id|. - for (const auto& id_and_picture : pictures_) { - if (id_and_picture.second->va_surface_id() == va_surface_id) { - picture_buffer_id = id_and_picture.first; - break; - } - } - } - picture = pictures_[picture_buffer_id].get(); - DCHECK(base::Contains(available_picture_buffers_, picture_buffer_id)); - base::Erase(available_picture_buffers_, picture_buffer_id); - } - - DCHECK(picture) << " could not find " << va_surface_id << " available"; - const int32_t output_id = picture->picture_buffer_id(); - - DVLOGF(4) << "Outputting VASurface " << va_surface->id() - << " into pixmap bound to picture buffer id " << output_id; - - if (buffer_allocation_mode_ != BufferAllocationMode::kNone) { - TRACE_EVENT2("media,gpu", "VAVDA::DownloadFromSurface", "input_id", - input_id, "output_id", output_id); - RETURN_AND_NOTIFY_ON_FAILURE(picture->DownloadFromSurface(va_surface), - "Failed putting surface into pixmap", - PLATFORM_FAILURE, ); - } - - { - base::AutoLock auto_lock(lock_); - TRACE_COUNTER_ID2("media,gpu", "Vaapi frames at client", this, "used", - pictures_.size() - available_picture_buffers_.size(), - "available", available_picture_buffers_.size()); - } - - DVLOGF(4) << "Notifying output picture id " << output_id << " for input " - << input_id - << " is ready. visible rect: " << visible_rect.ToString(); - if (!client_) - return; - - Picture client_picture(output_id, input_id, visible_rect, - picture_color_space.ToGfxColorSpace(), - picture->AllowOverlay()); - client_picture.set_read_lock_fences_enabled(true); - // Notify the |client_| a picture is ready to be consumed. - client_->PictureReady(client_picture); -} - -void VaapiVideoDecodeAccelerator::TryOutputPicture() { - DCHECK(task_runner_->BelongsToCurrentThread()); - - // Handle Destroy() arriving while pictures are queued for output. - if (!client_) - return; - - { - base::AutoLock auto_lock(lock_); - if (pending_output_cbs_.empty() || available_picture_buffers_.empty()) - return; - } - - auto output_cb = std::move(pending_output_cbs_.front()); - pending_output_cbs_.pop(); - std::move(output_cb).Run(); - - if (finish_flush_pending_ && pending_output_cbs_.empty()) - FinishFlush(); -} - -void VaapiVideoDecodeAccelerator::QueueInputBuffer( - scoped_refptr buffer, - int32_t bitstream_id) { - DVLOGF(4) << "Queueing new input buffer id: " << bitstream_id - << " size: " << (buffer->end_of_stream() ? 0 : buffer->data_size()); - DCHECK(task_runner_->BelongsToCurrentThread()); - TRACE_EVENT1("media,gpu", "QueueInputBuffer", "input_id", bitstream_id); - - base::AutoLock auto_lock(lock_); - if (buffer->end_of_stream()) { - auto flush_buffer = std::make_unique(); - DCHECK(flush_buffer->IsFlushRequest()); - input_buffers_.push(std::move(flush_buffer)); - } else { - auto input_buffer = std::make_unique( - bitstream_id, std::move(buffer), - BindToCurrentLoop( - base::BindOnce(&Client::NotifyEndOfBitstreamBuffer, client_))); - input_buffers_.push(std::move(input_buffer)); - } - - TRACE_COUNTER1("media,gpu", "Vaapi input buffers", input_buffers_.size()); - input_ready_.Signal(); - - switch (state_) { - case kIdle: - state_ = kDecoding; - decoder_thread_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&VaapiVideoDecodeAccelerator::DecodeTask, - base::Unretained(this))); - break; - - case kDecoding: - // Decoder already running. - break; - - case kResetting: - // When resetting, allow accumulating bitstream buffers, so that - // the client can queue after-seek-buffers while we are finishing with - // the before-seek one. - break; - - default: - LOG(ERROR) << "Decode/Flush request from client in invalid state: " - << state_; - NotifyError(PLATFORM_FAILURE); - break; - } -} - -bool VaapiVideoDecodeAccelerator::GetCurrInputBuffer_Locked() { - DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); - lock_.AssertAcquired(); - - if (curr_input_buffer_.get()) - return true; - - // Will only wait if it is expected that in current state new buffers will - // be queued from the client via Decode(). The state can change during wait. - while (input_buffers_.empty() && (state_ == kDecoding || state_ == kIdle)) - input_ready_.Wait(); - - // We could have got woken up in a different state or never got to sleep - // due to current state. - if (state_ != kDecoding && state_ != kIdle) - return false; - - DCHECK(!input_buffers_.empty()); - curr_input_buffer_ = std::move(input_buffers_.front()); - input_buffers_.pop(); - TRACE_COUNTER1("media,gpu", "Vaapi input buffers", input_buffers_.size()); - - if (curr_input_buffer_->IsFlushRequest()) { - DVLOGF(4) << "New flush buffer"; - return true; - } - - DVLOGF(4) << "New |curr_input_buffer_|, id: " << curr_input_buffer_->id() - << " size: " << curr_input_buffer_->buffer()->data_size() << "B"; - decoder_->SetStream(curr_input_buffer_->id(), *curr_input_buffer_->buffer()); - return true; -} - -void VaapiVideoDecodeAccelerator::ReturnCurrInputBuffer_Locked() { - DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); - lock_.AssertAcquired(); - DCHECK(curr_input_buffer_.get()); - curr_input_buffer_.reset(); -} - -// TODO(posciak): refactor the whole class to remove sleeping in wait for -// surfaces, and reschedule DecodeTask instead. -bool VaapiVideoDecodeAccelerator::WaitForSurfaces_Locked() { - DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); - lock_.AssertAcquired(); - - while (available_va_surfaces_.empty() && - (state_ == kDecoding || state_ == kIdle)) { - surfaces_available_.Wait(); - } - - return state_ == kDecoding || state_ == kIdle; -} - -void VaapiVideoDecodeAccelerator::DecodeTask() { - DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); - base::AutoLock auto_lock(lock_); - - if (state_ != kDecoding) - return; - DVLOGF(4) << "Decode task"; - - // Try to decode what stream data is (still) in the decoder until we run out - // of it. - while (GetCurrInputBuffer_Locked()) { - DCHECK(curr_input_buffer_.get()); - - if (curr_input_buffer_->IsFlushRequest()) { - FlushTask(); - break; - } - - AcceleratedVideoDecoder::DecodeResult res; - { - // We are OK releasing the lock here, as decoder never calls our methods - // directly and we will reacquire the lock before looking at state again. - // This is the main decode function of the decoder and while keeping - // the lock for its duration would be fine, it would defeat the purpose - // of having a separate decoder thread. - base::AutoUnlock auto_unlock(lock_); - TRACE_EVENT0("media,gpu", "VAVDA::Decode"); - res = decoder_->Decode(); - } - - switch (res) { - case AcceleratedVideoDecoder::kConfigChange: { - const uint8_t bit_depth = decoder_->GetBitDepth(); - RETURN_AND_NOTIFY_ON_FAILURE( - bit_depth == 8u, - "Unsupported bit depth: " << base::strict_cast(bit_depth), - PLATFORM_FAILURE, ); - // The visible rect should be a subset of the picture size. Otherwise, - // the encoded stream is bad. - const gfx::Size pic_size = decoder_->GetPicSize(); - const gfx::Rect visible_rect = decoder_->GetVisibleRect(); - RETURN_AND_NOTIFY_ON_FAILURE( - gfx::Rect(pic_size).Contains(visible_rect), - "The visible rectangle is not contained by the picture size", - UNREADABLE_INPUT, ); - VLOGF(2) << "Decoder requesting a new set of surfaces"; - size_t required_num_of_pictures = decoder_->GetRequiredNumOfPictures(); - if (buffer_allocation_mode_ == BufferAllocationMode::kNone && - profile_ >= H264PROFILE_MIN && profile_ <= H264PROFILE_MAX) { - // For H.264, the decoder might request too few pictures. In - // BufferAllocationMode::kNone, this can cause us to do a lot of busy - // work waiting for picture buffers to come back from the client (see - // crbug.com/910986#c32). This is a workaround to increase the - // likelihood that we don't have to wait on buffers to come back from - // the client. |kNumOfPics| is picked to mirror the value returned by - // VP9Decoder::GetRequiredNumOfPictures(). - constexpr size_t kMinNumOfPics = 13u; - required_num_of_pictures = - std::max(kMinNumOfPics, required_num_of_pictures); - } - - // Notify |decoder_delegate_| of an imminent VAContextID destruction, so - // it can destroy any internal structures making use of it. - decoder_delegate_->OnVAContextDestructionSoon(); - - task_runner_->PostTask( - FROM_HERE, - base::BindOnce( - &VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange, - weak_this_, required_num_of_pictures, pic_size, - decoder_->GetNumReferenceFrames(), visible_rect)); - // We'll get rescheduled once ProvidePictureBuffers() finishes. - return; - } - case AcceleratedVideoDecoder::kRanOutOfStreamData: - ReturnCurrInputBuffer_Locked(); - break; - - case AcceleratedVideoDecoder::kRanOutOfSurfaces: - // No more output buffers in the decoder, try getting more or go to - // sleep waiting for them. - if (!WaitForSurfaces_Locked()) - return; - - break; - - case AcceleratedVideoDecoder::kNeedContextUpdate: - // This should not happen as we return false from - // NeedsCompressedHeaderParsed(). - NOTREACHED() << "Context updates not supported"; - return; - - case AcceleratedVideoDecoder::kDecodeError: - RETURN_AND_NOTIFY_ON_FAILURE(false, "Error decoding stream", - PLATFORM_FAILURE, ); - return; - - case AcceleratedVideoDecoder::kTryAgain: - NOTREACHED() << "Should not reach here unless this class accepts " - "encrypted streams."; - RETURN_AND_NOTIFY_ON_FAILURE(false, "Error decoding stream", - PLATFORM_FAILURE, ); - return; - } - } -} - -void VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange( - size_t num_pics, - gfx::Size size, - size_t num_reference_frames, - const gfx::Rect& visible_rect) { - DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK(!awaiting_va_surfaces_recycle_); - DCHECK_GT(num_pics, num_reference_frames); - - // At this point decoder has stopped running and has already posted onto our - // loop any remaining output request callbacks, which executed before we got - // here. Some of them might have been pended though, because we might not have - // had enough PictureBuffers to output surfaces to. Initiate a wait cycle, - // which will wait for client to return enough PictureBuffers to us, so that - // we can finish all pending output callbacks, releasing associated surfaces. - awaiting_va_surfaces_recycle_ = true; - - requested_pic_size_ = size; - requested_visible_rect_ = visible_rect; - if (buffer_allocation_mode_ == BufferAllocationMode::kSuperReduced) { - // Add one to the reference frames for the one being currently egressed. - requested_num_reference_frames_ = num_reference_frames + 4; - requested_num_pics_ = num_pics - num_reference_frames; - } else if (buffer_allocation_mode_ == BufferAllocationMode::kReduced) { - // Add one to the reference frames for the one being currently egressed, - // and an extra allocation for both |client_| and |decoder_|. - requested_num_reference_frames_ = num_reference_frames + 5; - requested_num_pics_ = num_pics - num_reference_frames + 1; - } else { - requested_num_reference_frames_ = 0; - requested_num_pics_ = num_pics + num_extra_pics_; - } - - VLOGF(2) << " |requested_num_pics_| = " << requested_num_pics_ - << "; |requested_num_reference_frames_| = " - << requested_num_reference_frames_; - - TryFinishSurfaceSetChange(); -} - -void VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange() { - DCHECK(task_runner_->BelongsToCurrentThread()); - - if (!awaiting_va_surfaces_recycle_) - return; - - base::AutoLock auto_lock(lock_); - const size_t expected_max_available_va_surfaces = - IsBufferAllocationModeReducedOrSuperReduced() - ? previously_requested_num_reference_frames_ - : pictures_.size(); - if (!pending_output_cbs_.empty() || - expected_max_available_va_surfaces != available_va_surfaces_.size()) { - // If we're here the stream resolution has changed; we need to wait until: - // - all |pending_output_cbs_| have been executed - // - all VASurfaces are back to |available_va_surfaces_|; we can't use - // |requested_num_reference_frames_| for comparison, since it might have - // changed in the previous call to InitiateSurfaceSetChange(), so we use - // |previously_requested_num_reference_frames_| instead. - DVLOGF(2) << "Awaiting pending output/surface release callbacks to finish"; - task_runner_->PostTask( - FROM_HERE, - base::BindOnce(&VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange, - weak_this_)); - return; - } - - previously_requested_num_reference_frames_ = requested_num_reference_frames_; - - // All surfaces released, destroy them and dismiss all PictureBuffers. - awaiting_va_surfaces_recycle_ = false; - - const VideoCodecProfile new_profile = decoder_->GetProfile(); - if (profile_ != new_profile) { - profile_ = new_profile; - auto new_vaapi_wrapper = VaapiWrapper::CreateForVideoCodec( - VaapiWrapper::kDecode, profile_, EncryptionScheme::kUnencrypted, - base::BindRepeating(&ReportVaapiErrorToUMA, - "Media.VaapiVideoDecodeAccelerator.VAAPIError"), - /*enforce_sequence_affinity=*/false); - RETURN_AND_NOTIFY_ON_FAILURE(new_vaapi_wrapper.get(), - "Failed creating VaapiWrapper", - INVALID_ARGUMENT, ); - decoder_delegate_->set_vaapi_wrapper(new_vaapi_wrapper.get()); - vaapi_wrapper_ = std::move(new_vaapi_wrapper); - } else { - vaapi_wrapper_->DestroyContext(); - } - - available_va_surfaces_.clear(); - - for (auto iter = pictures_.begin(); iter != pictures_.end(); ++iter) { - VLOGF(2) << "Dismissing picture id: " << iter->first; - if (client_) - client_->DismissPictureBuffer(iter->first); - } - pictures_.clear(); - - // And ask for a new set as requested. - VLOGF(2) << "Requesting " << requested_num_pics_ - << " pictures of size: " << requested_pic_size_.ToString() - << " and visible rectangle = " << requested_visible_rect_.ToString(); - - const absl::optional format = - GfxBufferFormatToVideoPixelFormat( - vaapi_picture_factory_->GetBufferFormat()); - CHECK(format); - task_runner_->PostTask( - FROM_HERE, base::BindOnce(&Client::ProvidePictureBuffersWithVisibleRect, - client_, requested_num_pics_, *format, 1, - requested_pic_size_, requested_visible_rect_, - vaapi_picture_factory_->GetGLTextureTarget())); - // |client_| may respond via AssignPictureBuffers(). -} - -void VaapiVideoDecodeAccelerator::Decode(BitstreamBuffer bitstream_buffer) { - Decode(bitstream_buffer.ToDecoderBuffer(), bitstream_buffer.id()); -} - -void VaapiVideoDecodeAccelerator::Decode(scoped_refptr buffer, - int32_t bitstream_id) { - DCHECK(task_runner_->BelongsToCurrentThread()); - TRACE_EVENT1("media,gpu", "VAVDA::Decode", "Buffer id", bitstream_id); - - if (bitstream_id < 0) { - LOG(ERROR) << "Invalid bitstream_buffer, id: " << bitstream_id; - NotifyError(INVALID_ARGUMENT); - return; - } - - if (!buffer) { - if (client_) - client_->NotifyEndOfBitstreamBuffer(bitstream_id); - return; - } - - QueueInputBuffer(std::move(buffer), bitstream_id); -} - -void VaapiVideoDecodeAccelerator::AssignPictureBuffers( - const std::vector& buffers) { - DCHECK(task_runner_->BelongsToCurrentThread()); - base::AutoLock auto_lock(lock_); - DCHECK(pictures_.empty()); - - available_picture_buffers_.clear(); - - RETURN_AND_NOTIFY_ON_FAILURE( - buffers.size() >= requested_num_pics_, - "Got an invalid number of picture buffers. (Got " << buffers.size() - << ", requested " << requested_num_pics_ << ")", INVALID_ARGUMENT, ); - // requested_pic_size_ can be adjusted by VDA client. We should update - // |requested_pic_size_| by buffers[0].size(). But AMD driver doesn't decode - // frames correctly if the surface stride is different from the width of a - // coded size. - // TODO(b/139460315): Save buffers[0].size() as |adjusted_size_| once the - // AMD driver issue is resolved. - - va_surface_format_ = GetVaFormatForVideoCodecProfile(profile_); - std::vector va_surface_ids; - scoped_refptr vaapi_wrapper_for_picture = vaapi_wrapper_; - - const bool requires_vpp = - vaapi_picture_factory_->NeedsProcessingPipelineForDownloading(); - // If we aren't in BufferAllocationMode::kNone mode and the VaapiPicture - // implementation we get from |vaapi_picture_factory_| requires the video - // processing pipeline for downloading the decoded frame from the internal - // surface, we need to create a |vpp_vaapi_wrapper_|. - if (requires_vpp && buffer_allocation_mode_ != BufferAllocationMode::kNone && - buffer_allocation_mode_ != BufferAllocationMode::kWrapVdpau && - IsVppProfileSupported()) { - if (!vpp_vaapi_wrapper_) { - vpp_vaapi_wrapper_ = VaapiWrapper::Create( - VaapiWrapper::kVideoProcess, VAProfileNone, - EncryptionScheme::kUnencrypted, - base::BindRepeating( - &ReportVaapiErrorToUMA, - "Media.VaapiVideoDecodeAccelerator.Vpp.VAAPIError"), - /*enforce_sequence_affinity=*/false); - RETURN_AND_NOTIFY_ON_FAILURE(vpp_vaapi_wrapper_, - "Failed to initialize VppVaapiWrapper", - PLATFORM_FAILURE, ); - // Size is irrelevant for a VPP context. - RETURN_AND_NOTIFY_ON_FAILURE( - vpp_vaapi_wrapper_->CreateContext(gfx::Size()), - "Failed to create Context", PLATFORM_FAILURE, ); - } - vaapi_wrapper_for_picture = vpp_vaapi_wrapper_; - } - - for (size_t i = 0; i < buffers.size(); ++i) { - // TODO(b/139460315): Create with buffers[i] once the AMD driver issue is - // resolved. - PictureBuffer buffer = buffers[i]; - buffer.set_size(requested_pic_size_); - - // Note that the |size_to_bind| is not relevant in IMPORT mode. - const gfx::Size size_to_bind = - (output_mode_ == Config::OutputMode::ALLOCATE) - ? GetRectSizeFromOrigin(requested_visible_rect_) - : gfx::Size(); - - std::unique_ptr picture = vaapi_picture_factory_->Create( - vaapi_wrapper_for_picture, make_context_current_cb_, bind_image_cb_, - buffer, size_to_bind); - RETURN_AND_NOTIFY_ON_FAILURE(picture, "Failed creating a VaapiPicture", - PLATFORM_FAILURE, ); - - if (output_mode_ == Config::OutputMode::ALLOCATE) { - RETURN_AND_NOTIFY_ON_STATUS( - picture->Allocate(vaapi_picture_factory_->GetBufferFormat()), ); - - available_picture_buffers_.push_back(buffers[i].id()); - VASurfaceID va_surface_id = picture->va_surface_id(); - if (va_surface_id != VA_INVALID_ID) - va_surface_ids.push_back(va_surface_id); - } - - DCHECK(!base::Contains(pictures_, buffers[i].id())); - pictures_[buffers[i].id()] = std::move(picture); - - surfaces_available_.Signal(); - } - - base::RepeatingCallback va_surface_release_cb; - - // If we aren't in BufferAllocationMode::kNone, we use |va_surface_ids| for - // decode, otherwise ask |vaapi_wrapper_| to allocate them for us. - if (buffer_allocation_mode_ == BufferAllocationMode::kNone) { - DCHECK(!va_surface_ids.empty()); - RETURN_AND_NOTIFY_ON_FAILURE( - vaapi_wrapper_->CreateContext(requested_pic_size_), - "Failed creating VA Context", PLATFORM_FAILURE, ); - DCHECK_EQ(va_surface_ids.size(), buffers.size()); - - va_surface_release_cb = base::DoNothing(); - } else { - const size_t requested_num_surfaces = - IsBufferAllocationModeReducedOrSuperReduced() - ? requested_num_reference_frames_ - : pictures_.size(); - CHECK_NE(requested_num_surfaces, 0u); - va_surface_ids.clear(); - - RETURN_AND_NOTIFY_ON_FAILURE( - vaapi_wrapper_->CreateContextAndSurfaces( - va_surface_format_, requested_pic_size_, - {VaapiWrapper::SurfaceUsageHint::kVideoDecoder}, - requested_num_surfaces, &va_surface_ids), - "Failed creating VA Surfaces", PLATFORM_FAILURE, ); - - va_surface_release_cb = - base::BindRepeating(&VaapiWrapper::DestroySurface, vaapi_wrapper_); - } - - for (const VASurfaceID va_surface_id : va_surface_ids) { - available_va_surfaces_.emplace_back(std::make_unique( - va_surface_id, va_surface_release_cb)); - } - - // Resume DecodeTask if it is still in decoding state. - if (state_ == kDecoding) { - decoder_thread_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&VaapiVideoDecodeAccelerator::DecodeTask, - base::Unretained(this))); - } -} - -#if defined(USE_OZONE) -void VaapiVideoDecodeAccelerator::ImportBufferForPicture( - int32_t picture_buffer_id, - VideoPixelFormat pixel_format, - gfx::GpuMemoryBufferHandle gpu_memory_buffer_handle) { - VLOGF(2) << "Importing picture id: " << picture_buffer_id; - DCHECK(task_runner_->BelongsToCurrentThread()); - - if (output_mode_ != Config::OutputMode::IMPORT) { - LOG(ERROR) << "Cannot import in non-import mode"; - NotifyError(INVALID_ARGUMENT); - return; - } - - { - base::AutoLock auto_lock(lock_); - if (!pictures_.count(picture_buffer_id)) { - // It's possible that we've already posted a DismissPictureBuffer for this - // picture, but it has not yet executed when this ImportBufferForPicture - // was posted to us by the client. In that case just ignore this (we've - // already dismissed it and accounted for that). - DVLOGF(3) << "got picture id=" << picture_buffer_id - << " not in use (anymore?)."; - return; - } - - auto buffer_format = VideoPixelFormatToGfxBufferFormat(pixel_format); - if (!buffer_format) { - LOG(ERROR) << "Unsupported format: " << pixel_format; - NotifyError(INVALID_ARGUMENT); - return; - } - - VaapiPicture* picture = pictures_[picture_buffer_id].get(); - if (!picture->ImportGpuMemoryBufferHandle( - *buffer_format, std::move(gpu_memory_buffer_handle))) { - // ImportGpuMemoryBufferHandle will close the handles even on failure, so - // we don't need to do this ourselves. - LOG(ERROR) << "Failed to import GpuMemoryBufferHandle"; - NotifyError(PLATFORM_FAILURE); - return; - } - } - - ReusePictureBuffer(picture_buffer_id); -} -#endif - -void VaapiVideoDecodeAccelerator::ReusePictureBuffer( - int32_t picture_buffer_id) { - DVLOGF(4) << "picture id=" << picture_buffer_id; - DCHECK(task_runner_->BelongsToCurrentThread()); - TRACE_EVENT1("media,gpu", "VAVDA::ReusePictureBuffer", "Picture id", - picture_buffer_id); - - { - base::AutoLock auto_lock(lock_); - - if (!pictures_.count(picture_buffer_id)) { - // It's possible that we've already posted a DismissPictureBuffer for this - // picture, but it has not yet executed when this ReusePictureBuffer - // was posted to us by the client. In that case just ignore this (we've - // already dismissed it and accounted for that). - DVLOGF(3) << "got picture id=" << picture_buffer_id - << " not in use (anymore?)."; - return; - } - - available_picture_buffers_.push_back(picture_buffer_id); - TRACE_COUNTER_ID2("media,gpu", "Vaapi frames at client", this, "used", - pictures_.size() - available_picture_buffers_.size(), - "available", available_picture_buffers_.size()); - } - - TryOutputPicture(); -} - -void VaapiVideoDecodeAccelerator::FlushTask() { - VLOGF(2); - DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); - DCHECK(curr_input_buffer_ && curr_input_buffer_->IsFlushRequest()); - - curr_input_buffer_.reset(); - - // First flush all the pictures that haven't been outputted, notifying the - // client to output them. - bool res = decoder_->Flush(); - RETURN_AND_NOTIFY_ON_FAILURE(res, "Failed flushing the decoder.", - PLATFORM_FAILURE, ); - - // Put the decoder in idle state, ready to resume. - decoder_->Reset(); - - task_runner_->PostTask( - FROM_HERE, - base::BindOnce(&VaapiVideoDecodeAccelerator::FinishFlush, weak_this_)); -} - -void VaapiVideoDecodeAccelerator::Flush() { - VLOGF(2) << "Got flush request"; - DCHECK(task_runner_->BelongsToCurrentThread()); - - QueueInputBuffer(DecoderBuffer::CreateEOSBuffer(), -1); -} - -void VaapiVideoDecodeAccelerator::FinishFlush() { - VLOGF(2); - DCHECK(task_runner_->BelongsToCurrentThread()); - - finish_flush_pending_ = false; - - base::AutoLock auto_lock(lock_); - if (state_ != kDecoding) { - DCHECK(state_ == kDestroying || state_ == kResetting) << state_; - return; - } - - // Still waiting for textures from client to finish outputting all pending - // frames. Try again later. - if (!pending_output_cbs_.empty()) { - finish_flush_pending_ = true; - return; - } - - // Resume decoding if necessary. - if (input_buffers_.empty()) { - state_ = kIdle; - } else { - decoder_thread_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&VaapiVideoDecodeAccelerator::DecodeTask, - base::Unretained(this))); - } - - task_runner_->PostTask(FROM_HERE, - base::BindOnce(&Client::NotifyFlushDone, client_)); -} - -void VaapiVideoDecodeAccelerator::ResetTask() { - VLOGF(2); - DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); - - // All the decoding tasks from before the reset request from client are done - // by now, as this task was scheduled after them and client is expected not - // to call Decode() after Reset() and before NotifyResetDone. - decoder_->Reset(); - - base::AutoLock auto_lock(lock_); - - // Return current input buffer, if present. - if (curr_input_buffer_) - ReturnCurrInputBuffer_Locked(); - - // And let client know that we are done with reset. - task_runner_->PostTask( - FROM_HERE, - base::BindOnce(&VaapiVideoDecodeAccelerator::FinishReset, weak_this_)); -} - -void VaapiVideoDecodeAccelerator::Reset() { - VLOGF(2) << "Got reset request"; - DCHECK(task_runner_->BelongsToCurrentThread()); - - // This will make any new decode tasks exit early. - base::AutoLock auto_lock(lock_); - state_ = kResetting; - finish_flush_pending_ = false; - - // Drop all remaining input buffers, if present. - while (!input_buffers_.empty()) - input_buffers_.pop(); - TRACE_COUNTER1("media,gpu", "Vaapi input buffers", input_buffers_.size()); - - decoder_thread_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&VaapiVideoDecodeAccelerator::ResetTask, - base::Unretained(this))); - - input_ready_.Signal(); - surfaces_available_.Signal(); -} - -void VaapiVideoDecodeAccelerator::FinishReset() { - VLOGF(2); - DCHECK(task_runner_->BelongsToCurrentThread()); - base::AutoLock auto_lock(lock_); - - if (state_ != kResetting) { - DCHECK(state_ == kDestroying || state_ == kUninitialized) << state_; - return; // We could've gotten destroyed already. - } - - // Drop pending outputs. - while (!pending_output_cbs_.empty()) - pending_output_cbs_.pop(); - - if (awaiting_va_surfaces_recycle_) { - // Decoder requested a new surface set while we were waiting for it to - // finish the last DecodeTask, running at the time of Reset(). - // Let the surface set change finish first before resetting. - task_runner_->PostTask( - FROM_HERE, - base::BindOnce(&VaapiVideoDecodeAccelerator::FinishReset, weak_this_)); - return; - } - - state_ = kIdle; - - task_runner_->PostTask(FROM_HERE, - base::BindOnce(&Client::NotifyResetDone, client_)); - - // The client might have given us new buffers via Decode() while we were - // resetting and might be waiting for our move, and not call Decode() anymore - // until we return something. Post a DecodeTask() so that we won't - // sleep forever waiting for Decode() in that case. Having two of them - // in the pipe is harmless, the additional one will return as soon as it sees - // that we are back in kDecoding state. - if (!input_buffers_.empty()) { - state_ = kDecoding; - decoder_thread_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&VaapiVideoDecodeAccelerator::DecodeTask, - base::Unretained(this))); - } -} - -void VaapiVideoDecodeAccelerator::Cleanup() { - DCHECK(task_runner_->BelongsToCurrentThread()); - - base::AutoLock auto_lock(lock_); - if (state_ == kUninitialized || state_ == kDestroying) - return; - - VLOGF(2) << "Destroying VAVDA"; - state_ = kDestroying; - - // Call DismissPictureBuffer() to notify |client_| that the picture buffers - // are no longer used and thus |client_| shall release them. If |client_| has - // been invalidated in NotifyError(),|client_| will be destroyed shortly. The - // destruction should release all the PictureBuffers. - if (client_) { - for (const auto& id_and_picture : pictures_) - client_->DismissPictureBuffer(id_and_picture.first); - } - pictures_.clear(); - - client_ptr_factory_.reset(); - weak_this_factory_.InvalidateWeakPtrs(); - - // TODO(mcasas): consider deleting |decoder_| on - // |decoder_thread_task_runner_|, https://crbug.com/789160. - - // Signal all potential waiters on the decoder_thread_, let them early-exit, - // as we've just moved to the kDestroying state, and wait for all tasks - // to finish. - input_ready_.Signal(); - surfaces_available_.Signal(); - { - base::AutoUnlock auto_unlock(lock_); - decoder_thread_.Stop(); - } - if (buffer_allocation_mode_ != BufferAllocationMode::kNone) - available_va_surfaces_.clear(); - - // Notify |decoder_delegate_| of an imminent VAContextID destruction, so it - // can destroy any internal structures making use of it. At this point - // |decoder_thread_| is stopped so we can access |decoder_delegate_| from - // |task_runner_|. - decoder_delegate_->OnVAContextDestructionSoon(); - vaapi_wrapper_->DestroyContext(); - - if (vpp_vaapi_wrapper_) - vpp_vaapi_wrapper_->DestroyContext(); - state_ = kUninitialized; -} - -void VaapiVideoDecodeAccelerator::Destroy() { - DCHECK(task_runner_->BelongsToCurrentThread()); - Cleanup(); - delete this; -} - -bool VaapiVideoDecodeAccelerator::TryToSetupDecodeOnSeparateThread( - const base::WeakPtr& decode_client, - const scoped_refptr& decode_task_runner) { - return false; -} - -void VaapiVideoDecodeAccelerator::SurfaceReady( - scoped_refptr dec_surface, - int32_t bitstream_id, - const gfx::Rect& visible_rect, - const VideoColorSpace& color_space) { - if (!task_runner_->BelongsToCurrentThread()) { - task_runner_->PostTask( - FROM_HERE, base::BindOnce(&VaapiVideoDecodeAccelerator::SurfaceReady, - weak_this_, std::move(dec_surface), - bitstream_id, visible_rect, color_space)); - return; - } - - DCHECK(!awaiting_va_surfaces_recycle_); - - { - base::AutoLock auto_lock(lock_); - // Drop any requests to output if we are resetting or being destroyed. - if (state_ == kResetting || state_ == kDestroying) - return; - } - pending_output_cbs_.push(base::BindOnce( - &VaapiVideoDecodeAccelerator::OutputPicture, weak_this_, - std::move(dec_surface), bitstream_id, visible_rect, color_space)); - - TryOutputPicture(); -} - -scoped_refptr VaapiVideoDecodeAccelerator::CreateSurface() { - DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); - base::AutoLock auto_lock(lock_); - - if (available_va_surfaces_.empty()) - return nullptr; - - DCHECK_NE(VA_INVALID_ID, va_surface_format_); - DCHECK(!awaiting_va_surfaces_recycle_); - if (buffer_allocation_mode_ != BufferAllocationMode::kNone) { - auto va_surface_id = std::move(available_va_surfaces_.front()); - const VASurfaceID id = va_surface_id->id(); - available_va_surfaces_.pop_front(); - - TRACE_COUNTER_ID2("media,gpu", "Vaapi VASurfaceIDs", this, "used", - (IsBufferAllocationModeReducedOrSuperReduced() - ? requested_num_reference_frames_ - : pictures_.size()) - - available_va_surfaces_.size(), - "available", available_va_surfaces_.size()); - - return new VASurface( - id, requested_pic_size_, va_surface_format_, - base::BindOnce(va_surface_recycle_cb_, std::move(va_surface_id))); - } - - // Find the first |available_va_surfaces_| id such that the associated - // |pictures_| entry is marked as |available_picture_buffers_|. In practice, - // we will quickly find an available |va_surface_id|. - for (auto it = available_va_surfaces_.begin(); - it != available_va_surfaces_.end(); ++it) { - const VASurfaceID va_surface_id = (*it)->id(); - for (const auto& id_and_picture : pictures_) { - if (id_and_picture.second->va_surface_id() == va_surface_id && - base::Contains(available_picture_buffers_, id_and_picture.first)) { - // Remove |va_surface_id| from the list of availables, and use the id - // to return a new VASurface. - auto va_surface = std::move(*it); - available_va_surfaces_.erase(it); - return new VASurface( - va_surface_id, requested_pic_size_, va_surface_format_, - base::BindOnce(va_surface_recycle_cb_, std::move(va_surface))); - } - } - } - return nullptr; -} - -void VaapiVideoDecodeAccelerator::RecycleVASurface( - std::unique_ptr va_surface, - // We don't use |va_surface_id| but it must be here because this method is - // bound as VASurface::ReleaseCB. - VASurfaceID /*va_surface_id*/) { - DCHECK(task_runner_->BelongsToCurrentThread()); - - { - base::AutoLock auto_lock(lock_); - available_va_surfaces_.push_back(std::move(va_surface)); - - if (buffer_allocation_mode_ != BufferAllocationMode::kNone) { - TRACE_COUNTER_ID2("media,gpu", "Vaapi VASurfaceIDs", this, "used", - (IsBufferAllocationModeReducedOrSuperReduced() - ? requested_num_reference_frames_ - : pictures_.size()) - - available_va_surfaces_.size(), - "available", available_va_surfaces_.size()); - } - surfaces_available_.Signal(); - } - - TryOutputPicture(); -} - -// static -VideoDecodeAccelerator::SupportedProfiles -VaapiVideoDecodeAccelerator::GetSupportedProfiles() { - VideoDecodeAccelerator::SupportedProfiles profiles = - VaapiWrapper::GetSupportedDecodeProfiles(); - // VaVDA never supported VP9 Profile 2, AV1 and HEVC, but VaapiWrapper does. - // Filter them out. - base::EraseIf(profiles, [](const auto& profile) { - VideoCodec codec = VideoCodecProfileToVideoCodec(profile.profile); - return profile.profile == VP9PROFILE_PROFILE2 || - codec == VideoCodec::kAV1 || codec == VideoCodec::kHEVC; - }); - return profiles; -} - -// static -bool VaapiVideoDecodeAccelerator::IsVppProfileSupported() { - return VaapiWrapper::IsVppProfileSupported(); -} - -VaapiVideoDecodeAccelerator::BufferAllocationMode -VaapiVideoDecodeAccelerator::DecideBufferAllocationMode() { -#if BUILDFLAG(USE_VAAPI_X11) - // The IMPORT mode is used for Android on Chrome OS, so this doesn't apply - // here. - DCHECK_NE(output_mode_, VideoDecodeAccelerator::Config::OutputMode::IMPORT); - // TODO(crbug/1116701): get video decode acceleration working with ozone. - // For H.264 on older devices, another +1 is experimentally needed for - // high-to-high resolution changes. - // TODO(mcasas): Figure out why and why only H264, see crbug.com/912295 and - // http://crrev.com/c/1363807/9/media/gpu/h264_decoder.cc#1449. - if (profile_ >= H264PROFILE_MIN && profile_ <= H264PROFILE_MAX) - return BufferAllocationMode::kReduced; - return BufferAllocationMode::kSuperReduced; -#else - // NVIDIA blobs use VDPAU - if (VaapiWrapper::GetImplementationType() == VAImplementation::kNVIDIAVDPAU) { - LOG(INFO) << "VA-API driver on VDPAU backend"; - return BufferAllocationMode::kWrapVdpau; - } - - // TODO(crbug.com/912295): Enable a better BufferAllocationMode for IMPORT - // |output_mode_| as well. - if (output_mode_ == VideoDecodeAccelerator::Config::OutputMode::IMPORT) - return BufferAllocationMode::kNormal; - - // On Gemini Lake, Kaby Lake and later we can pass to libva the client's - // PictureBuffers to decode onto, which skips the use of the Vpp unit and its - // associated format reconciliation copy, avoiding all internal buffer - // allocations. - // TODO(crbug.com/911754): Enable for VP9 Profile 2. - if (false && IsGeminiLakeOrLater() && - (profile_ == VP9PROFILE_PROFILE0 || profile_ == VP8PROFILE_ANY || - (profile_ >= H264PROFILE_MIN && profile_ <= H264PROFILE_MAX))) { - // Add one to the reference frames for the one being currently egressed, and - // an extra allocation for both |client_| and |decoder_|, see - // crrev.com/c/1576560. - if (profile_ == VP8PROFILE_ANY) - num_extra_pics_ = 3; - return BufferAllocationMode::kNone; - } - - // For H.264 on older devices, another +1 is experimentally needed for - // high-to-high resolution changes. - // TODO(mcasas): Figure out why and why only H264, see crbug.com/912295 and - // http://crrev.com/c/1363807/9/media/gpu/h264_decoder.cc#1449. - if (profile_ >= H264PROFILE_MIN && profile_ <= H264PROFILE_MAX) - return BufferAllocationMode::kReduced; - - // If we're here, we have to use the Vpp unit and allocate buffers for - // |decoder_|; usually we'd have to allocate the |decoder_|s - // GetRequiredNumOfPictures() internally, we can allocate just |decoder_|s - // GetNumReferenceFrames() + 1. Moreover, we also request the |client_| to - // allocate less than the usual |decoder_|s GetRequiredNumOfPictures(). - return BufferAllocationMode::kSuperReduced; -#endif -} - -bool VaapiVideoDecodeAccelerator::IsBufferAllocationModeReducedOrSuperReduced() - const { - return buffer_allocation_mode_ == BufferAllocationMode::kSuperReduced || - buffer_allocation_mode_ == BufferAllocationMode::kReduced; -} - -bool VaapiVideoDecodeAccelerator::OnMemoryDump( - const base::trace_event::MemoryDumpArgs& args, - base::trace_event::ProcessMemoryDump* pmd) { - using base::trace_event::MemoryAllocatorDump; - base::AutoLock auto_lock(lock_); - if (buffer_allocation_mode_ == BufferAllocationMode::kNone || - !requested_num_reference_frames_) { - return false; - } - - auto dump_name = base::StringPrintf("gpu/vaapi/decoder/0x%" PRIxPTR, - reinterpret_cast(this)); - MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(dump_name); - - constexpr float kNumBytesPerPixelYUV420 = 12.0 / 8; - constexpr float kNumBytesPerPixelYUV420_10bpp = 2 * kNumBytesPerPixelYUV420; - DCHECK(va_surface_format_ == VA_RT_FORMAT_YUV420 || - va_surface_format_ == VA_RT_FORMAT_YUV420_10BPP); - const float va_surface_bytes_per_pixel = - va_surface_format_ == VA_RT_FORMAT_YUV420 ? kNumBytesPerPixelYUV420 - : kNumBytesPerPixelYUV420_10bpp; - // Report |requested_num_surfaces| and the associated memory size. The - // calculated size is an estimation since we don't know the internal VA - // strides, texture compression, headers, etc, but is a good lower boundary. - const size_t requested_num_surfaces = - IsBufferAllocationModeReducedOrSuperReduced() - ? requested_num_reference_frames_ - : pictures_.size(); - dump->AddScalar(MemoryAllocatorDump::kNameSize, - MemoryAllocatorDump::kUnitsBytes, - static_cast(requested_num_surfaces * - requested_pic_size_.GetArea() * - va_surface_bytes_per_pixel)); - dump->AddScalar(MemoryAllocatorDump::kNameObjectCount, - MemoryAllocatorDump::kUnitsObjects, - static_cast(requested_num_surfaces)); - - return true; -} - -} // namespace media diff --git a/media/gpu/vaapi/vaapi_video_decode_accelerator.h b/media/gpu/vaapi/vaapi_video_decode_accelerator.h deleted file mode 100644 index 25400a80..00000000 --- a/media/gpu/vaapi/vaapi_video_decode_accelerator.h +++ /dev/null @@ -1,368 +0,0 @@ -// Copyright (c) 2022 The Chromium Authors and Alex313031. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// This file contains an implementation of VideoDecoderAccelerator -// that utilizes hardware video decoder present on Intel CPUs. - -#ifndef MEDIA_GPU_VAAPI_VAAPI_VIDEO_DECODE_ACCELERATOR_H_ -#define MEDIA_GPU_VAAPI_VAAPI_VIDEO_DECODE_ACCELERATOR_H_ - -#include -#include - -#include -#include -#include -#include -#include - -#include "base/containers/queue.h" -#include "base/containers/small_map.h" -#include "base/memory/raw_ptr.h" -#include "base/memory/weak_ptr.h" -#include "base/synchronization/condition_variable.h" -#include "base/synchronization/lock.h" -#include "base/task/single_thread_task_runner.h" -#include "base/thread_annotations.h" -#include "base/threading/thread.h" -#include "base/trace_event/memory_dump_provider.h" -#include "media/base/bitstream_buffer.h" -#include "media/gpu/decode_surface_handler.h" -#include "media/gpu/gpu_video_decode_accelerator_helpers.h" -#include "media/gpu/media_gpu_export.h" -#include "media/gpu/vaapi/vaapi_picture_factory.h" -#include "media/gpu/vaapi/vaapi_wrapper.h" -#include "media/video/picture.h" -#include "media/video/video_decode_accelerator.h" - -namespace gl { -class GLImage; -} - -namespace media { - -class AcceleratedVideoDecoder; -template -class ScopedID; -class VaapiVideoDecoderDelegate; -class VaapiPicture; - -// Class to provide video decode acceleration for Intel systems with hardware -// support for it, and on which libva is available. -// Decoding tasks are performed in a separate decoding thread. -// -// Threading/life-cycle: this object is created & destroyed on the GPU -// ChildThread. A few methods on it are called on the decoder thread which is -// stopped during |this->Destroy()|, so any tasks posted to the decoder thread -// can assume |*this| is still alive. See |weak_this_| below for more details. -class MEDIA_GPU_EXPORT VaapiVideoDecodeAccelerator - : public VideoDecodeAccelerator, - public DecodeSurfaceHandler, - public base::trace_event::MemoryDumpProvider { - public: - VaapiVideoDecodeAccelerator( - const MakeGLContextCurrentCallback& make_context_current_cb, - const BindGLImageCallback& bind_image_cb); - - VaapiVideoDecodeAccelerator(const VaapiVideoDecodeAccelerator&) = delete; - VaapiVideoDecodeAccelerator& operator=(const VaapiVideoDecodeAccelerator&) = - delete; - - ~VaapiVideoDecodeAccelerator() override; - - // VideoDecodeAccelerator implementation. - bool Initialize(const Config& config, Client* client) override; - void Decode(BitstreamBuffer bitstream_buffer) override; - void Decode(scoped_refptr buffer, - int32_t bitstream_id) override; - void AssignPictureBuffers(const std::vector& buffers) override; -#if defined(USE_OZONE) - void ImportBufferForPicture( - int32_t picture_buffer_id, - VideoPixelFormat pixel_format, - gfx::GpuMemoryBufferHandle gpu_memory_buffer_handle) override; -#endif - void ReusePictureBuffer(int32_t picture_buffer_id) override; - void Flush() override; - void Reset() override; - void Destroy() override; - bool TryToSetupDecodeOnSeparateThread( - const base::WeakPtr& decode_client, - const scoped_refptr& decode_task_runner) - override; - - static VideoDecodeAccelerator::SupportedProfiles GetSupportedProfiles(); - - static bool IsVppProfileSupported(); - - // DecodeSurfaceHandler implementation. - scoped_refptr CreateSurface() override; - void SurfaceReady(scoped_refptr va_surface, - int32_t bitstream_id, - const gfx::Rect& visible_rect, - const VideoColorSpace& color_space) override; - - // base::trace_event::MemoryDumpProvider implementation. - bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args, - base::trace_event::ProcessMemoryDump* pmd) override; - - private: - friend class VaapiVideoDecodeAcceleratorTest; - - // An input buffer with id provided by the client and awaiting consumption. - class InputBuffer; - // A self-cleaning VASurfaceID. - using ScopedVASurfaceID = ScopedID; - - // Notify the client that an error has occurred and decoding cannot continue. - void NotifyError(Error error); - void NotifyStatus(VaapiStatus status); - - // Queue a input buffer for decode. - void QueueInputBuffer(scoped_refptr buffer, - int32_t bitstream_id); - - // Gets a new |current_input_buffer_| from |input_buffers_| and sets it up in - // |decoder_|. This method will sleep if no |input_buffers_| are available. - // Returns true if a new buffer has been set up, false if an early exit has - // been requested (due to initiated reset/flush/destroy). - bool GetCurrInputBuffer_Locked() EXCLUSIVE_LOCKS_REQUIRED(lock_); - - // Signals the client that |curr_input_buffer_| has been read and can be - // returned. Will also release the mapping. - void ReturnCurrInputBuffer_Locked() EXCLUSIVE_LOCKS_REQUIRED(lock_); - - // Waits for more surfaces to become available. Returns true once they do or - // false if an early exit has been requested (due to an initiated - // reset/flush/destroy). - bool WaitForSurfaces_Locked() EXCLUSIVE_LOCKS_REQUIRED(lock_); - - // Continue decoding given input buffers and sleep waiting for input/output - // as needed. Will exit if a new set of surfaces or reset/flush/destroy - // is requested. - void DecodeTask(); - - // Scheduled after receiving a flush request and executed after the current - // decoding task finishes decoding pending inputs. Makes the decoder return - // all remaining output pictures and puts it in an idle state, ready - // to resume if needed and schedules a FinishFlush. - void FlushTask(); - - // Scheduled by the FlushTask after decoder is flushed to put VAVDA into idle - // state and notify the client that flushing has been finished. - void FinishFlush(); - - // Scheduled after receiving a reset request and executed after the current - // decoding task finishes decoding the current frame. Puts the decoder into - // an idle state, ready to resume if needed, discarding decoded but not yet - // outputted pictures (decoder keeps ownership of their associated picture - // buffers). Schedules a FinishReset afterwards. - void ResetTask(); - - // Scheduled by ResetTask after it's done putting VAVDA into an idle state. - // Drops remaining input buffers and notifies the client that reset has been - // finished. - void FinishReset(); - - // Helper for Destroy(), doing all the actual work except for deleting self. - void Cleanup(); - - // Get a usable framebuffer configuration for use in binding textures - // or return false on failure. - bool InitializeFBConfig(); - - // Callback to be executed once we have a |va_surface| to be output and an - // available VaapiPicture in |available_picture_buffers_| for output. Puts - // contents of |va_surface| into the latter, releases the surface and passes - // the resulting picture to |client_| along with |visible_rect|. - void OutputPicture(scoped_refptr va_surface, - int32_t input_id, - gfx::Rect visible_rect, - const VideoColorSpace& picture_color_space); - - // Try to OutputPicture() if we have both a ready surface and picture. - void TryOutputPicture(); - - // Called when a VASurface is no longer in use by |decoder_| nor |client_|. - // Returns it to |available_va_surfaces_|. |va_surface_id| is not used but it - // must be here to bind this method as VASurface::ReleaseCB. - void RecycleVASurface(std::unique_ptr va_surface, - VASurfaceID va_surface_id); - - // Request a new set of |num_pics| PictureBuffers to be allocated by - // |client_|. Up to |num_reference_frames| out of |num_pics_| might be needed - // by |decoder_|. - void InitiateSurfaceSetChange(size_t num_pics, - gfx::Size size, - size_t num_reference_frames, - const gfx::Rect& visible_rect); - - // Check if the surfaces have been released or post ourselves for later. - void TryFinishSurfaceSetChange(); - - // Different modes of internal buffer allocations. - enum class BufferAllocationMode { - // Only using |client_|s provided PictureBuffers, none internal. - kNone, - - // Using a reduced amount of |client_|s provided PictureBuffers and - // |decoder_|s GetNumReferenceFrames() internallly. - kSuperReduced, - - // Similar to kSuperReduced, but we have to increase slightly the amount of - // PictureBuffers allocated for the |client_|. - kReduced, - - // VaapiVideoDecodeAccelerator can work with this mode on all platforms. - // Using |client_|s provided PictureBuffers and as many internally - // allocated. - kNormal, - kWrapVdpau, - }; - - // Decides the concrete buffer allocation mode, depending on the hardware - // platform and other parameters. - BufferAllocationMode DecideBufferAllocationMode(); - bool IsBufferAllocationModeReducedOrSuperReduced() const; - - // VAVDA state. - enum State { - // Initialize() not called yet or failed. - kUninitialized, - // DecodeTask running. - kDecoding, - // Resetting, waiting for decoder to finish current task and cleanup. - kResetting, - // Idle, decoder in state ready to start/resume decoding. - kIdle, - // Destroying, waiting for the decoder to finish current task. - kDestroying, - }; - - base::Lock lock_; - State state_ GUARDED_BY(lock_); - // Only used on |task_runner_|. - Config::OutputMode output_mode_; - - // Queue of available InputBuffers. - base::queue> input_buffers_ GUARDED_BY(lock_); - // Signalled when input buffers are queued onto |input_buffers_| queue. - base::ConditionVariable input_ready_; - - // Current input buffer at decoder. Only used on |decoder_thread_task_runner_| - std::unique_ptr curr_input_buffer_; - - // Only used on |task_runner_|. - std::unique_ptr vaapi_picture_factory_; - - // The following variables are constructed/initialized in Initialize() when - // the codec information is received. |vaapi_wrapper_| is thread safe. - scoped_refptr vaapi_wrapper_; - // Only used on |decoder_thread_task_runner_|. - std::unique_ptr decoder_; - // TODO(crbug.com/1022246): Instead of having the raw pointer here, getting - // the pointer from AcceleratedVideoDecoder. - raw_ptr decoder_delegate_ = nullptr; - - // Filled in during Initialize(). - BufferAllocationMode buffer_allocation_mode_; - - // VaapiWrapper for VPP (Video Post Processing). This is used for copying - // from a decoded surface to a surface bound to client's PictureBuffer. - scoped_refptr vpp_vaapi_wrapper_; - - // All allocated VaapiPictures, regardless of their current state. Pictures - // are allocated at AssignPictureBuffers() and are kept until dtor or - // TryFinishSurfaceSetChange(). Comes after |vaapi_wrapper_| to ensure all - // pictures are destroyed before this is destroyed. - base::small_map>> pictures_ - GUARDED_BY(lock_); - // List of PictureBuffer ids available to be sent to |client_| via - // OutputPicture() (|client_| returns them via ReusePictureBuffer()). - std::list available_picture_buffers_ GUARDED_BY(lock_); - - // VASurfaces available and that can be passed to |decoder_| for its use upon - // CreateSurface() request (and then returned via RecycleVASurface()). - std::list> available_va_surfaces_ - GUARDED_BY(lock_); - // Signalled when output surfaces are queued into |available_va_surfaces_|. - base::ConditionVariable surfaces_available_; - // VASurfaceIDs format, filled in when created. - unsigned int va_surface_format_; - - // Pending output requests from the decoder. When it indicates that we should - // output a surface and we have an available Picture (i.e. texture) ready - // to use, we'll execute the callback passing the Picture. The callback - // will put the contents of the surface into the picture and return it to - // the client, releasing the surface as well. - // If we don't have any available |pictures_| at the time when the decoder - // requests output, we'll store the request in this queue for later and run it - // once the client gives us more textures via ReusePictureBuffer(). - // Only used on |task_runner_|. - base::queue pending_output_cbs_; - - // WeakPtr<> pointing to |this| for use in posting tasks from the decoder - // thread back to the ChildThread. Because the decoder thread is a member of - // this class, any task running on the decoder thread is guaranteed that this - // object is still alive. As a result, tasks posted from ChildThread to - // decoder thread should use base::Unretained(this), and tasks posted from the - // decoder thread to the ChildThread should use |weak_this_|. - base::WeakPtr weak_this_; - - // Callback used to recycle VASurfaces. Only used on |task_runner_|. - base::RepeatingCallback, VASurfaceID)> - va_surface_recycle_cb_; - - // To expose client callbacks from VideoDecodeAccelerator. Used only on - // |task_runner_|. - std::unique_ptr> client_ptr_factory_; - base::WeakPtr client_; - - // ChildThread's task runner. - const scoped_refptr task_runner_; - - base::Thread decoder_thread_; - // Use this to post tasks to |decoder_thread_| instead of - // |decoder_thread_.task_runner()| because the latter will be NULL once - // |decoder_thread_.Stop()| returns. - scoped_refptr decoder_thread_task_runner_; - - // Whether we are waiting for any |pending_output_cbs_| to be run before - // NotifyingFlushDone. Only used on |task_runner_|. - bool finish_flush_pending_; - - // Decoder requested a new surface set and we are waiting for all the surfaces - // to be returned before we can free them. Only used on |task_runner_|. - bool awaiting_va_surfaces_recycle_; - - // Last requested number/resolution/visible rectangle of output - // PictureBuffers. - size_t requested_num_pics_; - gfx::Size requested_pic_size_; - gfx::Rect requested_visible_rect_; - // Potential extra PictureBuffers to request, used only on - // BufferAllocationMode::kNone, see DecideBufferAllocationMode(). - size_t num_extra_pics_ = 0; - - // Max number of reference frames needed by |decoder_|. Only used on - // |task_runner_| and when in BufferAllocationMode::kNone. - size_t requested_num_reference_frames_; - size_t previously_requested_num_reference_frames_; - - // The video stream's profile. - VideoCodecProfile profile_; - - // Callback to make GL context current. - MakeGLContextCurrentCallback make_context_current_cb_; - - // Callback to bind a GLImage to a given texture. - BindGLImageCallback bind_image_cb_; - - // The WeakPtrFactory for |weak_this_|. - base::WeakPtrFactory weak_this_factory_; -}; - -} // namespace media - -#endif // MEDIA_GPU_VAAPI_VAAPI_VIDEO_DECODE_ACCELERATOR_H_ diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc deleted file mode 100644 index 3ab91558..00000000 --- a/media/gpu/vaapi/vaapi_wrapper.cc +++ /dev/null @@ -1,3578 +0,0 @@ -// Copyright 2022 The Chromium Authors and Alex313031. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "media/gpu/vaapi/vaapi_wrapper.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "base/bind.h" -#include "base/callback_helpers.h" -#include "base/containers/contains.h" -#include "base/containers/cxx20_erase.h" -#include "base/containers/fixed_flat_set.h" -#include "base/cpu.h" -#include "base/environment.h" -#include "base/files/scoped_file.h" -#include "base/logging.h" -#include "base/metrics/histogram_macros.h" -#include "base/no_destructor.h" -#include "base/numerics/checked_math.h" -#include "base/numerics/safe_conversions.h" -#include "base/posix/eintr_wrapper.h" -#include "base/strings/pattern.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/system/sys_info.h" -#include "base/trace_event/trace_event.h" -#include "build/build_config.h" -#include "build/chromeos_buildflags.h" -#include "media/base/media_switches.h" -#include "media/base/video_codecs.h" -#include "media/base/video_frame.h" -#include "media/base/video_types.h" -#include "media/gpu/macros.h" -// Auto-generated for dlopen libva libraries -#include "media/gpu/vaapi/va_stubs.h" -#include "media/media_buildflags.h" -#include "third_party/libva_protected_content/va_protected_content.h" -#include "third_party/libyuv/include/libyuv.h" -#include "ui/gfx/buffer_format_util.h" -#include "ui/gfx/buffer_types.h" -#include "ui/gfx/geometry/rect.h" -#include "ui/gfx/linux/drm_util_linux.h" -#include "ui/gfx/linux/native_pixmap_dmabuf.h" -#include "ui/gfx/native_pixmap.h" -#include "ui/gfx/native_pixmap_handle.h" -#include "ui/gl/gl_bindings.h" -#include "ui/gl/gl_implementation.h" - -#if BUILDFLAG(USE_VAAPI_X11) -typedef XID Drawable; - -extern "C" { -#include "media/gpu/vaapi/va_x11.sigs" -} - -#include "ui/gfx/x/connection.h" // nogncheck -#endif // BUILDFLAG(USE_VAAPI_X11) - -#if defined(USE_OZONE) -#include "ui/ozone/public/ozone_platform.h" -#include "ui/ozone/public/surface_factory_ozone.h" -#endif - -#if BUILDFLAG(IS_CHROMEOS_ASH) -#include -using media_gpu_vaapi::kModuleVa_prot; -#endif - -using media_gpu_vaapi::kModuleVa; -using media_gpu_vaapi::kModuleVa_drm; -#if BUILDFLAG(USE_VAAPI_X11) -using media_gpu_vaapi::kModuleVa_x11; -#endif // BUILDFLAG(USE_VAAPI_X11) -using media_gpu_vaapi::InitializeStubs; -using media_gpu_vaapi::IsVaInitialized; -#if BUILDFLAG(USE_VAAPI_X11) -using media_gpu_vaapi::IsVa_x11Initialized; -#endif // BUILDFLAG(USE_VAAPI_X11) -using media_gpu_vaapi::IsVa_drmInitialized; -using media_gpu_vaapi::StubPathMap; - -namespace media { - -// These values are logged to UMA. Entries should not be renumbered and numeric -// values should never be reused. Please keep in sync with -// "VaapiFunctions" in src/tools/metrics/histograms/enums.xml. -enum class VaapiFunctions { - kVABeginPicture = 0, - kVACreateBuffer = 1, - kVACreateConfig = 2, - kVACreateContext = 3, - kVACreateImage = 4, - kVACreateSurfaces_Allocating = 5, - kVACreateSurfaces_Importing = 6, - kVADestroyBuffer = 7, - kVADestroyConfig = 8, - kVADestroyContext = 9, - kVADestroySurfaces = 10, - kVAEndPicture = 11, - kVAExportSurfaceHandle = 12, - kVAGetConfigAttributes = 13, - kVAPutImage = 14, - kVAPutSurface = 15, - kVAQueryConfigAttributes = 16, - kVAQueryImageFormats = 17, - kVAQuerySurfaceAttributes = 18, - kVAQueryVideoProcPipelineCaps = 19, - kVARenderPicture_VABuffers = 20, - kVARenderPicture_Vpp = 21, - kVASyncSurface = 22, - kVATerminate = 23, - kVAUnmapBuffer = 24, - // Protected mode functions below. - kVACreateProtectedSession = 25, - kVADestroyProtectedSession = 26, - kVAAttachProtectedSession = 27, - kVADetachProtectedSession = 28, - kVAProtectedSessionHwUpdate_Deprecated = 29, - kVAProtectedSessionExecute = 30, - // Anything else is captured in this last entry. - kOtherVAFunction = 31, - kMaxValue = kOtherVAFunction, -}; - -void ReportVaapiErrorToUMA(const std::string& histogram_name, - VaapiFunctions value) { - UMA_HISTOGRAM_ENUMERATION(histogram_name, value); -} - -constexpr std::array(VaapiFunctions::kMaxValue) + 1> - kVaapiFunctionNames = {"vaBeginPicture", - "vaCreateBuffer", - "vaCreateConfig", - "vaCreateContext", - "vaCreateImage", - "vaCreateSurfaces (allocate mode)", - "vaCreateSurfaces (import mode)", - "vaDestroyBuffer", - "vaDestroyConfig", - "vaDestroyContext", - "vaDestroySurfaces", - "vaEndPicture", - "vaExportSurfaceHandle", - "vaGetConfigAttributes", - "vaPutImage", - "vaPutSurface", - "vaQueryConfigAttributes", - "vaQueryImageFormats", - "vaQuerySurfaceAttributes", - "vaQueryVideoProcPipelineCaps", - "vaRenderPicture (|pending_va_buffers_|)", - "vaRenderPicture using Vpp", - "vaSyncSurface", - "vaTerminate", - "vaUnmapBuffer", - "vaCreateProtectedSession", - "vaDestroyProtectedSession", - "vaAttachProtectedSession", - "vaDetachProtectedSession", - "vaProtectedSessionHwUpdate (Deprecated)", - "vaProtectedSessionExecute", - "Other VA function"}; - -// Translates |function| into a human readable string for logging. -const char* VaapiFunctionName(VaapiFunctions function) { - DCHECK(function <= VaapiFunctions::kMaxValue); - return kVaapiFunctionNames[static_cast(function)]; -} - -} // namespace media - -#define LOG_VA_ERROR_AND_REPORT(va_error, function) \ - do { \ - LOG(ERROR) << VaapiFunctionName(function) \ - << " failed, VA error: " << vaErrorStr(va_error); \ - report_error_to_uma_cb_.Run(function); \ - } while (0) - -#define VA_LOG_ON_ERROR(va_res, function) \ - do { \ - const VAStatus va_res_va_log_on_error = (va_res); \ - if (va_res_va_log_on_error != VA_STATUS_SUCCESS) \ - LOG_VA_ERROR_AND_REPORT(va_res_va_log_on_error, function); \ - } while (0) - -#define VA_SUCCESS_OR_RETURN(va_res, function, ret) \ - do { \ - const VAStatus va_res_va_sucess_or_return = (va_res); \ - if (va_res_va_sucess_or_return != VA_STATUS_SUCCESS) { \ - LOG_VA_ERROR_AND_REPORT(va_res_va_sucess_or_return, function); \ - return (ret); \ - } \ - DVLOG(3) << VaapiFunctionName(function); \ - } while (0) - -namespace { - -uint32_t BufferFormatToVAFourCC(gfx::BufferFormat fmt) { - switch (fmt) { - case gfx::BufferFormat::BGRX_8888: - return VA_FOURCC_BGRX; - case gfx::BufferFormat::BGRA_8888: - return VA_FOURCC_BGRA; - case gfx::BufferFormat::RGBX_8888: - return VA_FOURCC_RGBX; - case gfx::BufferFormat::RGBA_8888: - return VA_FOURCC_RGBA; - case gfx::BufferFormat::YVU_420: - return VA_FOURCC_YV12; - case gfx::BufferFormat::YUV_420_BIPLANAR: - return VA_FOURCC_NV12; - case gfx::BufferFormat::P010: - return VA_FOURCC_P010; - default: - NOTREACHED() << gfx::BufferFormatToString(fmt); - return 0; - } -} - -media::VAImplementation VendorStringToImplementationType( - const std::string& va_vendor_string) { - if (base::StartsWith(va_vendor_string, "Mesa Gallium driver", - base::CompareCase::SENSITIVE)) { - return media::VAImplementation::kMesaGallium; - } else if (base::StartsWith(va_vendor_string, "Intel i965 driver", - base::CompareCase::SENSITIVE)) { - return media::VAImplementation::kIntelI965; - } else if (base::StartsWith(va_vendor_string, "Intel iHD driver", - base::CompareCase::SENSITIVE)) { - return media::VAImplementation::kIntelIHD; - } else if (base::StartsWith(va_vendor_string, "Splitted-Desktop Systems VDPAU", - base::CompareCase::SENSITIVE)) { - return media::VAImplementation::kNVIDIAVDPAU; - } - return media::VAImplementation::kOther; -} - -bool UseGlobalVaapiLock(media::VAImplementation implementation_type) { - // Only iHD and Mesa Gallium are known to be thread safe at the moment. - // * Mesa Gallium: b/144877595 - // * iHD: crbug.com/1123429. - constexpr auto kNoVaapiLockImplementations = - base::MakeFixedFlatSet( - {media::VAImplementation::kMesaGallium, - media::VAImplementation::kIntelIHD}); - return !kNoVaapiLockImplementations.contains(implementation_type) || - base::FeatureList::IsEnabled(media::kGlobalVaapiLock); -} - -bool FillVADRMPRIMESurfaceDescriptor(const gfx::NativePixmap& pixmap, - VADRMPRIMESurfaceDescriptor& descriptor) { - memset(&descriptor, 0, sizeof(VADRMPRIMESurfaceDescriptor)); - - const gfx::BufferFormat buffer_format = pixmap.GetBufferFormat(); - const uint32_t va_fourcc = BufferFormatToVAFourCC(buffer_format); - DCHECK(va_fourcc); - - const gfx::Size size = pixmap.GetBufferSize(); - const size_t num_planes = pixmap.GetNumberOfPlanes(); - const int drm_fourcc = ui::GetFourCCFormatFromBufferFormat(buffer_format); - if (drm_fourcc == DRM_FORMAT_INVALID) { - LOG(ERROR) << "Failed to get the DRM format from the buffer format"; - return false; - } - if (num_planes > std::size(descriptor.objects)) { - LOG(ERROR) << "Too many planes in the NativePixmap; got " << num_planes - << " but the maximum number is " - << std::size(descriptor.objects); - return false; - } - static_assert(std::size(VADRMPRIMESurfaceDescriptor{}.layers) == - std::size(VADRMPRIMESurfaceDescriptor{}.objects)); - static_assert( - std::size(VADRMPRIMESurfaceDescriptor{}.layers[0].object_index) == - std::size(VADRMPRIMESurfaceDescriptor{}.objects)); - static_assert(std::size(VADRMPRIMESurfaceDescriptor{}.layers[0].offset) == - std::size(VADRMPRIMESurfaceDescriptor{}.objects)); - static_assert(std::size(VADRMPRIMESurfaceDescriptor{}.layers[0].pitch) == - std::size(VADRMPRIMESurfaceDescriptor{}.objects)); - - descriptor.fourcc = va_fourcc; - descriptor.width = base::checked_cast(size.width()); - descriptor.height = base::checked_cast(size.height()); - - // We can pass the planes as separate layers or all in one layer. The choice - // of doing the latter was arbitrary. - descriptor.num_layers = 1u; - descriptor.layers[0].drm_format = base::checked_cast(drm_fourcc); - descriptor.layers[0].num_planes = base::checked_cast(num_planes); - - descriptor.num_objects = base::checked_cast(num_planes); - for (size_t i = 0u; i < num_planes; i++) { - const int dma_buf_fd = pixmap.GetDmaBufFd(i); - if (dma_buf_fd < 0) { - LOG(ERROR) << "Failed to get dmabuf from an Ozone NativePixmap"; - return false; - } - const off_t data_size = lseek(dma_buf_fd, /*offset=*/0, SEEK_END); - if (data_size == static_cast(-1)) { - PLOG(ERROR) << "Failed to get the size of the dma-buf"; - return false; - } - if (lseek(dma_buf_fd, /*offset=*/0, SEEK_SET) == static_cast(-1)) { - PLOG(ERROR) << "Failed to reset the file offset of the dma-buf"; - return false; - } - - descriptor.objects[i].fd = dma_buf_fd; - descriptor.objects[i].size = base::checked_cast(data_size); - descriptor.objects[i].drm_format_modifier = - pixmap.GetBufferFormatModifier(); - - descriptor.layers[0].object_index[i] = base::checked_cast(i); - if (!base::IsValueInRangeForNumericType( - pixmap.GetDmaBufOffset(i))) { - LOG(ERROR) << "The offset for plane " << i << " is out-of-range"; - return false; - } - descriptor.layers[0].offset[i] = - base::checked_cast(pixmap.GetDmaBufOffset(i)); - descriptor.layers[0].pitch[i] = pixmap.GetDmaBufPitch(i); - } - - return true; -} - -struct VASurfaceAttribExternalBuffersAndFD { - VASurfaceAttribExternalBuffers va_attrib_extbuf; - uintptr_t fd; -}; - -bool FillVASurfaceAttribExternalBuffers( - const gfx::NativePixmap& pixmap, - VASurfaceAttribExternalBuffersAndFD& va_attrib_extbuf_and_fd) { - VASurfaceAttribExternalBuffers& va_attrib_extbuf = - va_attrib_extbuf_and_fd.va_attrib_extbuf; - memset(&va_attrib_extbuf_and_fd, 0, - sizeof(VASurfaceAttribExternalBuffersAndFD)); - - const uint32_t va_fourcc = BufferFormatToVAFourCC(pixmap.GetBufferFormat()); - DCHECK(va_fourcc); - - const gfx::Size size = pixmap.GetBufferSize(); - const size_t num_planes = pixmap.GetNumberOfPlanes(); - - va_attrib_extbuf.pixel_format = va_fourcc; - va_attrib_extbuf.width = base::checked_cast(size.width()); - va_attrib_extbuf.height = base::checked_cast(size.height()); - - static_assert(std::size(VASurfaceAttribExternalBuffers{}.pitches) == - std::size(VASurfaceAttribExternalBuffers{}.offsets)); - if (num_planes > std::size(va_attrib_extbuf.pitches)) { - LOG(ERROR) << "Too many planes in the NativePixmap; got " << num_planes - << " but the maximum number is " - << std::size(va_attrib_extbuf.pitches); - return false; - } - for (size_t i = 0; i < num_planes; ++i) { - va_attrib_extbuf.pitches[i] = pixmap.GetDmaBufPitch(i); - va_attrib_extbuf.offsets[i] = - base::checked_cast(pixmap.GetDmaBufOffset(i)); - DVLOG(4) << "plane " << i << ": pitch: " << va_attrib_extbuf.pitches[i] - << " offset: " << va_attrib_extbuf.offsets[i]; - } - va_attrib_extbuf.num_planes = base::checked_cast(num_planes); - - const int dma_buf_fd = pixmap.GetDmaBufFd(0); - if (dma_buf_fd < 0) { - LOG(ERROR) << "Failed to get dmabuf from an Ozone NativePixmap"; - return false; - } - const off_t data_size = lseek(dma_buf_fd, /*offset=*/0, SEEK_END); - if (data_size == static_cast(-1)) { - PLOG(ERROR) << "Failed to get the size of the dma-buf"; - return false; - } - if (lseek(dma_buf_fd, /*offset=*/0, SEEK_SET) == static_cast(-1)) { - PLOG(ERROR) << "Failed to reset the file offset of the dma-buf"; - return false; - } - // If the data size doesn't fit in a uint32_t, we probably have bigger - // problems. - va_attrib_extbuf.data_size = base::checked_cast(data_size); - - // We only have to pass the first file descriptor to a driver. A VA-API driver - // shall create a VASurface from the single fd correctly. - va_attrib_extbuf_and_fd.fd = base::checked_cast(dma_buf_fd); - va_attrib_extbuf.buffers = &va_attrib_extbuf_and_fd.fd; - va_attrib_extbuf.num_buffers = 1u; - - DCHECK_EQ(va_attrib_extbuf.flags, 0u); - DCHECK_EQ(va_attrib_extbuf.private_data, nullptr); - return true; -} - -} // namespace - -namespace media { - -namespace { -// VAEntrypoint is an enumeration starting from 1, but has no "invalid" value. -constexpr VAEntrypoint kVAEntrypointInvalid = static_cast(0); - -// Returns true if the SoC has a Gen8 GPU. CPU model ID's are referenced from -// the following file in the kernel source: arch/x86/include/asm/intel-family.h. -bool IsGen8Gpu() { - constexpr int kPentiumAndLaterFamily = 0x06; - constexpr int kBroadwellCoreModelId = 0x3D; - constexpr int kBroadwellGT3EModelId = 0x47; - constexpr int kBroadwellXModelId = 0x4F; - constexpr int kBroadwellXeonDModelId = 0x56; - constexpr int kBraswellModelId = 0x4C; - static const base::NoDestructor cpuid; - static const bool is_gen8_gpu = cpuid->family() == kPentiumAndLaterFamily && - (cpuid->model() == kBroadwellCoreModelId || - cpuid->model() == kBroadwellGT3EModelId || - cpuid->model() == kBroadwellXModelId || - cpuid->model() == kBroadwellXeonDModelId || - cpuid->model() == kBraswellModelId); - return is_gen8_gpu; -} - -// Returns true if the SoC has a Gen9 GPU. CPU model ID's are referenced from -// the following file in the kernel source: arch/x86/include/asm/intel-family.h. -bool IsGen9Gpu() { - constexpr int kPentiumAndLaterFamily = 0x06; - constexpr int kSkyLakeModelId = 0x5E; - constexpr int kSkyLake_LModelId = 0x4E; - constexpr int kApolloLakeModelId = 0x5c; - static const base::NoDestructor cpuid; - static const bool is_gen9_gpu = cpuid->family() == kPentiumAndLaterFamily && - (cpuid->model() == kSkyLakeModelId || - cpuid->model() == kSkyLake_LModelId || - cpuid->model() == kApolloLakeModelId); - return is_gen9_gpu; -} - -// Returns true if the SoC has a 9.5 GPU. CPU model IDs are referenced from the -// following file in the kernel source: arch/x86/include/asm/intel-family.h. -bool IsGen95Gpu() { - constexpr int kPentiumAndLaterFamily = 0x06; - constexpr int kKabyLakeModelId = 0x9E; - // Amber Lake, Whiskey Lake and some Comet Lake CPU IDs are the same as KBL L. - constexpr int kKabyLake_LModelId = 0x8E; - constexpr int kGeminiLakeModelId = 0x7A; - constexpr int kCometLakeModelId = 0xA5; - constexpr int kCometLake_LModelId = 0xA6; - static const base::NoDestructor cpuid; - static const bool is_gen95_gpu = cpuid->family() == kPentiumAndLaterFamily && - (cpuid->model() == kKabyLakeModelId || - cpuid->model() == kKabyLake_LModelId || - cpuid->model() == kGeminiLakeModelId || - cpuid->model() == kCometLakeModelId || - cpuid->model() == kCometLake_LModelId); - return is_gen95_gpu; -} - -// Returns true if the intel hybrid driver is used for decoding |va_profile|. -// https://github.com/intel/intel-hybrid-driver -// Note that since the hybrid driver runs as a part of the i965 driver, -// vaQueryVendorString() returns "Intel i965 driver". -bool IsUsingHybridDriverForDecoding(VAProfile va_profile) { - // Note that Skylake (not gen8) also needs the hybrid decoder for VP9 - // decoding. However, it is disabled today on ChromeOS - // (see crrev.com/c/390511). - return va_profile == VAProfileVP9Profile0 && IsGen8Gpu(); -} - -// Returns true if the SoC is considered a low power one, i.e. it's an Intel -// Pentium, Celeron, or a Core Y-series. See go/intel-socs-101 or -// https://www.intel.com/content/www/us/en/processors/processor-numbers.html. -bool IsLowPowerIntelProcessor() { - constexpr int kPentiumAndLaterFamily = 0x06; - static const base::NoDestructor cpuid; - static const bool is_core_y_processor = - base::MatchPattern(cpuid->cpu_brand(), "Intel(R) Core(TM) *Y CPU*"); - - static const bool is_low_power_intel = - cpuid->family() == kPentiumAndLaterFamily && - (base::Contains(cpuid->cpu_brand(), "Pentium") || - base::Contains(cpuid->cpu_brand(), "Celeron") || is_core_y_processor); - return is_low_power_intel; -} - -bool IsModeDecoding(VaapiWrapper::CodecMode mode) { - return mode == VaapiWrapper::CodecMode::kDecode -#if BUILDFLAG(IS_CHROMEOS_ASH) - || VaapiWrapper::CodecMode::kDecodeProtected -#endif - ; -} - -bool IsModeEncoding(VaapiWrapper::CodecMode mode) { - return mode == VaapiWrapper::CodecMode::kEncodeConstantBitrate || - mode == - VaapiWrapper::CodecMode::kEncodeConstantQuantizationParameter || - mode == VaapiWrapper::CodecMode::kEncodeVariableBitrate; -} - -bool GetNV12VisibleWidthBytes(int visible_width, - uint32_t plane, - size_t* bytes) { - if (plane == 0) { - *bytes = base::checked_cast(visible_width); - return true; - } - - *bytes = base::checked_cast(visible_width); - return visible_width % 2 == 0 || - base::CheckAdd(visible_width, 1).AssignIfValid(bytes); -} - -// Fill 0 on VAImage's non visible area. -bool ClearNV12Padding(const VAImage& image, - const gfx::Size& visible_size, - uint8_t* data) { - DCHECK_EQ(2u, image.num_planes); - DCHECK_EQ(image.format.fourcc, static_cast(VA_FOURCC_NV12)); - - size_t visible_width_bytes[2] = {}; - if (!GetNV12VisibleWidthBytes(visible_size.width(), 0u, - &visible_width_bytes[0]) || - !GetNV12VisibleWidthBytes(visible_size.width(), 1u, - &visible_width_bytes[1])) { - return false; - } - - for (uint32_t plane = 0; plane < image.num_planes; plane++) { - size_t row_bytes = base::strict_cast(image.pitches[plane]); - if (row_bytes == visible_width_bytes[plane]) - continue; - - CHECK_GT(row_bytes, visible_width_bytes[plane]); - int visible_height = visible_size.height(); - if (plane == 1 && !(base::CheckAdd(visible_size.height(), 1) / 2) - .AssignIfValid(&visible_height)) { - return false; - } - - const size_t padding_bytes = row_bytes - visible_width_bytes[plane]; - uint8_t* plane_data = data + image.offsets[plane]; - for (int row = 0; row < visible_height; row++, plane_data += row_bytes) - memset(plane_data + visible_width_bytes[plane], 0, padding_bytes); - - CHECK_GE(base::strict_cast(image.height), visible_height); - size_t image_height = base::strict_cast(image.height); - if (plane == 1 && !(base::CheckAdd(image.height, 1) / 2) - .AssignIfValid(&image_height)) { - return false; - } - - base::CheckedNumeric remaining_area(image_height); - remaining_area -= base::checked_cast(visible_height); - remaining_area *= row_bytes; - if (!remaining_area.IsValid()) - return false; - memset(plane_data, 0, remaining_area.ValueOrDie()); - } - - return true; -} - -// Can't statically initialize the profile map: -// https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables -using ProfileCodecMap = std::map; -const ProfileCodecMap& GetProfileCodecMap() { - static const base::NoDestructor kMediaToVAProfileMap({ - // VAProfileH264Baseline is deprecated in since libva 2.0.0. - {H264PROFILE_BASELINE, VAProfileH264ConstrainedBaseline}, - {H264PROFILE_MAIN, VAProfileH264Main}, - // TODO(posciak): See if we can/want to support other variants of - // H264PROFILE_HIGH*. - {H264PROFILE_HIGH, VAProfileH264High}, - {VP8PROFILE_ANY, VAProfileVP8Version0_3}, - {VP9PROFILE_PROFILE0, VAProfileVP9Profile0}, - // VaapiWrapper does not support VP9 Profile 1, see b/153680337. - // {VP9PROFILE_PROFILE1, VAProfileVP9Profile1}, - {VP9PROFILE_PROFILE2, VAProfileVP9Profile2}, - // VaapiWrapper does not support Profile 3. - //{VP9PROFILE_PROFILE3, VAProfileVP9Profile3}, - {AV1PROFILE_PROFILE_MAIN, VAProfileAV1Profile0}, - // VaapiWrapper does not support AV1 Profile 1. - // {AV1PROFILE_PROFILE_HIGH, VAProfileAV1Profile1}, -#if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER) - {HEVCPROFILE_MAIN, VAProfileHEVCMain}, - {HEVCPROFILE_MAIN_STILL_PICTURE, VAProfileHEVCMain}, - {HEVCPROFILE_MAIN10, VAProfileHEVCMain10}, -#endif // BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER) - }); - return *kMediaToVAProfileMap; -} - -// Maps a VideoCodecProfile |profile| to a VAProfile, or VAProfileNone. -VAProfile ProfileToVAProfile(VideoCodecProfile profile, - VaapiWrapper::CodecMode mode) { - const auto& profiles = GetProfileCodecMap(); - const auto& maybe_profile = profiles.find(profile); - if (maybe_profile == profiles.end()) - return VAProfileNone; - return maybe_profile->second; -} - -bool IsVAProfileSupported(VAProfile va_profile) { - const auto& profiles = GetProfileCodecMap(); - // VAProfileJPEGBaseline and VAProfileProtected are always recognized but are - // not video codecs per se. - return va_profile == VAProfileJPEGBaseline || -#if BUILDFLAG(IS_CHROMEOS_ASH) - va_profile == VAProfileProtected || -#endif - std::find_if(profiles.begin(), profiles.end(), - [va_profile](const auto& entry) { - return entry.second == va_profile; - }) != profiles.end(); -} - -bool IsBlockedDriver(VaapiWrapper::CodecMode mode, VAProfile va_profile) { - if (!IsModeEncoding(mode)) { - return va_profile == VAProfileAV1Profile0 && - !base::FeatureList::IsEnabled(kVaapiAV1Decoder); - } - - // TODO(posciak): Remove once VP8 encoding is to be enabled by default. - if (va_profile == VAProfileVP8Version0_3 && - !base::FeatureList::IsEnabled(kVaapiVP8Encoder)) { - return true; - } - - // TODO(crbug.com/811912): Remove once VP9 encoding is enabled by default. - if (va_profile == VAProfileVP9Profile0 && - !base::FeatureList::IsEnabled(kVaapiVP9Encoder)) { - return true; - } - - return false; -} - -// This class is a wrapper around its |va_display_| (and its associated -// |va_lock_|) to guarantee mutual exclusion and singleton behaviour. -class VADisplayState { - public: - static VADisplayState* Get(); - - VADisplayState(const VADisplayState&) = delete; - VADisplayState& operator=(const VADisplayState&) = delete; - - // Initialize static data before sandbox is enabled. - static void PreSandboxInitialization(); - - bool Initialize(); - VAStatus Deinitialize(); - - base::Lock* va_lock() { return &va_lock_; } - VADisplay va_display() const { return va_display_; } - VAImplementation implementation_type() const { return implementation_type_; } - - void SetDrmFd(base::PlatformFile fd) { drm_fd_.reset(HANDLE_EINTR(dup(fd))); } - - private: - friend class base::NoDestructor; - - VADisplayState(); - ~VADisplayState() = default; - - // Implementation of Initialize() called only once. - bool InitializeOnce() EXCLUSIVE_LOCKS_REQUIRED(va_lock_); - bool InitializeVaDisplay_Locked() EXCLUSIVE_LOCKS_REQUIRED(va_lock_); - bool InitializeVaDriver_Locked() EXCLUSIVE_LOCKS_REQUIRED(va_lock_); - - int refcount_ GUARDED_BY(va_lock_); - - // Libva may or may not be thread safe depending on the backend. If not thread - // safe, we have to do locking for it ourselves. Therefore, this lock may need - // to be taken for the duration of all VA-API calls and for the entire job - // submission sequence in ExecuteAndDestroyPendingBuffers(). - base::Lock va_lock_; - - // Drm fd used to obtain access to the driver interface by VA. - base::ScopedFD drm_fd_; - - // The VADisplay handle. Valid between Initialize() and Deinitialize(). - VADisplay va_display_; - - // True if vaInitialize() has been called successfully, until Deinitialize(). - bool va_initialized_; - - // Enumerated version of vaQueryVendorString(). Valid after Initialize(). - VAImplementation implementation_type_ = VAImplementation::kInvalid; -}; - -// static -VADisplayState* VADisplayState::Get() { - static base::NoDestructor display_state; - return display_state.get(); -} - -// static -void VADisplayState::PreSandboxInitialization() { - constexpr char kRenderNodeFilePattern[] = "/dev/dri/renderD%d"; - const char kNvidiaPath[] = "/dev/dri/nvidiactl"; - - // TODO: Is this still needed? - base::File nvidia_file = base::File( - base::FilePath::FromUTF8Unsafe(kNvidiaPath), - base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE); - - // This loop ends on either the first card that does not exist or the first - // render node that is not vgem. - for (int i = 128;; i++) { - base::FilePath dev_path(FILE_PATH_LITERAL( - base::StringPrintf(kRenderNodeFilePattern, i).c_str())); - base::File drm_file = - base::File(dev_path, base::File::FLAG_OPEN | base::File::FLAG_READ | - base::File::FLAG_WRITE); - if (!drm_file.IsValid()) - return; - // Skip the virtual graphics memory manager device. - drmVersionPtr version = drmGetVersion(drm_file.GetPlatformFile()); - if (!version) - continue; - std::string version_name( - version->name, - base::checked_cast(version->name_len)); - drmFreeVersion(version); - if (base::EqualsCaseInsensitiveASCII(version_name, "vgem")) - continue; - VADisplayState::Get()->SetDrmFd(drm_file.GetPlatformFile()); - return; - } -} - -VADisplayState::VADisplayState() - : refcount_(0), va_display_(nullptr), va_initialized_(false) {} - -bool VADisplayState::Initialize() { - base::AutoLock auto_lock(va_lock_); - -#if defined(USE_OZONE) && BUILDFLAG(IS_LINUX) - // TODO(crbug.com/1116701): add vaapi support for other Ozone platforms on - // Linux. See comment in OzonePlatform::PlatformProperties::supports_vaapi - // for more details. This will also require revisiting everything that's - // guarded by USE_VAAPI_X11. For example, if USE_VAAPI_X11 is true, but the - // user chooses the Wayland backend for Ozone at runtime, then many things (if - // not all) that we do for X11 won't apply. - if (!ui::OzonePlatform::GetInstance()->GetPlatformProperties().supports_vaapi) - return false; -#endif - - bool libraries_initialized = IsVaInitialized() && IsVa_drmInitialized(); -#if BUILDFLAG(USE_VAAPI_X11) - libraries_initialized = libraries_initialized && IsVa_x11Initialized(); -#endif - if (!libraries_initialized) - return false; - - // Manual refcounting to ensure the rest of the method is called only once. - if (refcount_++ > 0) - return true; - - const bool success = InitializeOnce(); - UMA_HISTOGRAM_BOOLEAN("Media.VaapiWrapper.VADisplayStateInitializeSuccess", - success); - return success; -} - -#if BUILDFLAG(USE_VAAPI_X11) - -absl::optional GetVADisplayStateX11(const base::ScopedFD& drm_fd) { - switch (gl::GetGLImplementation()) { - case gl::kGLImplementationEGLGLES2: - return vaGetDisplayDRM(drm_fd.get()); - - case gl::kGLImplementationNone: - - case gl::kGLImplementationDesktopGL: { - VADisplay display = - vaGetDisplay(x11::Connection::Get()->GetXlibDisplay()); - if (vaDisplayIsValid(display)) - return display; - return vaGetDisplayDRM(drm_fd.get()); - } - - case gl::kGLImplementationEGLANGLE: - return vaGetDisplay(x11::Connection::Get()->GetXlibDisplay()); - - default: - LOG(WARNING) << "VAAPI video acceleration not available for " - << gl::GetGLImplementationGLName( - gl::GetGLImplementationParts()); - return absl::nullopt; - } -} - -#else - -absl::optional GetVADisplayState(const base::ScopedFD& drm_fd) { - switch (gl::GetGLImplementation()) { - case gl::kGLImplementationEGLGLES2: - case gl::kGLImplementationEGLANGLE: - case gl::kGLImplementationNone: - return vaGetDisplayDRM(drm_fd.get()); - default: - LOG(WARNING) << "VAAPI video acceleration not available for " - << gl::GetGLImplementationGLName( - gl::GetGLImplementationParts()); - return absl::nullopt; - } -} - -#endif // BUILDFLAG(USE_VAAPI_X11) - -bool VADisplayState::InitializeVaDisplay_Locked() { - absl::optional display = -#if BUILDFLAG(USE_VAAPI_X11) - GetVADisplayStateX11(drm_fd_); -#else - GetVADisplayState(drm_fd_); -#endif - - if (!display) - return false; - - va_display_ = *display; - if (!vaDisplayIsValid(va_display_)) { - LOG(ERROR) << "Could not get a valid VA display"; - return false; - } - - return true; -} - -bool VADisplayState::InitializeVaDriver_Locked() { - // The VAAPI version. - int major_version, minor_version; - VAStatus va_res = vaInitialize(va_display_, &major_version, &minor_version); - if (va_res != VA_STATUS_SUCCESS) { - VLOGF(1) << "vaInitialize failed: " << vaErrorStr(va_res); - return false; - } - const std::string va_vendor_string = vaQueryVendorString(va_display_); - DLOG_IF(WARNING, va_vendor_string.empty()) - << "Vendor string empty or error reading."; - DVLOG(1) << "VAAPI version: " << major_version << "." << minor_version << " " - << va_vendor_string; - implementation_type_ = VendorStringToImplementationType(va_vendor_string); - - va_initialized_ = true; - - // The VAAPI version is determined from what is loaded on the system by - // calling vaInitialize(). Since the libva is now ABI-compatible, relax the - // version check which helps in upgrading the libva, without breaking any - // existing functionality. Make sure the system version is not older than - // the version with which the chromium is built since libva is only - // guaranteed to be backward (and not forward) compatible. - if (VA_MAJOR_VERSION > major_version || - (VA_MAJOR_VERSION == major_version && VA_MINOR_VERSION > minor_version)) { - VLOGF(1) << "The system version " << major_version << "." << minor_version - << " should be greater than or equal to " << VA_MAJOR_VERSION - << "." << VA_MINOR_VERSION; - return false; - } - return true; -} - -bool VADisplayState::InitializeOnce() { - // Set VA logging level, unless already set. - constexpr char libva_log_level_env[] = "LIBVA_MESSAGING_LEVEL"; - std::unique_ptr env(base::Environment::Create()); - if (!env->HasVar(libva_log_level_env)) - env->SetVar(libva_log_level_env, "1"); - - if (!InitializeVaDisplay_Locked() || !InitializeVaDriver_Locked()) - return false; - return true; -} - -VAStatus VADisplayState::Deinitialize() { - base::AutoLock auto_lock(va_lock_); - VAStatus va_res = VA_STATUS_SUCCESS; - - if (--refcount_ > 0) - return va_res; - - // Must check if vaInitialize completed successfully, to work around a bug in - // libva. The bug was fixed upstream: - // http://lists.freedesktop.org/archives/libva/2013-July/001807.html - // TODO(mgiuca): Remove this check, and the |va_initialized_| variable, once - // the fix has rolled out sufficiently. - if (va_initialized_ && va_display_) - va_res = vaTerminate(va_display_); - va_initialized_ = false; - va_display_ = nullptr; - return va_res; -} - -// Returns all the VAProfiles that the driver lists as supported, regardless of -// what Chrome supports or not. -std::vector GetSupportedVAProfiles(const base::Lock* va_lock, - VADisplay va_display) { - MAYBE_ASSERT_ACQUIRED(va_lock); - - // Query the driver for supported profiles. - const int max_va_profiles = vaMaxNumProfiles(va_display); - std::vector va_profiles( - base::checked_cast(max_va_profiles)); - - int num_va_profiles; - const VAStatus va_res = - vaQueryConfigProfiles(va_display, &va_profiles[0], &num_va_profiles); - if (va_res != VA_STATUS_SUCCESS) { - LOG(ERROR) << "vaQueryConfigProfiles failed: " << vaErrorStr(va_res); - return {}; - } - if (num_va_profiles < 0 || num_va_profiles > max_va_profiles) { - LOG(ERROR) << "vaQueryConfigProfiles returned: " << num_va_profiles - << " profiles"; - return {}; - } - - va_profiles.resize(base::checked_cast(num_va_profiles)); - return va_profiles; -} - -// Queries the driver for the supported entrypoints for |va_profile|, then -// returns those allowed for |mode|. -std::vector GetEntryPointsForProfile(const base::Lock* va_lock, - VADisplay va_display, - VaapiWrapper::CodecMode mode, - VAProfile va_profile) { - MAYBE_ASSERT_ACQUIRED(va_lock); - - const int max_entrypoints = vaMaxNumEntrypoints(va_display); - std::vector va_entrypoints( - base::checked_cast(max_entrypoints)); - - int num_va_entrypoints; - const VAStatus va_res = vaQueryConfigEntrypoints( - va_display, va_profile, &va_entrypoints[0], &num_va_entrypoints); - if (va_res != VA_STATUS_SUCCESS) { - LOG(ERROR) << "vaQueryConfigEntrypoints failed, VA error: " - << vaErrorStr(va_res); - return {}; - } - if (num_va_entrypoints < 0 || num_va_entrypoints > max_entrypoints) { - LOG(ERROR) << "vaQueryConfigEntrypoints returned: " << num_va_entrypoints - << " entry points, when the max is: " << max_entrypoints; - return {}; - } - va_entrypoints.resize(num_va_entrypoints); - - const std::vector kAllowedEntryPoints[] = { - {VAEntrypointVLD}, // kDecode. -#if BUILDFLAG(IS_CHROMEOS_ASH) - {VAEntrypointVLD, VAEntrypointProtectedContent}, // kDecodeProtected. -#endif - {VAEntrypointEncSlice, VAEntrypointEncPicture, - VAEntrypointEncSliceLP}, // kEncodeConstantBitrate. - {VAEntrypointEncSlice, - VAEntrypointEncSliceLP}, // kEncodeConstantQuantizationParameter. - {VAEntrypointEncSlice, VAEntrypointEncSliceLP}, // kEncodeVariableBitrate. - {VAEntrypointVideoProc} // kVideoProcess. - }; - static_assert(std::size(kAllowedEntryPoints) == VaapiWrapper::kCodecModeMax, - ""); - - std::vector entrypoints; - std::copy_if(va_entrypoints.begin(), va_entrypoints.end(), - std::back_inserter(entrypoints), - [&kAllowedEntryPoints, mode](VAEntrypoint entry_point) { - return base::Contains(kAllowedEntryPoints[mode], entry_point); - }); - return entrypoints; -} - -bool GetRequiredAttribs(const base::Lock* va_lock, - VADisplay va_display, - VaapiWrapper::CodecMode mode, - VAProfile profile, - VAEntrypoint entrypoint, - std::vector* required_attribs) { - MAYBE_ASSERT_ACQUIRED(va_lock); - - // Choose a suitable VAConfigAttribRTFormat for every |mode|. For video - // processing, the supported surface attribs may vary according to which RT - // format is set. - if (profile == VAProfileVP9Profile2 || profile == VAProfileVP9Profile3) { - required_attribs->push_back( - {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420_10BPP}); -#if BUILDFLAG(IS_CHROMEOS_ASH) - } else if (profile == VAProfileProtected) { - DCHECK_EQ(mode, VaapiWrapper::kDecodeProtected); - constexpr int kWidevineUsage = 0x1; - required_attribs->push_back( - {VAConfigAttribProtectedContentUsage, kWidevineUsage}); - required_attribs->push_back( - {VAConfigAttribProtectedContentCipherAlgorithm, VA_PC_CIPHER_AES}); - required_attribs->push_back( - {VAConfigAttribProtectedContentCipherBlockSize, VA_PC_BLOCK_SIZE_128}); - required_attribs->push_back( - {VAConfigAttribProtectedContentCipherMode, VA_PC_CIPHER_MODE_CTR}); -#endif - } else { - required_attribs->push_back({VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420}); - } - -#if BUILDFLAG(IS_CHROMEOS_ASH) - if (mode == VaapiWrapper::kDecodeProtected && profile != VAProfileProtected) { - required_attribs->push_back( - {VAConfigAttribEncryption, VA_ENCRYPTION_TYPE_SUBSAMPLE_CTR}); - } -#endif - - if (!IsModeEncoding(mode)) - return true; - - if (profile == VAProfileJPEGBaseline) - return true; - - if (mode == VaapiWrapper::kEncodeConstantBitrate) - required_attribs->push_back({VAConfigAttribRateControl, VA_RC_CBR}); - if (mode == VaapiWrapper::kEncodeConstantQuantizationParameter) - required_attribs->push_back({VAConfigAttribRateControl, VA_RC_CQP}); - if (mode == VaapiWrapper::kEncodeVariableBitrate) - required_attribs->push_back({VAConfigAttribRateControl, VA_RC_VBR}); - - constexpr VAProfile kSupportedH264VaProfilesForEncoding[] = { - VAProfileH264ConstrainedBaseline, VAProfileH264Main, VAProfileH264High}; - // VAConfigAttribEncPackedHeaders is H.264 specific. - if (base::Contains(kSupportedH264VaProfilesForEncoding, profile)) { - // Encode with Packed header if the driver supports. - VAConfigAttrib attrib{}; - attrib.type = VAConfigAttribEncPackedHeaders; - const VAStatus va_res = - vaGetConfigAttributes(va_display, profile, entrypoint, &attrib, 1); - if (va_res != VA_STATUS_SUCCESS) { - LOG(ERROR) << "vaGetConfigAttributes failed: " << vaProfileStr(profile); - return false; - } - - const uint32_t packed_header_attributes = - (VA_ENC_PACKED_HEADER_SEQUENCE | VA_ENC_PACKED_HEADER_PICTURE | - VA_ENC_PACKED_HEADER_SLICE); - if ((packed_header_attributes & attrib.value) == packed_header_attributes) { - required_attribs->push_back( - {VAConfigAttribEncPackedHeaders, packed_header_attributes}); - } else { - required_attribs->push_back( - {VAConfigAttribEncPackedHeaders, VA_ENC_PACKED_HEADER_NONE}); - } - } - return true; -} - -// Returns true if |va_profile| for |entrypoint| with |required_attribs| is -// supported. -bool AreAttribsSupported(const base::Lock* va_lock, - VADisplay va_display, - VAProfile va_profile, - VAEntrypoint entrypoint, - const std::vector& required_attribs) { - MAYBE_ASSERT_ACQUIRED(va_lock); - // Query the driver for required attributes. - std::vector attribs = required_attribs; - for (size_t i = 0; i < required_attribs.size(); ++i) - attribs[i].value = 0; - - VAStatus va_res = vaGetConfigAttributes(va_display, va_profile, entrypoint, - &attribs[0], attribs.size()); - if (va_res != VA_STATUS_SUCCESS) { - LOG(ERROR) << "vaGetConfigAttributes failed error: " << vaErrorStr(va_res); - return false; - } - for (size_t i = 0; i < required_attribs.size(); ++i) { - if (attribs[i].type != required_attribs[i].type || - (attribs[i].value & required_attribs[i].value) != - required_attribs[i].value) { - VLOG(1) << "Unsupported value " << required_attribs[i].value << " for " - << vaConfigAttribTypeStr(required_attribs[i].type); - return false; - } - } - return true; -} - -// This class encapsulates reading and giving access to the list of supported -// ProfileInfo entries, in a singleton way. -class VASupportedProfiles { - public: - struct ProfileInfo { - VAProfile va_profile; - VAEntrypoint va_entrypoint; - gfx::Size min_resolution; - gfx::Size max_resolution; - std::vector pixel_formats; - VaapiWrapper::InternalFormats supported_internal_formats; - }; - static const VASupportedProfiles& Get(); - - VASupportedProfiles(const VASupportedProfiles&) = delete; - VASupportedProfiles& operator=(const VASupportedProfiles&) = delete; - - // Determines if |mode| supports |va_profile| (and |va_entrypoint| if defined - // and valid). If so, returns a const pointer to its ProfileInfo, otherwise - // returns nullptr. - // TODO(hiroh): If VAEntrypoint is kVAEntrypointInvalid, the default entry - // point acquired by GetDefaultVaEntryPoint() is used. If the default entry - // point is not supported, the earlier supported entrypoint in - // |kAllowedEntryPopints| is used. - const ProfileInfo* IsProfileSupported( - VaapiWrapper::CodecMode mode, - VAProfile va_profile, - VAEntrypoint va_entrypoint = kVAEntrypointInvalid) const; - - private: - friend class base::NoDestructor; - - friend std::map> - VaapiWrapper::GetSupportedConfigurationsForCodecModeForTesting( - CodecMode mode); - - VASupportedProfiles(); - ~VASupportedProfiles() = default; - - // Fills in |supported_profiles_|. - void FillSupportedProfileInfos(base::Lock* va_lock, VADisplay va_display); - - // Fills |profile_info| for |va_profile| and |entrypoint| with - // |required_attribs|. If the return value is true, the operation was - // successful. Otherwise, the information in *|profile_info| shouldn't be - // relied upon. - bool FillProfileInfo_Locked(const base::Lock* va_lock, - VADisplay va_display, - VAProfile va_profile, - VAEntrypoint entrypoint, - std::vector& required_attribs, - ProfileInfo* profile_info) const; - - std::vector supported_profiles_[VaapiWrapper::kCodecModeMax]; - static_assert(std::extent() == - VaapiWrapper::kCodecModeMax, - "|supported_profiles_| size is incorrect."); - - const ReportErrorToUMACB report_error_to_uma_cb_; -}; - -// static -const VASupportedProfiles& VASupportedProfiles::Get() { - static const base::NoDestructor profile_infos; - return *profile_infos; -} - -const VASupportedProfiles::ProfileInfo* VASupportedProfiles::IsProfileSupported( - VaapiWrapper::CodecMode mode, - VAProfile va_profile, - VAEntrypoint va_entrypoint) const { - auto iter = std::find_if( - supported_profiles_[mode].begin(), supported_profiles_[mode].end(), - [va_profile, va_entrypoint](const ProfileInfo& profile) { - return profile.va_profile == va_profile && - (va_entrypoint == kVAEntrypointInvalid || - profile.va_entrypoint == va_entrypoint); - }); - if (iter != supported_profiles_[mode].end()) - return &*iter; - return nullptr; -} - -VASupportedProfiles::VASupportedProfiles() - : report_error_to_uma_cb_(base::DoNothing()) { - VADisplayState* display_state = VADisplayState::Get(); - if (!display_state->Initialize()) - return; - - VADisplay va_display = display_state->va_display(); - DCHECK(va_display) << "VADisplayState hasn't been properly Initialize()d"; - - base::Lock* va_lock = display_state->va_lock(); - if (!UseGlobalVaapiLock(display_state->implementation_type())) { - va_lock = nullptr; - } - - FillSupportedProfileInfos(va_lock, va_display); - - const VAStatus va_res = display_state->Deinitialize(); - VA_LOG_ON_ERROR(va_res, VaapiFunctions::kVATerminate); -} - -void VASupportedProfiles::FillSupportedProfileInfos(base::Lock* va_lock, - VADisplay va_display) { - base::AutoLockMaybe auto_lock(va_lock); - - const std::vector va_profiles = - GetSupportedVAProfiles(va_lock, va_display); - - constexpr VaapiWrapper::CodecMode kWrapperModes[] = { - VaapiWrapper::kDecode, -#if BUILDFLAG(IS_CHROMEOS_ASH) - VaapiWrapper::kDecodeProtected, -#endif - VaapiWrapper::kEncodeConstantBitrate, - VaapiWrapper::kEncodeConstantQuantizationParameter, - VaapiWrapper::kEncodeVariableBitrate, - VaapiWrapper::kVideoProcess - }; - static_assert(std::size(kWrapperModes) == VaapiWrapper::kCodecModeMax, ""); - - for (VaapiWrapper::CodecMode mode : kWrapperModes) { - std::vector supported_profile_infos; - - for (const auto& va_profile : va_profiles) { - if (IsBlockedDriver(mode, va_profile)) - continue; - - if ((mode != VaapiWrapper::kVideoProcess) && - !IsVAProfileSupported(va_profile)) { - continue; - } - - const std::vector supported_entrypoints = - GetEntryPointsForProfile(va_lock, va_display, mode, va_profile); - - for (const auto& entrypoint : supported_entrypoints) { - std::vector required_attribs; - if (!GetRequiredAttribs(va_lock, va_display, mode, va_profile, - entrypoint, &required_attribs)) { - continue; - } - if (!AreAttribsSupported(va_lock, va_display, va_profile, entrypoint, - required_attribs)) { - continue; - } - ProfileInfo profile_info{}; - if (!FillProfileInfo_Locked(va_lock, va_display, va_profile, entrypoint, - required_attribs, &profile_info)) { - LOG(ERROR) << "FillProfileInfo_Locked failed for va_profile " - << vaProfileStr(va_profile) << " and entrypoint " - << vaEntrypointStr(entrypoint); - continue; - } - - supported_profile_infos.push_back(profile_info); - } - } - supported_profiles_[static_cast(mode)] = supported_profile_infos; - } -} - -bool VASupportedProfiles::FillProfileInfo_Locked( - const base::Lock* va_lock, - VADisplay va_display, - VAProfile va_profile, - VAEntrypoint entrypoint, - std::vector& required_attribs, - ProfileInfo* profile_info) const { - MAYBE_ASSERT_ACQUIRED(va_lock); - VAConfigID va_config_id; - VAStatus va_res = - vaCreateConfig(va_display, va_profile, entrypoint, &required_attribs[0], - required_attribs.size(), &va_config_id); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVACreateConfig, false); - base::ScopedClosureRunner vaconfig_destroyer(base::BindOnce( - [](VADisplay display, VAConfigID id) { - if (id != VA_INVALID_ID) { - VAStatus va_res = vaDestroyConfig(display, id); - if (va_res != VA_STATUS_SUCCESS) - LOG(ERROR) << "vaDestroyConfig failed. VA error: " - << vaErrorStr(va_res); - } - }, - va_display, va_config_id)); - -#if BUILDFLAG(IS_CHROMEOS_ASH) - // Nothing further to query for protected profile. - if (va_profile == VAProfileProtected) { - profile_info->va_profile = va_profile; - profile_info->va_entrypoint = entrypoint; - return true; - } -#endif - - // Calls vaQuerySurfaceAttributes twice. The first time is to get the number - // of attributes to prepare the space and the second time is to get all - // attributes. - unsigned int num_attribs; - va_res = - vaQuerySurfaceAttributes(va_display, va_config_id, nullptr, &num_attribs); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVAQuerySurfaceAttributes, - false); - if (!num_attribs) - return false; - - std::vector attrib_list( - base::checked_cast(num_attribs)); - - va_res = vaQuerySurfaceAttributes(va_display, va_config_id, &attrib_list[0], - &num_attribs); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVAQuerySurfaceAttributes, - false); - - profile_info->va_profile = va_profile; - profile_info->va_entrypoint = entrypoint; - profile_info->min_resolution = gfx::Size(); - profile_info->max_resolution = gfx::Size(); - for (const auto& attrib : attrib_list) { - if (attrib.type == VASurfaceAttribMaxWidth) { - profile_info->max_resolution.set_width( - base::strict_cast(attrib.value.value.i)); - } else if (attrib.type == VASurfaceAttribMaxHeight) { - profile_info->max_resolution.set_height( - base::strict_cast(attrib.value.value.i)); - } else if (attrib.type == VASurfaceAttribMinWidth) { - profile_info->min_resolution.set_width( - base::strict_cast(attrib.value.value.i)); - } else if (attrib.type == VASurfaceAttribMinHeight) { - profile_info->min_resolution.set_height( - base::strict_cast(attrib.value.value.i)); - } else if (attrib.type == VASurfaceAttribPixelFormat) { - // According to va.h, VASurfaceAttribPixelFormat is meaningful as input to - // vaQuerySurfaceAttributes(). However, per the implementation of - // i965_QuerySurfaceAttributes(), our usage here should enumerate all the - // formats. - profile_info->pixel_formats.push_back(attrib.value.value.i); - } - } - if (profile_info->max_resolution.IsEmpty()) { - LOG(ERROR) << "Empty codec maximum resolution"; - return false; - } - - if (va_profile != VAProfileJPEGBaseline) { - // Set a reasonable minimum value for both encoding and decoding. - profile_info->min_resolution.SetToMax(gfx::Size(16, 16)); - - const bool is_encoding = entrypoint == VAEntrypointEncSliceLP || - entrypoint == VAEntrypointEncSlice; - const bool is_hybrid_decoding = entrypoint == VAEntrypointVLD && - IsUsingHybridDriverForDecoding(va_profile); - - // Using HW encoding for small resolutions is less efficient than using a SW - // encoder. Similarly, using the intel-hybrid-driver for decoding is less - // efficient than using a SW decoder. In both cases, increase - // |min_resolution| to QVGA + 1 which is an experimental lower threshold. - // This can be turned off with kVaapiVideoMinResolutionForPerformance for - // testing. - if ((is_encoding || is_hybrid_decoding) && - base::FeatureList::IsEnabled(kVaapiVideoMinResolutionForPerformance)) { - constexpr gfx::Size kMinVideoResolution(320 + 1, 240 + 1); - profile_info->min_resolution.SetToMax(kMinVideoResolution); - DVLOG(2) << "Setting the minimum supported resolution for " - << vaProfileStr(va_profile) - << (is_encoding ? " encoding" : " decoding") << " to " - << profile_info->min_resolution.ToString(); - } - } - - // Create a new configuration to find the supported RT formats. We don't pass - // required attributes here because we want the driver to tell us all the - // supported RT formats. - va_res = vaCreateConfig(va_display, va_profile, entrypoint, nullptr, 0, - &va_config_id); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVACreateConfig, false); - base::ScopedClosureRunner vaconfig_no_attribs_destroyer(base::BindOnce( - [](VADisplay display, VAConfigID id) { - if (id != VA_INVALID_ID) { - VAStatus va_res = vaDestroyConfig(display, id); - if (va_res != VA_STATUS_SUCCESS) - LOG(ERROR) << "vaDestroyConfig failed. VA error: " - << vaErrorStr(va_res); - } - }, - va_display, va_config_id)); - profile_info->supported_internal_formats = {}; - size_t max_num_config_attributes; - if (!base::CheckedNumeric(vaMaxNumConfigAttributes(va_display)) - .AssignIfValid(&max_num_config_attributes)) { - LOG(ERROR) << "Can't get the maximum number of config attributes"; - return false; - } - std::vector config_attributes(max_num_config_attributes); - int num_config_attributes; - va_res = vaQueryConfigAttributes(va_display, va_config_id, &va_profile, - &entrypoint, config_attributes.data(), - &num_config_attributes); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVAQueryConfigAttributes, false); - for (int i = 0; i < num_config_attributes; i++) { - const VAConfigAttrib& attrib = config_attributes[i]; - if (attrib.type != VAConfigAttribRTFormat) - continue; - if (attrib.value & VA_RT_FORMAT_YUV420) - profile_info->supported_internal_formats.yuv420 = true; - if (attrib.value & VA_RT_FORMAT_YUV420_10) - profile_info->supported_internal_formats.yuv420_10 = true; - if (attrib.value & VA_RT_FORMAT_YUV422) - profile_info->supported_internal_formats.yuv422 = true; - if (attrib.value & VA_RT_FORMAT_YUV444) - profile_info->supported_internal_formats.yuv444 = true; - break; - } - - // Now work around some driver misreporting for JPEG decoding. - if (va_profile == VAProfileJPEGBaseline && entrypoint == VAEntrypointVLD) { - if (VADisplayState::Get()->implementation_type() == - VAImplementation::kMesaGallium) { - // TODO(andrescj): the VAAPI state tracker in mesa does not report - // VA_RT_FORMAT_YUV422 as being supported for JPEG decoding. However, it - // is happy to allocate YUYV surfaces - // (https://gitlab.freedesktop.org/mesa/mesa/commit/5608f442). Remove this - // workaround once b/128337341 is resolved. - profile_info->supported_internal_formats.yuv422 = true; - } - } - const bool is_any_profile_supported = - profile_info->supported_internal_formats.yuv420 || - profile_info->supported_internal_formats.yuv420_10 || - profile_info->supported_internal_formats.yuv422 || - profile_info->supported_internal_formats.yuv444; - DLOG_IF(ERROR, !is_any_profile_supported) - << "No cool internal formats supported"; - return is_any_profile_supported; -} - -void DestroyVAImage(VADisplay va_display, VAImage image) { - if (image.image_id != VA_INVALID_ID) - vaDestroyImage(va_display, image.image_id); -} - -// This class encapsulates fetching the list of supported output image formats -// from the VAAPI driver, in a singleton way. -class VASupportedImageFormats { - public: - static const VASupportedImageFormats& Get(); - - VASupportedImageFormats(const VASupportedImageFormats&) = delete; - VASupportedImageFormats& operator=(const VASupportedImageFormats&) = delete; - - bool IsImageFormatSupported(const VAImageFormat& va_format) const; - - const std::vector& GetSupportedImageFormats() const; - - private: - friend class base::NoDestructor; - - VASupportedImageFormats(); - ~VASupportedImageFormats() = default; - - // Initialize the list of supported image formats. - bool InitSupportedImageFormats_Locked(const base::Lock* va_lock, - VADisplay va_display); - - std::vector supported_formats_; - const ReportErrorToUMACB report_error_to_uma_cb_; -}; - -// static -const VASupportedImageFormats& VASupportedImageFormats::Get() { - static const base::NoDestructor image_formats; - return *image_formats; -} - -bool VASupportedImageFormats::IsImageFormatSupported( - const VAImageFormat& va_image_format) const { - auto it = std::find_if(supported_formats_.begin(), supported_formats_.end(), - [&va_image_format](const VAImageFormat& format) { - return format.fourcc == va_image_format.fourcc; - }); - return it != supported_formats_.end(); -} - -const std::vector& -VASupportedImageFormats::GetSupportedImageFormats() const { -#if DCHECK_IS_ON() - std::string formats_str; - for (size_t i = 0; i < supported_formats_.size(); i++) { - if (i > 0) - formats_str += ", "; - formats_str += FourccToString(supported_formats_[i].fourcc); - } - DVLOG(1) << "Supported image formats: " << formats_str; -#endif - return supported_formats_; -} - -VASupportedImageFormats::VASupportedImageFormats() - : report_error_to_uma_cb_(base::DoNothing()) { - VADisplayState* display_state = VADisplayState::Get(); - if (!display_state->Initialize()) - return; - - // Pointer to VADisplayState's members |va_lock_| if using global VA lock or - // the implementation is not thread safe. - base::Lock* va_lock = display_state->va_lock(); - if (!UseGlobalVaapiLock(display_state->implementation_type())) { - va_lock = nullptr; - } - - { - base::AutoLockMaybe auto_lock(va_lock); - VADisplay va_display = display_state->va_display(); - DCHECK(va_display) << "VADisplayState hasn't been properly initialized"; - - if (!InitSupportedImageFormats_Locked(va_lock, va_display)) - LOG(ERROR) << "Failed to get supported image formats"; - } - - const VAStatus va_res = display_state->Deinitialize(); - VA_LOG_ON_ERROR(va_res, VaapiFunctions::kVATerminate); -} - -bool VASupportedImageFormats::InitSupportedImageFormats_Locked( - const base::Lock* va_lock, - VADisplay va_display) { - MAYBE_ASSERT_ACQUIRED(va_lock); - - // Query the driver for the max number of image formats and allocate space. - const int max_image_formats = vaMaxNumImageFormats(va_display); - if (max_image_formats < 0) { - LOG(ERROR) << "vaMaxNumImageFormats returned: " << max_image_formats; - return false; - } - supported_formats_.resize(static_cast(max_image_formats)); - - // Query the driver for the list of supported image formats. - int num_image_formats; - const VAStatus va_res = vaQueryImageFormats( - va_display, supported_formats_.data(), &num_image_formats); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVAQueryImageFormats, false); - if (num_image_formats < 0 || num_image_formats > max_image_formats) { - LOG(ERROR) << "vaQueryImageFormats returned: " << num_image_formats; - supported_formats_.clear(); - return false; - } - - // Resize the list to the actual number of formats returned by the driver. - supported_formats_.resize(static_cast(num_image_formats)); - - // Now work around some driver misreporting. - if (VADisplayState::Get()->implementation_type() == - VAImplementation::kMesaGallium) { - // TODO(andrescj): considering that the VAAPI state tracker in mesa can - // convert from NV12 to IYUV when doing vaGetImage(), it's reasonable to - // assume that IYUV/I420 is supported. However, it's not currently being - // reported. See https://gitlab.freedesktop.org/mesa/mesa/commit/b0a44f10. - // Remove this workaround once b/128340287 is resolved. - if (std::find_if(supported_formats_.cbegin(), supported_formats_.cend(), - [](const VAImageFormat& format) { - return format.fourcc == VA_FOURCC_I420; - }) == supported_formats_.cend()) { - VAImageFormat i420_format{}; - i420_format.fourcc = VA_FOURCC_I420; - supported_formats_.push_back(i420_format); - } - } - return true; -} - -bool IsLowPowerEncSupported(VAProfile va_profile) { - constexpr VAProfile kSupportedLowPowerEncodeProfiles[] = { - VAProfileH264ConstrainedBaseline, - VAProfileH264Main, - VAProfileH264High, - VAProfileVP9Profile0, - VAProfileVP9Profile2}; - if (!base::Contains(kSupportedLowPowerEncodeProfiles, va_profile)) - return false; - - if ((IsGen95Gpu() || IsGen9Gpu()) && - !base::FeatureList::IsEnabled(kVaapiLowPowerEncoderGen9x)) { - return false; - } - - if (VASupportedProfiles::Get().IsProfileSupported( - VaapiWrapper::kEncodeConstantBitrate, va_profile, - VAEntrypointEncSliceLP)) { - return true; - } - return false; -} - -bool IsVBREncodingSupported(VAProfile va_profile) { - if (!base::FeatureList::IsEnabled(kChromeOSHWVBREncoding)) - return false; - - auto mode = VaapiWrapper::CodecMode::kCodecModeMax; - switch (va_profile) { - case VAProfileH264ConstrainedBaseline: - case VAProfileH264Main: - case VAProfileH264High: - mode = VaapiWrapper::CodecMode::kEncodeVariableBitrate; - break; - default: - return false; - } - - return VASupportedProfiles::Get().IsProfileSupported(mode, va_profile); -} - -} // namespace - -NativePixmapAndSizeInfo::NativePixmapAndSizeInfo() = default; - -NativePixmapAndSizeInfo::~NativePixmapAndSizeInfo() = default; - -// static -VAImplementation VaapiWrapper::GetImplementationType() { - return VADisplayState::Get()->implementation_type(); -} - -// static -scoped_refptr VaapiWrapper::Create( - CodecMode mode, - VAProfile va_profile, - EncryptionScheme encryption_scheme, - const ReportErrorToUMACB& report_error_to_uma_cb, - bool enforce_sequence_affinity) { - if (!VASupportedProfiles::Get().IsProfileSupported(mode, va_profile)) { - DVLOG(1) << "Unsupported va_profile: " << vaProfileStr(va_profile); - return nullptr; - } -#if BUILDFLAG(IS_CHROMEOS_ASH) - // In protected decode |mode| we need to ensure that |va_profile| is supported - // (which we verified above) and that VAProfileProtected is supported, which - // we check here. - if (mode == kDecodeProtected && - !VASupportedProfiles::Get().IsProfileSupported(mode, - VAProfileProtected)) { - LOG(ERROR) << "Protected content profile not supported"; - return nullptr; - } -#endif - - scoped_refptr vaapi_wrapper( - new VaapiWrapper(mode, enforce_sequence_affinity)); - if (vaapi_wrapper->VaInitialize(report_error_to_uma_cb)) { - if (vaapi_wrapper->Initialize(va_profile, encryption_scheme)) - return vaapi_wrapper; - } - LOG(ERROR) << "Failed to create VaapiWrapper for va_profile: " - << vaProfileStr(va_profile); - return nullptr; -} - -// static -scoped_refptr VaapiWrapper::CreateForVideoCodec( - CodecMode mode, - VideoCodecProfile profile, - EncryptionScheme encryption_scheme, - const ReportErrorToUMACB& report_error_to_uma_cb, - bool enforce_sequence_affinity) { - const VAProfile va_profile = ProfileToVAProfile(profile, mode); - return Create(mode, va_profile, encryption_scheme, report_error_to_uma_cb, - enforce_sequence_affinity); -} - -// static -std::vector VaapiWrapper::GetSupportedScalabilityModes( - VideoCodecProfile media_profile, - VAProfile va_profile) { - std::vector scalability_modes; -#if BUILDFLAG(IS_CHROMEOS) - if (media_profile == VP9PROFILE_PROFILE0) { - scalability_modes.push_back(SVCScalabilityMode::kL1T2); - scalability_modes.push_back(SVCScalabilityMode::kL1T3); - if (base::FeatureList::IsEnabled(kVaapiVp9kSVCHWEncoding) && - GetDefaultVaEntryPoint( - VaapiWrapper::kEncodeConstantQuantizationParameter, va_profile) == - VAEntrypointEncSliceLP) { - scalability_modes.push_back(SVCScalabilityMode::kL2T2Key); - scalability_modes.push_back(SVCScalabilityMode::kL2T3Key); - scalability_modes.push_back(SVCScalabilityMode::kL3T2Key); - scalability_modes.push_back(SVCScalabilityMode::kL3T3Key); - } - } - - if (media_profile >= VP8PROFILE_MIN && media_profile <= VP8PROFILE_MAX) { - if (base::FeatureList::IsEnabled(kVaapiVp8TemporalLayerHWEncoding)) { - scalability_modes.push_back(SVCScalabilityMode::kL1T2); - scalability_modes.push_back(SVCScalabilityMode::kL1T3); - } - } - - if (media_profile >= H264PROFILE_MIN && media_profile <= H264PROFILE_MAX) { - if (base::FeatureList::IsEnabled(kVaapiH264TemporalLayerHWEncoding)) { - scalability_modes.push_back(SVCScalabilityMode::kL1T2); - scalability_modes.push_back(SVCScalabilityMode::kL1T3); - } - } -#endif - return scalability_modes; -} - -// static -VideoEncodeAccelerator::SupportedProfiles -VaapiWrapper::GetSupportedEncodeProfiles() { - VideoEncodeAccelerator::SupportedProfiles profiles; - - for (const auto& [media_profile, va_profile] : GetProfileCodecMap()) { - DCHECK(va_profile != VAProfileNone); - - const VASupportedProfiles::ProfileInfo* profile_info = - VASupportedProfiles::Get().IsProfileSupported(kEncodeConstantBitrate, - va_profile); - if (!profile_info) - continue; - - VideoEncodeAccelerator::SupportedProfile profile; - profile.profile = media_profile; - profile.min_resolution = profile_info->min_resolution; - profile.max_resolution = profile_info->max_resolution; - // Maximum framerate of encoded profile. This value is an arbitrary - // limit and not taken from HW documentation. - constexpr int kMaxEncoderFramerate = 30; - profile.max_framerate_numerator = kMaxEncoderFramerate; - profile.max_framerate_denominator = 1; - profile.rate_control_modes = media::VideoEncodeAccelerator::kConstantMode; - // This code assumes that the resolutions are the same between CBR and VBR. - // This is checked in a test in vaapi_unittest.cc: VbrAndCbrResolutionsMatch - if (IsVBREncodingSupported(va_profile)) { - profile.rate_control_modes |= - media::VideoEncodeAccelerator::kVariableMode; - } - profile.scalability_modes = - GetSupportedScalabilityModes(media_profile, va_profile); - profiles.push_back(profile); - } - return profiles; -} - -// static -VideoDecodeAccelerator::SupportedProfiles -VaapiWrapper::GetSupportedDecodeProfiles() { - VideoDecodeAccelerator::SupportedProfiles profiles; - - for (const auto& [media_profile, va_profile] : GetProfileCodecMap()) { - DCHECK(va_profile != VAProfileNone); - - const VASupportedProfiles::ProfileInfo* profile_info = - VASupportedProfiles::Get().IsProfileSupported(kDecode, va_profile); - if (!profile_info) - continue; - - VideoDecodeAccelerator::SupportedProfile profile; - profile.profile = media_profile; - profile.max_resolution = profile_info->max_resolution; - profile.min_resolution = profile_info->min_resolution; - profiles.push_back(profile); - } - return profiles; -} - -// static -bool VaapiWrapper::IsDecodeSupported(VAProfile va_profile) { - return VASupportedProfiles::Get().IsProfileSupported(kDecode, va_profile); -} - -// static -VaapiWrapper::InternalFormats VaapiWrapper::GetDecodeSupportedInternalFormats( - VAProfile va_profile) { - const VASupportedProfiles::ProfileInfo* profile_info = - VASupportedProfiles::Get().IsProfileSupported(kDecode, va_profile); - if (!profile_info) - return InternalFormats{}; - return profile_info->supported_internal_formats; -} - -// static -bool VaapiWrapper::IsDecodingSupportedForInternalFormat( - VAProfile va_profile, - unsigned int rt_format) { - static const VaapiWrapper::InternalFormats supported_internal_formats( - VaapiWrapper::GetDecodeSupportedInternalFormats(va_profile)); - switch (rt_format) { - case VA_RT_FORMAT_YUV420: - return supported_internal_formats.yuv420; - case VA_RT_FORMAT_YUV420_10: - return supported_internal_formats.yuv420_10; - case VA_RT_FORMAT_YUV422: - return supported_internal_formats.yuv422; - case VA_RT_FORMAT_YUV444: - return supported_internal_formats.yuv444; - } - return false; -} - -// static -bool VaapiWrapper::GetSupportedResolutions(VAProfile va_profile, - CodecMode codec_mode, - gfx::Size& min_size, - gfx::Size& max_size) { - const VASupportedProfiles::ProfileInfo* profile_info = - VASupportedProfiles::Get().IsProfileSupported(codec_mode, va_profile); - if (!profile_info || profile_info->max_resolution.IsEmpty()) - return false; - - min_size = gfx::Size(std::max(1, profile_info->min_resolution.width()), - std::max(1, profile_info->min_resolution.height())); - max_size = profile_info->max_resolution; - return true; -} - -// static -bool VaapiWrapper::GetJpegDecodeSuitableImageFourCC(unsigned int rt_format, - uint32_t preferred_fourcc, - uint32_t* suitable_fourcc) { - if (!IsDecodingSupportedForInternalFormat(VAProfileJPEGBaseline, rt_format)) - return false; - - // Work around some driver-specific conversion issues. If you add a workaround - // here, please update the VaapiJpegDecoderTest.MinimalImageFormatSupport - // test. - DCHECK_NE(VAImplementation::kInvalid, GetImplementationType()); - if (GetImplementationType() == VAImplementation::kMesaGallium) { - // The VAAPI mesa state tracker only supports conversion from NV12 to YV12 - // and IYUV (synonym of I420). - if (rt_format == VA_RT_FORMAT_YUV420) { - if (preferred_fourcc != VA_FOURCC_I420 && - preferred_fourcc != VA_FOURCC_YV12) { - preferred_fourcc = VA_FOURCC_NV12; - } - } else if (rt_format == VA_RT_FORMAT_YUV422) { - preferred_fourcc = VA_FOURCC('Y', 'U', 'Y', 'V'); - } else { - // Out of the three internal formats we care about (4:2:0, 4:2:2, and - // 4:4:4), this driver should only support the first two. Since we check - // for supported internal formats at the beginning of this function, we - // shouldn't get here. - NOTREACHED(); - return false; - } - } else if (GetImplementationType() == VAImplementation::kIntelI965) { - // Workaround deduced from observations in samus and nocturne: we found that - // - // - For a 4:2:2 image, the internal format is 422H. - // - For a 4:2:0 image, the internal format is IMC3. - // - For a 4:4:4 image, the internal format is 444P. - // - // For these internal formats and an image format of either 422H or P010, an - // intermediate NV12 surface is allocated. Then, a conversion is made from - // {422H, IMC3, 444P} -> NV12 -> {422H, P010}. Unfortunately, the - // NV12 -> {422H, P010} conversion is unimplemented in - // i965_image_pl2_processing(). So, when |preferred_fourcc| is either 422H - // or P010, we can just fallback to I420. - // - if (preferred_fourcc == VA_FOURCC_422H || - preferred_fourcc == VA_FOURCC_P010) { - preferred_fourcc = VA_FOURCC_I420; - } - } else if (GetImplementationType() == VAImplementation::kIntelIHD) { - // (b/159896972): iHD v20.1.1 cannot create Y216 and Y416 images from a - // decoded JPEG on gen 12. It is also failing to support Y800 format. - if (preferred_fourcc == VA_FOURCC_Y216 || - preferred_fourcc == VA_FOURCC_Y416 || - preferred_fourcc == VA_FOURCC_Y800) { - preferred_fourcc = VA_FOURCC_I420; - } - } - - if (!VASupportedImageFormats::Get().IsImageFormatSupported( - VAImageFormat{.fourcc = preferred_fourcc})) { - preferred_fourcc = VA_FOURCC_I420; - } - - // After workarounds, assume the conversion is supported. - *suitable_fourcc = preferred_fourcc; - return true; -} - -// static -bool VaapiWrapper::IsVppProfileSupported() { - return VASupportedProfiles::Get().IsProfileSupported(kVideoProcess, - VAProfileNone); -} - -// static -bool VaapiWrapper::IsVppResolutionAllowed(const gfx::Size& size) { - const VASupportedProfiles::ProfileInfo* profile_info = - VASupportedProfiles::Get().IsProfileSupported(kVideoProcess, - VAProfileNone); - if (!profile_info) - return false; - - return size.width() >= profile_info->min_resolution.width() && - size.width() <= profile_info->max_resolution.width() && - size.height() >= profile_info->min_resolution.height() && - size.height() <= profile_info->max_resolution.height(); -} - -// static -bool VaapiWrapper::IsVppFormatSupported(uint32_t va_fourcc) { - const VASupportedProfiles::ProfileInfo* profile_info = - VASupportedProfiles::Get().IsProfileSupported(kVideoProcess, - VAProfileNone); - if (!profile_info) - return false; - - return base::Contains(profile_info->pixel_formats, va_fourcc); -} - -// static -std::vector VaapiWrapper::GetVppSupportedFormats() { - const VASupportedProfiles::ProfileInfo* profile_info = - VASupportedProfiles::Get().IsProfileSupported(kVideoProcess, - VAProfileNone); - if (!profile_info) - return {}; - - std::vector supported_fourccs; - for (uint32_t pixel_format : profile_info->pixel_formats) { - auto fourcc = Fourcc::FromVAFourCC(pixel_format); - if (!fourcc) - continue; - supported_fourccs.push_back(*fourcc); - } - return supported_fourccs; -} - -// static -bool VaapiWrapper::IsVppSupportedForJpegDecodedSurfaceToFourCC( - unsigned int rt_format, - uint32_t fourcc) { - if (!IsDecodingSupportedForInternalFormat(VAProfileJPEGBaseline, rt_format)) - return false; - - // Workaround: for Mesa VAAPI driver, VPP only supports internal surface - // format for 4:2:0 JPEG image. - DCHECK_NE(VAImplementation::kInvalid, GetImplementationType()); - if (GetImplementationType() == VAImplementation::kMesaGallium && - rt_format != VA_RT_FORMAT_YUV420) { - return false; - } - - return IsVppFormatSupported(fourcc); -} - -// static -bool VaapiWrapper::IsJpegEncodeSupported() { - return VASupportedProfiles::Get().IsProfileSupported(kEncodeConstantBitrate, - VAProfileJPEGBaseline); -} - -// static -bool VaapiWrapper::IsImageFormatSupported(const VAImageFormat& format) { - return VASupportedImageFormats::Get().IsImageFormatSupported(format); -} - -// static -const std::vector& -VaapiWrapper::GetSupportedImageFormatsForTesting() { - return VASupportedImageFormats::Get().GetSupportedImageFormats(); -} - -// static -std::map> -VaapiWrapper::GetSupportedConfigurationsForCodecModeForTesting(CodecMode mode) { - std::map> configurations; - for (const auto& supported_profile : - VASupportedProfiles::Get().supported_profiles_[mode]) { - configurations[supported_profile.va_profile].push_back( - supported_profile.va_entrypoint); - } - return configurations; -} - -// static -VAEntrypoint VaapiWrapper::GetDefaultVaEntryPoint(CodecMode mode, - VAProfile profile) { - switch (mode) { - case VaapiWrapper::kDecode: - return VAEntrypointVLD; -#if BUILDFLAG(IS_CHROMEOS_ASH) - case VaapiWrapper::kDecodeProtected: - if (profile == VAProfileProtected) - return VAEntrypointProtectedContent; - return VAEntrypointVLD; -#endif - case VaapiWrapper::kEncodeConstantBitrate: - case VaapiWrapper::kEncodeConstantQuantizationParameter: - case VaapiWrapper::kEncodeVariableBitrate: - if (profile == VAProfileJPEGBaseline) - return VAEntrypointEncPicture; - DCHECK(IsModeEncoding(mode)); - if (IsLowPowerEncSupported(profile)) - return VAEntrypointEncSliceLP; - return VAEntrypointEncSlice; - case VaapiWrapper::kVideoProcess: - return VAEntrypointVideoProc; - case VaapiWrapper::kCodecModeMax: - NOTREACHED(); - return VAEntrypointVLD; - } -} - -// static -uint32_t VaapiWrapper::BufferFormatToVARTFormat(gfx::BufferFormat fmt) { - switch (fmt) { - case gfx::BufferFormat::BGRX_8888: - case gfx::BufferFormat::BGRA_8888: - case gfx::BufferFormat::RGBX_8888: - case gfx::BufferFormat::RGBA_8888: - return VA_RT_FORMAT_RGB32; - case gfx::BufferFormat::YVU_420: - case gfx::BufferFormat::YUV_420_BIPLANAR: - return VA_RT_FORMAT_YUV420; - case gfx::BufferFormat::P010: - return VA_RT_FORMAT_YUV420_10BPP; - default: - NOTREACHED() << gfx::BufferFormatToString(fmt); - return 0; - } -} - -bool VaapiWrapper::CreateContextAndSurfaces( - unsigned int va_format, - const gfx::Size& size, - const std::vector& surface_usage_hints, - size_t num_surfaces, - std::vector* va_surfaces) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - DVLOG(2) << "Creating " << num_surfaces << " surfaces"; - DCHECK(va_surfaces->empty()); - - if (va_context_id_ != VA_INVALID_ID) { - LOG(ERROR) - << "The current context should be destroyed before creating a new one"; - return false; - } - - if (!CreateSurfaces(va_format, size, surface_usage_hints, num_surfaces, - va_surfaces)) { - return false; - } - - const bool success = CreateContext(size); - if (!success) - DestroyContextAndSurfaces(*va_surfaces); - return success; -} - -std::vector> -VaapiWrapper::CreateContextAndScopedVASurfaces( - unsigned int va_format, - const gfx::Size& size, - const std::vector& usage_hints, - size_t num_surfaces, - const absl::optional& visible_size) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - if (va_context_id_ != VA_INVALID_ID) { - LOG(ERROR) << "The current context should be destroyed before creating a " - "new one"; - return {}; - } - - std::vector> scoped_va_surfaces = - CreateScopedVASurfaces(va_format, size, usage_hints, num_surfaces, - visible_size, /*va_fourcc=*/absl::nullopt); - if (scoped_va_surfaces.empty()) - return {}; - - if (CreateContext(size)) - return scoped_va_surfaces; - - DestroyContext(); - return {}; -} - -bool VaapiWrapper::CreateProtectedSession( - EncryptionScheme encryption, - const std::vector& hw_config, - std::vector* hw_identifier_out) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); -#if BUILDFLAG(IS_CHROMEOS_ASH) - DCHECK_EQ(va_protected_config_id_, VA_INVALID_ID); - DCHECK_EQ(va_protected_session_id_, VA_INVALID_ID); - DCHECK(hw_identifier_out); - if (mode_ != kDecodeProtected) { - LOG(ERROR) << "Cannot attached protected context if not in protected mode"; - return false; - } - if (encryption == EncryptionScheme::kUnencrypted) { - LOG(ERROR) << "Must specify encryption scheme for protected mode"; - return false; - } - const VAProfile va_profile = VAProfileProtected; - const VAEntrypoint entrypoint = GetDefaultVaEntryPoint(mode_, va_profile); - { - base::AutoLockMaybe auto_lock(va_lock_.get()); - std::vector required_attribs; - if (!GetRequiredAttribs(va_lock_, va_display_, mode_, va_profile, - entrypoint, &required_attribs)) { - LOG(ERROR) << "Failed getting required attributes for protected mode"; - return false; - } - DCHECK(!required_attribs.empty()); - - // We need to adjust the attribute for encryption scheme. - for (auto& attrib : required_attribs) { - if (attrib.type == VAConfigAttribProtectedContentCipherMode) { - attrib.value = (encryption == EncryptionScheme::kCbcs) - ? VA_PC_CIPHER_MODE_CBC - : VA_PC_CIPHER_MODE_CTR; - } - } - - VAStatus va_res = vaCreateConfig( - va_display_, va_profile, entrypoint, &required_attribs[0], - required_attribs.size(), &va_protected_config_id_); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVACreateConfig, false); - - va_res = vaCreateProtectedSession(va_display_, va_protected_config_id_, - &va_protected_session_id_); - DCHECK(va_res == VA_STATUS_SUCCESS || - va_protected_session_id_ == VA_INVALID_ID); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVACreateProtectedSession, - false); - } - // We have to hold the VABuffer outside of the lock because its destructor - // will acquire the lock when it goes out of scope. We also must do this after - // we create the protected session. - VAProtectedSessionExecuteBuffer hw_update_buf; - std::unique_ptr hw_update = CreateVABuffer( - VAProtectedSessionExecuteBufferType, sizeof(hw_update_buf)); - { - base::AutoLockMaybe auto_lock(va_lock_.get()); - constexpr size_t kHwIdentifierMaxSize = 64; - memset(&hw_update_buf, 0, sizeof(hw_update_buf)); - hw_update_buf.function_id = VA_TEE_EXEC_TEE_FUNCID_HW_UPDATE; - hw_update_buf.input.data_size = hw_config.size(); - hw_update_buf.input.data = - static_cast(const_cast(hw_config.data())); - hw_update_buf.output.max_data_size = kHwIdentifierMaxSize; - hw_identifier_out->resize(kHwIdentifierMaxSize); - hw_update_buf.output.data = hw_identifier_out->data(); - if (!MapAndCopy_Locked( - hw_update->id(), - {hw_update->type(), hw_update->size(), &hw_update_buf})) { - LOG(ERROR) << "Failed mapping Execute buf"; - return false; - } - - VAStatus va_res = vaProtectedSessionExecute( - va_display_, va_protected_session_id_, hw_update->id()); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVAProtectedSessionExecute, - false); - - ScopedVABufferMapping mapping(va_lock_, va_display_, hw_update->id()); - if (!mapping.IsValid()) { - LOG(ERROR) << "Failed mapping returned Execute buf"; - return false; - } - auto* hw_update_buf_out = - reinterpret_cast(mapping.data()); - if (!hw_update_buf_out->output.data_size) { - LOG(ERROR) << "Received empty HW identifier"; - return false; - } - hw_identifier_out->resize(hw_update_buf_out->output.data_size); - memcpy(hw_identifier_out->data(), hw_update_buf_out->output.data, - hw_update_buf_out->output.data_size); - - // If the decoding context is created, attach the protected session. - // Otherwise this is done in CreateContext when the decoding context is - // created. - return MaybeAttachProtectedSession_Locked(); - } -#else - NOTIMPLEMENTED() << "Protected content mode not supported"; - return false; -#endif -} - -bool VaapiWrapper::IsProtectedSessionDead() { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); -#if BUILDFLAG(IS_CHROMEOS_ASH) - return IsProtectedSessionDead(va_protected_session_id_); -#else - return false; -#endif -} - -#if BUILDFLAG(IS_CHROMEOS_ASH) -bool VaapiWrapper::IsProtectedSessionDead( - VAProtectedSessionID va_protected_session_id) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - if (va_protected_session_id == VA_INVALID_ID) - return false; - - uint8_t alive; - VAProtectedSessionExecuteBuffer tee_exec_buf = {}; - tee_exec_buf.function_id = VA_TEE_EXEC_TEE_FUNCID_IS_SESSION_ALIVE; - tee_exec_buf.input.data_size = 0; - tee_exec_buf.input.data = nullptr; - tee_exec_buf.output.data_size = sizeof(alive); - tee_exec_buf.output.data = &alive; - - base::AutoLockMaybe auto_lock(va_lock_.get()); - VABufferID buf_id; - VAStatus va_res = vaCreateBuffer( - va_display_, va_protected_session_id, VAProtectedSessionExecuteBufferType, - sizeof(tee_exec_buf), 1, &tee_exec_buf, &buf_id); - // Failure here is valid if the protected session has been closed. - if (va_res != VA_STATUS_SUCCESS) - return true; - - va_res = - vaProtectedSessionExecute(va_display_, va_protected_session_id, buf_id); - vaDestroyBuffer(va_display_, buf_id); - if (va_res != VA_STATUS_SUCCESS) - return true; - - return !alive; -} -#endif - -#if BUILDFLAG(IS_CHROMEOS_ASH) -VAProtectedSessionID VaapiWrapper::GetProtectedSessionID() const { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - return va_protected_session_id_; -} -#endif - -void VaapiWrapper::DestroyProtectedSession() { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); -#if BUILDFLAG(IS_CHROMEOS_ASH) - if (va_protected_session_id_ == VA_INVALID_ID) - return; - base::AutoLockMaybe auto_lock(va_lock_.get()); - VAStatus va_res = - vaDestroyProtectedSession(va_display_, va_protected_session_id_); - VA_LOG_ON_ERROR(va_res, VaapiFunctions::kVADestroyProtectedSession); - va_res = vaDestroyConfig(va_display_, va_protected_config_id_); - VA_LOG_ON_ERROR(va_res, VaapiFunctions::kVADestroyConfig); - va_protected_session_id_ = VA_INVALID_ID; - va_protected_config_id_ = VA_INVALID_ID; -#endif -} - -void VaapiWrapper::DestroyContextAndSurfaces( - std::vector va_surfaces) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - DestroyContext(); - DestroySurfaces(va_surfaces); -} - -bool VaapiWrapper::CreateContext(const gfx::Size& size) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - DVLOG(2) << "Creating context"; - base::AutoLockMaybe auto_lock(va_lock_.get()); - - // vaCreateContext() doesn't really need an array of VASurfaceIDs (see - // https://lists.01.org/pipermail/intel-vaapi-media/2017-July/000052.html and - // https://github.com/intel/libva/issues/251); pass a dummy list of valid - // (non-null) IDs until the signature gets updated. - constexpr VASurfaceID* empty_va_surfaces_ids_pointer = nullptr; - constexpr size_t empty_va_surfaces_ids_size = 0u; - - // No flag must be set and passing picture size is irrelevant in the case of - // vpp, just passing 0x0. - const int flag = mode_ != kVideoProcess ? VA_PROGRESSIVE : 0x0; - const gfx::Size picture_size = mode_ != kVideoProcess ? size : gfx::Size(); - if (base::FeatureList::IsEnabled(kVaapiEnforceVideoMinMaxResolution) && - mode_ != kVideoProcess) { - const VASupportedProfiles::ProfileInfo* profile_info = - VASupportedProfiles::Get().IsProfileSupported(mode_, va_profile_, - va_entrypoint_); - DCHECK(profile_info); - const bool is_picture_within_bounds = - gfx::Rect(picture_size) - .Contains(gfx::Rect(profile_info->min_resolution)) && - gfx::Rect(profile_info->max_resolution) - .Contains(gfx::Rect(picture_size)); - if (!is_picture_within_bounds) { - VLOG(2) << "Requested resolution=" << picture_size.ToString() - << " is not within bounds [" - << profile_info->min_resolution.ToString() << ", " - << profile_info->max_resolution.ToString() << "]"; - return false; - } - } - - VAStatus va_res = vaCreateContext( - va_display_, va_config_id_, picture_size.width(), picture_size.height(), - flag, empty_va_surfaces_ids_pointer, empty_va_surfaces_ids_size, - &va_context_id_); - VA_LOG_ON_ERROR(va_res, VaapiFunctions::kVACreateContext); - if (va_res != VA_STATUS_SUCCESS) - return false; - - // TODO(b/200779101): Remove low resolution i965 condition. This was - // added to avoid a duplicated frame specific to quality 7 at ~400kbps. - if (IsModeEncoding(mode_) && IsLowPowerIntelProcessor() && - !(GetImplementationType() == VAImplementation::kIntelI965 && - picture_size.GetArea() <= gfx::Size(320, 240).GetArea())) { - MaybeSetLowQualityEncoding_Locked(); - } - - // If we have a protected session already, attach it to this new context. - return MaybeAttachProtectedSession_Locked(); -} - -scoped_refptr VaapiWrapper::CreateVASurfaceForPixmap( - scoped_refptr pixmap, - bool protected_content) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - const gfx::BufferFormat buffer_format = pixmap->GetBufferFormat(); - if (!BufferFormatToVAFourCC(buffer_format)) { - LOG(ERROR) << "Failed to get the VA fourcc from the buffer format"; - return nullptr; - } - - // TODO(b/233894465): use the DRM_PRIME_2 API with the Mesa Gallium driver - // when AMD supports it. - // TODO(b/233924862): use the DRM_PRIME_2 API with protected content. - // TODO(b/233929647): use the DRM_PRIME_2 API with the i965 driver. - // TODO(b/236746283): remove the kNoModifier check once the modifier is - // plumbed for JPEG decoding and encoding. - const bool use_drm_prime_2 = - GetImplementationType() == VAImplementation::kIntelIHD && - !protected_content && - pixmap->GetBufferFormatModifier() != gfx::NativePixmapHandle::kNoModifier; - - union { - VADRMPRIMESurfaceDescriptor descriptor; - VASurfaceAttribExternalBuffersAndFD va_attrib_extbuf_and_fd; - }; - - if (use_drm_prime_2) { - if (!FillVADRMPRIMESurfaceDescriptor(*pixmap, descriptor)) - return nullptr; - } else { - if (!FillVASurfaceAttribExternalBuffers(*pixmap, va_attrib_extbuf_and_fd)) - return nullptr; - } - - unsigned int va_format = - base::strict_cast(BufferFormatToVARTFormat(buffer_format)); - if (!va_format) { - LOG(ERROR) << "Failed to get the VA RT format from the buffer format"; - return nullptr; - } - - if (protected_content) { - if (GetImplementationType() == VAImplementation::kMesaGallium) { - va_format |= VA_RT_FORMAT_PROTECTED; - } else { - va_attrib_extbuf_and_fd.va_attrib_extbuf.flags = - VA_SURFACE_EXTBUF_DESC_PROTECTED; - } - } - - std::vector va_attribs(2); - - va_attribs[0].type = VASurfaceAttribMemoryType; - va_attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE; - va_attribs[0].value.type = VAGenericValueTypeInteger; - va_attribs[0].value.value.i = use_drm_prime_2 - ? VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2 - : VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME; - - va_attribs[1].type = VASurfaceAttribExternalBufferDescriptor; - va_attribs[1].flags = VA_SURFACE_ATTRIB_SETTABLE; - va_attribs[1].value.type = VAGenericValueTypePointer; - va_attribs[1].value.value.p = use_drm_prime_2 - ? static_cast(&descriptor) - : &va_attrib_extbuf_and_fd.va_attrib_extbuf; - - const gfx::Size size = pixmap->GetBufferSize(); - VASurfaceID va_surface_id = VA_INVALID_ID; - { - base::AutoLockMaybe auto_lock(va_lock_.get()); - VAStatus va_res = vaCreateSurfaces( - va_display_, va_format, base::checked_cast(size.width()), - base::checked_cast(size.height()), &va_surface_id, 1, - &va_attribs[0], va_attribs.size()); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVACreateSurfaces_Importing, - nullptr); - } - DVLOG(3) << __func__ << " " << va_surface_id; - // VASurface shares an ownership of the buffer referred by the passed file - // descriptor. We can release |pixmap| here. - return new VASurface(va_surface_id, size, va_format, - base::BindOnce(&VaapiWrapper::DestroySurface, this)); -} - -scoped_refptr VaapiWrapper::CreateVASurfaceForUserPtr( - const gfx::Size& size, - uintptr_t* buffers, - size_t buffer_size) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - VASurfaceAttribExternalBuffers va_attrib_extbuf{}; - va_attrib_extbuf.num_planes = 3; - va_attrib_extbuf.buffers = buffers; - va_attrib_extbuf.data_size = base::checked_cast(buffer_size); - va_attrib_extbuf.num_buffers = 1u; - va_attrib_extbuf.width = base::checked_cast(size.width()); - va_attrib_extbuf.height = base::checked_cast(size.height()); - va_attrib_extbuf.offsets[0] = 0; - va_attrib_extbuf.offsets[1] = size.GetCheckedArea().ValueOrDie(); - va_attrib_extbuf.offsets[2] = - (size.GetCheckedArea() * 2).ValueOrDie(); - std::fill(va_attrib_extbuf.pitches, va_attrib_extbuf.pitches + 3, - base::checked_cast(size.width())); - va_attrib_extbuf.pixel_format = VA_FOURCC_RGBP; - - std::vector va_attribs(2); - va_attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE; - va_attribs[0].type = VASurfaceAttribMemoryType; - va_attribs[0].value.type = VAGenericValueTypeInteger; - va_attribs[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_USER_PTR; - - va_attribs[1].flags = VA_SURFACE_ATTRIB_SETTABLE; - va_attribs[1].type = VASurfaceAttribExternalBufferDescriptor; - va_attribs[1].value.type = VAGenericValueTypePointer; - va_attribs[1].value.value.p = &va_attrib_extbuf; - - VASurfaceID va_surface_id = VA_INVALID_ID; - const unsigned int va_format = VA_RT_FORMAT_RGBP; - { - base::AutoLockMaybe auto_lock(va_lock_.get()); - VAStatus va_res = vaCreateSurfaces( - va_display_, va_format, base::checked_cast(size.width()), - base::checked_cast(size.height()), &va_surface_id, 1, - &va_attribs[0], va_attribs.size()); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVACreateSurfaces_Importing, - nullptr); - } - DVLOG(2) << __func__ << " " << va_surface_id; - return new VASurface(va_surface_id, size, va_format, - base::BindOnce(&VaapiWrapper::DestroySurface, this)); -} - -scoped_refptr VaapiWrapper::CreateVASurfaceWithUsageHints( - unsigned int va_rt_format, - const gfx::Size& size, - const std::vector& usage_hints) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - std::vector surfaces; - if (!CreateSurfaces(va_rt_format, size, usage_hints, 1, &surfaces)) - return nullptr; - return new VASurface(surfaces[0], size, va_rt_format, - base::BindOnce(&VaapiWrapper::DestroySurface, this)); -} - -std::unique_ptr -VaapiWrapper::ExportVASurfaceAsNativePixmapDmaBufUnwrapped( - VASurfaceID va_surface_id, - const gfx::Size& va_surface_size) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - DCHECK_NE(va_surface_id, VA_INVALID_SURFACE); - DCHECK(!va_surface_size.IsEmpty()); - - if (GetImplementationType() == VAImplementation::kNVIDIAVDPAU) { - LOG(ERROR) << "Disabled due to potential breakage."; - return nullptr; - } - - VADRMPRIMESurfaceDescriptor descriptor; - { - base::AutoLockMaybe auto_lock(va_lock_.get()); - VAStatus va_res = vaSyncSurface(va_display_, va_surface_id); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVASyncSurface, nullptr); - va_res = vaExportSurfaceHandle( - va_display_, va_surface_id, VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2, - VA_EXPORT_SURFACE_READ_ONLY | VA_EXPORT_SURFACE_SEPARATE_LAYERS, - &descriptor); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVAExportSurfaceHandle, - nullptr); - } - - // We only support one bo containing all the planes. The fd should be owned by - // us: per va/va.h, "the exported handles are owned by the caller." - // - // TODO(crbug.com/974438): support multiple buffer objects so that this can - // work in AMD. - if (descriptor.num_objects != 1u) { - DVLOG(1) << "Only surface descriptors with one bo are supported"; - NOTREACHED(); - return nullptr; - } - base::ScopedFD bo_fd(descriptor.objects[0].fd); - const uint64_t bo_modifier = descriptor.objects[0].drm_format_modifier; - - // Translate the pixel format to a gfx::BufferFormat. - gfx::BufferFormat buffer_format; - switch (descriptor.fourcc) { - case VA_FOURCC_IMC3: - // IMC3 is like I420 but all the planes have the same stride. This is used - // for decoding 4:2:0 JPEGs in the Intel i965 driver. We don't currently - // have a gfx::BufferFormat for YUV420. Instead, we reuse YVU_420 and - // later swap the U and V planes. - // - // TODO(andrescj): revisit this once crrev.com/c/1573718 lands. - buffer_format = gfx::BufferFormat::YVU_420; - break; - case VA_FOURCC_NV12: - buffer_format = gfx::BufferFormat::YUV_420_BIPLANAR; - break; - default: - LOG(ERROR) << "Cannot export a surface with FOURCC " - << FourccToString(descriptor.fourcc); - return nullptr; - } - - gfx::NativePixmapHandle handle{}; - handle.modifier = bo_modifier; - for (uint32_t layer = 0; layer < descriptor.num_layers; layer++) { - // According to va/va_drmcommon.h, if VA_EXPORT_SURFACE_SEPARATE_LAYERS is - // specified, each layer should contain one plane. - DCHECK_EQ(1u, descriptor.layers[layer].num_planes); - - auto plane_fd = base::ScopedFD( - layer == 0 ? bo_fd.release() - : HANDLE_EINTR(dup(handle.planes[0].fd.get()))); - PCHECK(plane_fd.is_valid()); - constexpr uint64_t kZeroSizeToPreventMapping = 0u; - handle.planes.emplace_back( - base::checked_cast(descriptor.layers[layer].pitch[0]), - base::checked_cast(descriptor.layers[layer].offset[0]), - kZeroSizeToPreventMapping, std::move(plane_fd)); - } - - if (descriptor.fourcc == VA_FOURCC_IMC3) { - // Recall that for VA_FOURCC_IMC3, we will return a format of - // gfx::BufferFormat::YVU_420, so we need to swap the U and V planes to keep - // the semantics. - DCHECK_EQ(3u, handle.planes.size()); - std::swap(handle.planes[1], handle.planes[2]); - } - - auto exported_pixmap = std::make_unique(); - exported_pixmap->va_surface_resolution = - gfx::Size(base::checked_cast(descriptor.width), - base::checked_cast(descriptor.height)); - exported_pixmap->byte_size = - base::strict_cast(descriptor.objects[0].size); - if (!gfx::Rect(exported_pixmap->va_surface_resolution) - .Contains(gfx::Rect(va_surface_size))) { - LOG(ERROR) << "A " << va_surface_size.ToString() - << " surface cannot be contained by a " - << exported_pixmap->va_surface_resolution.ToString() - << " buffer"; - return nullptr; - } - exported_pixmap->pixmap = base::MakeRefCounted( - va_surface_size, buffer_format, std::move(handle)); - return exported_pixmap; -} - -std::unique_ptr -VaapiWrapper::ExportVASurfaceAsNativePixmapDmaBuf(const VASurface& va_surface) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - if (va_surface.id() == VA_INVALID_SURFACE || va_surface.size().IsEmpty() || - va_surface.format() == kInvalidVaRtFormat) { - LOG(ERROR) << "Cannot export an invalid surface"; - return nullptr; - } - return ExportVASurfaceAsNativePixmapDmaBufUnwrapped(va_surface.id(), - va_surface.size()); -} - -std::unique_ptr -VaapiWrapper::ExportVASurfaceAsNativePixmapDmaBuf( - const ScopedVASurface& scoped_va_surface) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - if (!scoped_va_surface.IsValid()) { - LOG(ERROR) << "Cannot export an invalid surface"; - return nullptr; - } - return ExportVASurfaceAsNativePixmapDmaBufUnwrapped(scoped_va_surface.id(), - scoped_va_surface.size()); -} - -bool VaapiWrapper::SyncSurface(VASurfaceID va_surface_id) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - DCHECK_NE(va_surface_id, VA_INVALID_ID); - - base::AutoLockMaybe auto_lock(va_lock_.get()); - - VAStatus va_res = vaSyncSurface(va_display_, va_surface_id); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVASyncSurface, false); - return true; -} - -bool VaapiWrapper::SubmitBuffer(VABufferType va_buffer_type, - size_t size, - const void* data) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - TRACE_EVENT0("media,gpu", "VaapiWrapper::SubmitBuffer"); - base::AutoLockMaybe auto_lock(va_lock_.get()); - return SubmitBuffer_Locked({va_buffer_type, size, data}); -} - -bool VaapiWrapper::SubmitBuffers( - const std::vector& va_buffers) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - TRACE_EVENT0("media,gpu", "VaapiWrapper::SubmitBuffers"); - base::AutoLockMaybe auto_lock(va_lock_.get()); - for (const VABufferDescriptor& va_buffer : va_buffers) { - if (!SubmitBuffer_Locked(va_buffer)) - return false; - } - return true; -} - -void VaapiWrapper::DestroyPendingBuffers() { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - TRACE_EVENT0("media,gpu", "VaapiWrapper::DestroyPendingBuffers"); - base::AutoLockMaybe auto_lock(va_lock_.get()); - DestroyPendingBuffers_Locked(); -} - -void VaapiWrapper::DestroyPendingBuffers_Locked() { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - TRACE_EVENT0("media,gpu", "VaapiWrapper::DestroyPendingBuffers_Locked"); - MAYBE_ASSERT_ACQUIRED(va_lock_); - for (const auto& pending_va_buf : pending_va_buffers_) { - VAStatus va_res = vaDestroyBuffer(va_display_, pending_va_buf); - VA_LOG_ON_ERROR(va_res, VaapiFunctions::kVADestroyBuffer); - } - pending_va_buffers_.clear(); -} - -bool VaapiWrapper::ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - base::AutoLockMaybe auto_lock(va_lock_.get()); - bool result = Execute_Locked(va_surface_id, pending_va_buffers_); - DestroyPendingBuffers_Locked(); - return result; -} - -bool VaapiWrapper::MapAndCopyAndExecute( - VASurfaceID va_surface_id, - const std::vector>& va_buffers) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - DCHECK_NE(va_surface_id, VA_INVALID_SURFACE); - - TRACE_EVENT0("media,gpu", "VaapiWrapper::MapAndCopyAndExecute"); - base::AutoLockMaybe auto_lock(va_lock_.get()); - std::vector va_buffer_ids; - - for (const auto& va_buffer : va_buffers) { - const VABufferID va_buffer_id = va_buffer.first; - const VABufferDescriptor& descriptor = va_buffer.second; - DCHECK_NE(va_buffer_id, VA_INVALID_ID); - - if (!MapAndCopy_Locked(va_buffer_id, descriptor)) - return false; - - va_buffer_ids.push_back(va_buffer_id); - } - - return Execute_Locked(va_surface_id, va_buffer_ids); -} - -#if BUILDFLAG(USE_VAAPI_X11) -bool VaapiWrapper::PutSurfaceIntoPixmap(VASurfaceID va_surface_id, - x11::Pixmap x_pixmap, - gfx::Size dest_size) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - base::AutoLockMaybe auto_lock(va_lock_.get()); - - VAStatus va_res = vaSyncSurface(va_display_, va_surface_id); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVASyncSurface, false); - - // Put the data into an X Pixmap. - va_res = - vaPutSurface(va_display_, va_surface_id, static_cast(x_pixmap), - 0, 0, dest_size.width(), dest_size.height(), 0, 0, - dest_size.width(), dest_size.height(), nullptr, 0, 0); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVAPutSurface, false); - return true; -} -#endif // BUILDFLAG(USE_VAAPI_X11) - -std::unique_ptr VaapiWrapper::CreateVaImage( - VASurfaceID va_surface_id, - VAImageFormat* format, - const gfx::Size& size) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - std::unique_ptr scoped_image; - { - base::AutoLockMaybe auto_lock(va_lock_.get()); - - VAStatus va_res = vaSyncSurface(va_display_, va_surface_id); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVASyncSurface, nullptr); - - scoped_image = std::make_unique(va_lock_, va_display_, - va_surface_id, format, size); - } - return scoped_image->IsValid() ? std::move(scoped_image) : nullptr; -} - -bool VaapiWrapper::UploadVideoFrameToSurface(const VideoFrame& frame, - VASurfaceID va_surface_id, - const gfx::Size& va_surface_size) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - TRACE_EVENT0("media,gpu", "VaapiWrapper::UploadVideoFrameToSurface"); - base::AutoLockMaybe auto_lock(va_lock_.get()); - TRACE_EVENT0("media,gpu", "VaapiWrapper::UploadVideoFrameToSurfaceLocked"); - - if (frame.visible_rect().origin() != gfx::Point(0, 0)) { - LOG(ERROR) << "The origin of the frame's visible rectangle is not (0, 0), " - << "frame.visible_rect().origin()=" - << frame.visible_rect().origin().ToString(); - return false; - } - - const gfx::Size visible_size = frame.visible_rect().size(); - bool needs_va_put_image = false; - VAImage image; - VAStatus va_res = vaDeriveImage(va_display_, va_surface_id, &image); - if (va_res == VA_STATUS_ERROR_OPERATION_FAILED) { - DVLOG(4) << "vaDeriveImage failed and fallback to Create_PutImage"; - constexpr VAImageFormat kImageFormatNV12{.fourcc = VA_FOURCC_NV12, - .byte_order = VA_LSB_FIRST, - .bits_per_pixel = 12}; - VAImageFormat image_format = kImageFormatNV12; - - va_res = vaCreateImage(va_display_, &image_format, va_surface_size.width(), - va_surface_size.height(), &image); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVACreateImage, false); - needs_va_put_image = true; - } - base::ScopedClosureRunner vaimage_deleter( - base::BindOnce(&DestroyVAImage, va_display_, image)); - - if (image.format.fourcc != VA_FOURCC_NV12) { - LOG(ERROR) << "Unsupported image format: " << image.format.fourcc; - return false; - } - - if (image.width % 2 != 0 || image.height % 2 != 0) { - LOG(ERROR) << "Buffer's width and height are not even, " - << "width=" << image.width << ", height=" << image.height; - return false; - } - - if (!gfx::Rect(image.width, image.height).Contains(gfx::Rect(visible_size))) { - LOG(ERROR) << "Buffer too small to fit the frame."; - return false; - } - - ScopedVABufferMapping mapping(va_lock_, va_display_, image.buf); - if (!mapping.IsValid()) - return false; - uint8_t* image_ptr = static_cast(mapping.data()); - - if (!ClearNV12Padding(image, visible_size, image_ptr)) { - LOG(ERROR) << "Failed to clear non visible area of VAImage"; - return false; - } - - int ret = 0; - { - TRACE_EVENT0("media,gpu", "VaapiWrapper::UploadVideoFrameToSurface_copy"); - - std::unique_ptr auto_unlock; - if (va_lock_) - auto_unlock = std::make_unique(*va_lock_); - if (frame.format() == PIXEL_FORMAT_I420) { - ret = libyuv::I420ToNV12( - frame.data(VideoFrame::kYPlane), frame.stride(VideoFrame::kYPlane), - frame.data(VideoFrame::kUPlane), frame.stride(VideoFrame::kUPlane), - frame.data(VideoFrame::kVPlane), frame.stride(VideoFrame::kVPlane), - image_ptr + image.offsets[0], image.pitches[0], - image_ptr + image.offsets[1], image.pitches[1], visible_size.width(), - visible_size.height()); - } else { - LOG(ERROR) << "Unsupported pixel format: " - << VideoPixelFormatToString(frame.format()); - return false; - } - } - if (needs_va_put_image) { - va_res = vaPutImage(va_display_, va_surface_id, image.image_id, 0, 0, - visible_size.width(), visible_size.height(), 0, 0, - visible_size.width(), visible_size.height()); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVAPutImage, false); - } - return ret == 0; -} - -std::unique_ptr VaapiWrapper::CreateVABuffer(VABufferType type, - size_t size) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - TRACE_EVENT0("media,gpu", "VaapiWrapper::CreateVABuffer"); - base::AutoLockMaybe auto_lock(va_lock_.get()); - TRACE_EVENT2("media,gpu", "VaapiWrapper::CreateVABufferLocked", "type", type, - "size", size); -#if BUILDFLAG(IS_CHROMEOS_ASH) - VAContextID context_id = type == VAProtectedSessionExecuteBufferType - ? va_protected_session_id_ - : va_context_id_; -#else - VAContextID context_id = va_context_id_; -#endif - - if (context_id == VA_INVALID_ID) - return nullptr; - return ScopedVABuffer::Create(va_lock_, va_display_, context_id, type, size); -} - -uint64_t VaapiWrapper::GetEncodedChunkSize(VABufferID buffer_id, - VASurfaceID sync_surface_id) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - TRACE_EVENT0("media,gpu", "VaapiWrapper::GetEncodedChunkSize"); - base::AutoLockMaybe auto_lock(va_lock_.get()); - TRACE_EVENT0("media,gpu", "VaapiWrapper::GetEncodedChunkSizeLocked"); - - // vaSyncSurface() is not necessary on Intel platforms as long as there is a - // vaMapBuffer() like in ScopedVABufferMapping below. - // vaSyncSurface() synchronizes all active workloads (potentially many, e.g. - // for k-SVC encoding). On Intel, we'd rather use the more fine-grained - // vaMapBuffer() in ScopedVABufferMapping below. see b/184312032. - if (VaapiWrapper::GetImplementationType() != VAImplementation::kIntelI965 && - VaapiWrapper::GetImplementationType() != VAImplementation::kIntelIHD) { - VAStatus va_res = vaSyncSurface(va_display_, sync_surface_id); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVASyncSurface, 0u); - } - - ScopedVABufferMapping mapping(va_lock_, va_display_, buffer_id); - if (!mapping.IsValid()) - return 0u; - - uint64_t coded_data_size = 0; - for (auto* buffer_segment = - reinterpret_cast(mapping.data()); - buffer_segment; buffer_segment = reinterpret_cast( - buffer_segment->next)) { - coded_data_size += buffer_segment->size; - } - return coded_data_size; -} - -bool VaapiWrapper::DownloadFromVABuffer( - VABufferID buffer_id, - absl::optional sync_surface_id, - uint8_t* target_ptr, - size_t target_size, - size_t* coded_data_size) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - DCHECK(target_ptr); - TRACE_EVENT0("media,gpu", "VaapiWrapper::DownloadFromVABuffer"); - base::AutoLockMaybe auto_lock(va_lock_.get()); - TRACE_EVENT0("media,gpu", "VaapiWrapper::DownloadFromVABufferLocked"); - - // vaSyncSurface() is not necessary on Intel platforms as long as there is a - // vaMapBuffer() like in ScopedVABufferMapping below, see b/184312032. - // |sync_surface_id| will be nullopt because it has been synced already. - // vaSyncSurface() is not executed in the case. - if (sync_surface_id && - GetImplementationType() != VAImplementation::kIntelI965 && - GetImplementationType() != VAImplementation::kIntelIHD) { - TRACE_EVENT0("media,gpu", "VaapiWrapper::DownloadFromVABuffer_SyncSurface"); - const VAStatus va_res = vaSyncSurface(va_display_, *sync_surface_id); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVASyncSurface, false); - } - - ScopedVABufferMapping mapping(va_lock_, va_display_, buffer_id); - if (!mapping.IsValid()) - return false; - auto* buffer_segment = - reinterpret_cast(mapping.data()); - - // memcpy calls should be fast, unlocking and relocking for unmapping might - // cause another thread to acquire the lock and we'd have to wait delaying the - // notification that the encode is done. - { - TRACE_EVENT0("media,gpu", "VaapiWrapper::DownloadFromVABuffer_copy"); - *coded_data_size = 0; - - while (buffer_segment) { - DCHECK(buffer_segment->buf); - - if (buffer_segment->size > target_size) { - LOG(ERROR) << "Insufficient output buffer size: " << target_size - << ", the buffer segment size: " << buffer_segment->size; - break; - } - memcpy(target_ptr, buffer_segment->buf, buffer_segment->size); - - target_ptr += buffer_segment->size; - target_size -= buffer_segment->size; - *coded_data_size += buffer_segment->size; - buffer_segment = - reinterpret_cast(buffer_segment->next); - } - } - - return buffer_segment == nullptr; -} - -bool VaapiWrapper::GetVAEncMaxNumOfRefFrames(VideoCodecProfile profile, - size_t* max_ref_frames) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - const VAProfile va_profile = - ProfileToVAProfile(profile, CodecMode::kEncodeConstantBitrate); - VAConfigAttrib attrib; - attrib.type = VAConfigAttribEncMaxRefFrames; - - base::AutoLockMaybe auto_lock(va_lock_.get()); - VAStatus va_res = vaGetConfigAttributes(va_display_, va_profile, - va_entrypoint_, &attrib, 1); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVAGetConfigAttributes, false); - - *max_ref_frames = attrib.value; - return true; -} - -bool VaapiWrapper::GetSupportedPackedHeaders(VideoCodecProfile profile, - bool& packed_sps, - bool& packed_pps, - bool& packed_slice) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - const VAProfile va_profile = - ProfileToVAProfile(profile, CodecMode::kEncodeConstantBitrate); - VAConfigAttrib attrib{}; - attrib.type = VAConfigAttribEncPackedHeaders; - base::AutoLockMaybe auto_lock(va_lock_.get()); - const VAStatus va_res = vaGetConfigAttributes(va_display_, va_profile, - va_entrypoint_, &attrib, 1); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVAGetConfigAttributes, false); - packed_sps = attrib.value & VA_ENC_PACKED_HEADER_SEQUENCE; - packed_pps = attrib.value & VA_ENC_PACKED_HEADER_PICTURE; - packed_slice = attrib.value & VA_ENC_PACKED_HEADER_SLICE; - - return true; -} - -bool VaapiWrapper::IsRotationSupported() { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - base::AutoLockMaybe auto_lock(va_lock_.get()); - VAProcPipelineCaps pipeline_caps; - memset(&pipeline_caps, 0, sizeof(pipeline_caps)); - VAStatus va_res = vaQueryVideoProcPipelineCaps(va_display_, va_context_id_, - nullptr, 0, &pipeline_caps); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVAQueryVideoProcPipelineCaps, - false); - - if (!pipeline_caps.rotation_flags) { - DVLOG(2) << "VA-API driver doesn't support any rotation"; - return false; - } - return true; -} - -bool VaapiWrapper::BlitSurface(const VASurface& va_surface_src, - const VASurface& va_surface_dest, - absl::optional src_rect, - absl::optional dest_rect, - VideoRotation rotation -#if BUILDFLAG(IS_CHROMEOS_ASH) - , - VAProtectedSessionID va_protected_session_id -#endif -) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - DCHECK_EQ(mode_, kVideoProcess); - base::AutoLockMaybe auto_lock(va_lock_.get()); - - // Create a buffer for VPP if it has not been created. - if (!va_buffer_for_vpp_) { - DCHECK_NE(VA_INVALID_ID, va_context_id_); - va_buffer_for_vpp_ = - ScopedVABuffer::Create(va_lock_, va_display_, va_context_id_, - VAProcPipelineParameterBufferType, - sizeof(VAProcPipelineParameterBuffer)); - if (!va_buffer_for_vpp_) - return false; - } - - // Note that since we store pointers to these regions in our mapping below, - // these may be accessed after the Unmap() below. These must therefore live - // until the end of the function. - VARectangle input_region; - VARectangle output_region; - { - ScopedVABufferMapping mapping(va_lock_, va_display_, - va_buffer_for_vpp_->id()); - if (!mapping.IsValid()) - return false; - auto* pipeline_param = - reinterpret_cast(mapping.data()); - - memset(pipeline_param, 0, sizeof *pipeline_param); - if (!src_rect) - src_rect.emplace(gfx::Rect(va_surface_src.size())); - if (!dest_rect) - dest_rect.emplace(gfx::Rect(va_surface_dest.size())); - - input_region.x = src_rect->x(); - input_region.y = src_rect->y(); - input_region.width = src_rect->width(); - input_region.height = src_rect->height(); - pipeline_param->surface_region = &input_region; - pipeline_param->surface = va_surface_src.id(); - pipeline_param->surface_color_standard = VAProcColorStandardNone; - - output_region.x = dest_rect->x(); - output_region.y = dest_rect->y(); - output_region.width = dest_rect->width(); - output_region.height = dest_rect->height(); - pipeline_param->output_region = &output_region; - pipeline_param->output_background_color = 0xff000000; - pipeline_param->output_color_standard = VAProcColorStandardNone; - pipeline_param->filter_flags = VA_FILTER_SCALING_DEFAULT; - - switch (rotation) { - case VIDEO_ROTATION_0: - pipeline_param->rotation_state = VA_ROTATION_NONE; - break; - case VIDEO_ROTATION_90: - pipeline_param->rotation_state = VA_ROTATION_90; - break; - case VIDEO_ROTATION_180: - pipeline_param->rotation_state = VA_ROTATION_180; - break; - case VIDEO_ROTATION_270: - pipeline_param->rotation_state = VA_ROTATION_270; - break; - } - } - -#if BUILDFLAG(IS_CHROMEOS_ASH) - base::ScopedClosureRunner protected_session_detacher; - if (va_protected_session_id != VA_INVALID_ID) { - const VAStatus va_res = vaAttachProtectedSession( - va_display_, va_context_id_, va_protected_session_id); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVAAttachProtectedSession, - false); - // Note that we use a lambda expression to wrap vaDetachProtectedSession() - // because the function in |protected_session_detacher| must return void. - protected_session_detacher.ReplaceClosure(base::BindOnce( - [](VADisplay va_display, VAContextID va_context_id) { - vaDetachProtectedSession(va_display, va_context_id); - }, - va_display_, va_context_id_)); - } -#endif // BUILDFLAG(IS_CHROMEOS_ASH) - - TRACE_EVENT2("media,gpu", "VaapiWrapper::BlitSurface", "src_rect", - src_rect->ToString(), "dest_rect", dest_rect->ToString()); - - VAStatus va_res = - vaBeginPicture(va_display_, va_context_id_, va_surface_dest.id()); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVABeginPicture, false); - - VABufferID va_buffer_id = va_buffer_for_vpp_->id(); - va_res = vaRenderPicture(va_display_, va_context_id_, &va_buffer_id, 1); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVARenderPicture_Vpp, false); - va_res = vaEndPicture(va_display_, va_context_id_); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVAEndPicture, false); - - return true; -} - -// static -void VaapiWrapper::PreSandboxInitialization() { - VADisplayState::PreSandboxInitialization(); - - const std::string va_suffix(std::to_string(VA_MAJOR_VERSION + 1)); - StubPathMap paths; - - paths[kModuleVa].push_back(std::string("libva.so.") + va_suffix); - paths[kModuleVa_drm].push_back(std::string("libva-drm.so.") + va_suffix); -#if BUILDFLAG(USE_VAAPI_X11) - paths[kModuleVa_x11].push_back(std::string("libva-x11.so.") + va_suffix); -#endif -#if BUILDFLAG(IS_CHROMEOS_ASH) - paths[kModuleVa_prot].push_back(std::string("libva.so.") + va_suffix); -#endif - - // InitializeStubs dlopen() VA-API libraries - // libva.so - // libva-x11.so (X11) - // libva-drm.so (X11 and Ozone). - static bool result = InitializeStubs(paths); - if (!result) { - static const char kErrorMsg[] = "Failed to initialize VAAPI libs"; - LOG(ERROR) << kErrorMsg; - } - - // VASupportedProfiles::Get creates VADisplayState and in so doing - // driver associated libraries are dlopen(), to know: - // i965_drv_video.so - // hybrid_drv_video.so (platforms that support it) - // libcmrt.so (platforms that support it) - VASupportedProfiles::Get(); -} - -VaapiWrapper::VaapiWrapper(CodecMode mode, bool enforce_sequence_affinity) - : mode_(mode), - enforce_sequence_affinity_(enforce_sequence_affinity), - va_lock_(VADisplayState::Get()->va_lock()), - va_display_(nullptr), - va_profile_(VAProfileNone), - va_entrypoint_(kVAEntrypointInvalid) {} - -VaapiWrapper::~VaapiWrapper() { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - // Destroy ScopedVABuffer before VaapiWrappers are destroyed to ensure - // VADisplay is valid on ScopedVABuffer's destruction. - va_buffer_for_vpp_.reset(); - DestroyPendingBuffers(); - DestroyContext(); - Deinitialize(); -} - -bool VaapiWrapper::Initialize(VAProfile va_profile, - EncryptionScheme encryption_scheme) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); -#if DCHECK_IS_ON() - if (mode_ == kEncodeConstantQuantizationParameter) { - DCHECK_NE(va_profile, VAProfileJPEGBaseline) - << "JPEG Encoding doesn't support CQP bitrate control"; - } - if (mode_ == kEncodeVariableBitrate) { - DCHECK_NE(va_profile, VAProfileJPEGBaseline) - << "JPEG Encoding doesn't support VBR bitrate control"; - } -#endif // DCHECK_IS_ON() - -#if BUILDFLAG(IS_CHROMEOS_ASH) - if (encryption_scheme != EncryptionScheme::kUnencrypted && - mode_ != kDecodeProtected) { - return false; - } -#endif - - const VAEntrypoint entrypoint = GetDefaultVaEntryPoint(mode_, va_profile); - - base::AutoLockMaybe auto_lock(va_lock_.get()); - std::vector required_attribs; - if (!GetRequiredAttribs(va_lock_, va_display_, mode_, va_profile, entrypoint, - &required_attribs)) { - return false; - } - -#if BUILDFLAG(IS_CHROMEOS_ASH) - if (encryption_scheme != EncryptionScheme::kUnencrypted) { - DCHECK(!required_attribs.empty()); - // We need to adjust the attribute for encryption scheme. - for (auto& attrib : required_attribs) { - if (attrib.type == VAConfigAttribEncryption) { - attrib.value = (encryption_scheme == EncryptionScheme::kCbcs) - ? VA_ENCRYPTION_TYPE_SUBSAMPLE_CBC - : VA_ENCRYPTION_TYPE_SUBSAMPLE_CTR; - } - } - } -#endif // BUILDFLAG(IS_CHROMEOS_ASH) - - const VAStatus va_res = - vaCreateConfig(va_display_, va_profile, entrypoint, - required_attribs.empty() ? nullptr : &required_attribs[0], - required_attribs.size(), &va_config_id_); - va_profile_ = va_profile; - va_entrypoint_ = entrypoint; - - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVACreateConfig, false); - return true; -} - -void VaapiWrapper::Deinitialize() { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - { - base::AutoLockMaybe auto_lock(va_lock_.get()); -#if BUILDFLAG(IS_CHROMEOS_ASH) - if (va_protected_session_id_ != VA_INVALID_ID) { - VAStatus va_res = - vaDestroyProtectedSession(va_display_, va_protected_session_id_); - VA_LOG_ON_ERROR(va_res, VaapiFunctions::kVADestroyProtectedSession); - va_res = vaDestroyConfig(va_display_, va_protected_config_id_); - VA_LOG_ON_ERROR(va_res, VaapiFunctions::kVADestroyConfig); - } -#endif - if (va_config_id_ != VA_INVALID_ID) { - const VAStatus va_res = vaDestroyConfig(va_display_, va_config_id_); - VA_LOG_ON_ERROR(va_res, VaapiFunctions::kVADestroyConfig); - } -#if BUILDFLAG(IS_CHROMEOS_ASH) - va_protected_session_id_ = VA_INVALID_ID; - va_protected_config_id_ = VA_INVALID_ID; -#endif - va_config_id_ = VA_INVALID_ID; - va_display_ = nullptr; - } - - const VAStatus va_res = VADisplayState::Get()->Deinitialize(); - VA_LOG_ON_ERROR(va_res, VaapiFunctions::kVATerminate); -} - -bool VaapiWrapper::VaInitialize( - const ReportErrorToUMACB& report_error_to_uma_cb) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - report_error_to_uma_cb_ = report_error_to_uma_cb; - if (!VADisplayState::Get()->Initialize()) - return false; - - DCHECK(va_lock_); - if (enforce_sequence_affinity_ && - !UseGlobalVaapiLock(VADisplayState::Get()->implementation_type())) { - va_lock_ = nullptr; - } - - { - base::AutoLockMaybe auto_lock(va_lock_.get()); - va_display_ = VADisplayState::Get()->va_display(); - DCHECK(va_display_) << "VADisplayState hasn't been properly Initialize()d"; - } - return true; -} - -void VaapiWrapper::DestroyContext() { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - base::AutoLockMaybe auto_lock(va_lock_.get()); - DVLOG(2) << "Destroying context"; - - if (va_context_id_ != VA_INVALID_ID) { -#if BUILDFLAG(IS_CHROMEOS_ASH) - if (va_protected_session_id_ != VA_INVALID_ID) { - const VAStatus va_res = - vaDetachProtectedSession(va_display_, va_context_id_); - VA_LOG_ON_ERROR(va_res, VaapiFunctions::kVADetachProtectedSession); - } -#endif - const VAStatus va_res = vaDestroyContext(va_display_, va_context_id_); - VA_LOG_ON_ERROR(va_res, VaapiFunctions::kVADestroyContext); - } - - va_context_id_ = VA_INVALID_ID; -} - -bool VaapiWrapper::CreateSurfaces( - unsigned int va_format, - const gfx::Size& size, - const std::vector& usage_hints, - size_t num_surfaces, - std::vector* va_surfaces) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - DVLOG(2) << "Creating " << num_surfaces << " " << size.ToString() - << " surfaces"; - DCHECK_NE(va_format, kInvalidVaRtFormat); - DCHECK(va_surfaces->empty()); - - va_surfaces->resize(num_surfaces); - VASurfaceAttrib attribute; - - if (GetImplementationType() != VAImplementation::kNVIDIAVDPAU) { - // Nvidia's VAAPI-VDPAU driver doesn't support this attribute - memset(&attribute, 0, sizeof(attribute)); - attribute.type = VASurfaceAttribUsageHint; - attribute.flags = VA_SURFACE_ATTRIB_SETTABLE; - attribute.value.type = VAGenericValueTypeInteger; - attribute.value.value.i = 0; - for (SurfaceUsageHint usage_hint : usage_hints) - attribute.value.value.i |= static_cast(usage_hint); - static_assert(std::is_same::value, - "attribute.value.value.i is not int32_t"); - static_assert(std::is_same::type, - int32_t>::value, - "The underlying type of SurfaceUsageHint is not int32_t"); - } - - VAStatus va_res; - { - base::AutoLockMaybe auto_lock(va_lock_.get()); - if (GetImplementationType() == VAImplementation::kNVIDIAVDPAU) { - va_res = vaCreateSurfaces( - va_display_, va_format, base::checked_cast(size.width()), - base::checked_cast(size.height()), va_surfaces->data(), - num_surfaces, NULL, 0); - } else { - va_res = vaCreateSurfaces( - va_display_, va_format, base::checked_cast(size.width()), - base::checked_cast(size.height()), va_surfaces->data(), - num_surfaces, &attribute, 1u); - } - } - VA_LOG_ON_ERROR(va_res, VaapiFunctions::kVACreateSurfaces_Allocating); - return va_res == VA_STATUS_SUCCESS; -} - -std::vector> -VaapiWrapper::CreateScopedVASurfaces( - unsigned int va_rt_format, - const gfx::Size& size, - const std::vector& usage_hints, - size_t num_surfaces, - const absl::optional& visible_size, - const absl::optional& va_fourcc) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - if (kInvalidVaRtFormat == va_rt_format) { - LOG(ERROR) << "Invalid VA RT format to CreateScopedVASurface"; - return {}; - } - - if (size.IsEmpty()) { - LOG(ERROR) << "Invalid visible size input to CreateScopedVASurface"; - return {}; - } - - VASurfaceAttrib attribs[2]; - unsigned int num_attribs = 1; - memset(attribs, 0, sizeof(attribs)); - attribs[0].type = VASurfaceAttribUsageHint; - attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE; - attribs[0].value.type = VAGenericValueTypeInteger; - attribs[0].value.value.i = 0; - for (SurfaceUsageHint usage_hint : usage_hints) - attribs[0].value.value.i |= static_cast(usage_hint); - - if (va_fourcc) { - num_attribs += 1; - attribs[1].type = VASurfaceAttribPixelFormat; - attribs[1].flags = VA_SURFACE_ATTRIB_SETTABLE; - attribs[1].value.type = VAGenericValueTypeInteger; - attribs[1].value.value.i = base::checked_cast(*va_fourcc); - } - base::AutoLockMaybe auto_lock(va_lock_.get()); - std::vector va_surface_ids(num_surfaces, VA_INVALID_ID); - const VAStatus va_res = vaCreateSurfaces( - va_display_, va_rt_format, base::checked_cast(size.width()), - base::checked_cast(size.height()), va_surface_ids.data(), - num_surfaces, attribs, num_attribs); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVACreateSurfaces_Allocating, - std::vector>{}); - - DCHECK(!base::Contains(va_surface_ids, VA_INVALID_ID)) - << "Invalid VA surface id after vaCreateSurfaces"; - - DCHECK(!visible_size.has_value() || !visible_size->IsEmpty()); - std::vector> scoped_va_surfaces; - scoped_va_surfaces.reserve(num_surfaces); - for (const VASurfaceID va_surface_id : va_surface_ids) { - auto scoped_va_surface = std::make_unique( - this, va_surface_id, visible_size.has_value() ? *visible_size : size, - va_rt_format); - DCHECK(scoped_va_surface->IsValid()); - scoped_va_surfaces.push_back(std::move(scoped_va_surface)); - } - - return scoped_va_surfaces; -} - -void VaapiWrapper::DestroySurfaces(std::vector va_surfaces) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - DVLOG(2) << "Destroying " << va_surfaces.size() << " surfaces"; - - // vaDestroySurfaces() makes no guarantees about VA_INVALID_SURFACE. - base::Erase(va_surfaces, VA_INVALID_SURFACE); - if (va_surfaces.empty()) - return; - - base::AutoLockMaybe auto_lock(va_lock_.get()); - const VAStatus va_res = - vaDestroySurfaces(va_display_, va_surfaces.data(), va_surfaces.size()); - VA_LOG_ON_ERROR(va_res, VaapiFunctions::kVADestroySurfaces); -} - -void VaapiWrapper::DestroySurface(VASurfaceID va_surface_id) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - if (va_surface_id == VA_INVALID_SURFACE) - return; - DVLOG(3) << __func__ << " " << va_surface_id; - base::AutoLockMaybe auto_lock(va_lock_.get()); - const VAStatus va_res = vaDestroySurfaces(va_display_, &va_surface_id, 1); - VA_LOG_ON_ERROR(va_res, VaapiFunctions::kVADestroySurfaces); -} - -bool VaapiWrapper::Execute_Locked(VASurfaceID va_surface_id, - const std::vector& va_buffers) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - TRACE_EVENT0("media,gpu", "VaapiWrapper::Execute_Locked"); - MAYBE_ASSERT_ACQUIRED(va_lock_); - - DVLOG(4) << "Pending VA bufs to commit: " << pending_va_buffers_.size(); - DVLOG(4) << "Target VA surface " << va_surface_id; - const auto decode_start_time = base::TimeTicks::Now(); - - // Get ready to execute for given surface. - VAStatus va_res = vaBeginPicture(va_display_, va_context_id_, va_surface_id); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVABeginPicture, false); - - if (!va_buffers.empty()) { - // vaRenderPicture() needs a non-const pointer, possibly unnecessarily. - VABufferID* va_buffers_data = const_cast(va_buffers.data()); - va_res = vaRenderPicture(va_display_, va_context_id_, va_buffers_data, - base::checked_cast(va_buffers.size())); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVARenderPicture_VABuffers, - false); - } - - // Instruct HW codec to start processing the submitted commands. In theory, - // this shouldn't be blocking, relying on vaSyncSurface() instead, however - // evidence points to it actually waiting for the job to be done. - va_res = vaEndPicture(va_display_, va_context_id_); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVAEndPicture, false); - - if (IsModeDecoding(mode_) && va_profile_ != VAProfileNone && - va_profile_ != VAProfileJPEGBaseline) { - UMA_HISTOGRAM_TIMES("Media.PlatformVideoDecoding.Decode", - base::TimeTicks::Now() - decode_start_time); - } - - return true; -} - -bool VaapiWrapper::SubmitBuffer_Locked(const VABufferDescriptor& va_buffer) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - TRACE_EVENT0("media,gpu", "VaapiWrapper::SubmitBuffer_Locked"); - MAYBE_ASSERT_ACQUIRED(va_lock_); - - DCHECK(IsValidVABufferType(va_buffer.type)); - base::ScopedClosureRunner pending_buffers_destroyer_on_failure(base::BindOnce( - &VaapiWrapper::DestroyPendingBuffers_Locked, base::Unretained(this))); - unsigned int va_buffer_size; - // We use a null |va_buffer|.data for testing: it signals that we want this - // SubmitBuffer_Locked() call to fail. - if (!va_buffer.data || !base::CheckedNumeric(va_buffer.size) - .AssignIfValid(&va_buffer_size)) { - return false; - } - - VABufferID buffer_id; - { - TRACE_EVENT2("media,gpu", - "VaapiWrapper::SubmitBuffer_Locked_vaCreateBuffer", "type", - va_buffer.type, "size", va_buffer_size); - // The type of |data| in vaCreateBuffer() is void*, though a driver must not - // change the |data| buffer. We execute const_cast to limit the type - // mismatch. https://github.com/intel/libva/issues/597 - const VAStatus va_res = vaCreateBuffer( - va_display_, va_context_id_, va_buffer.type, va_buffer_size, 1, - const_cast(va_buffer.data), &buffer_id); - VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVACreateBuffer, false); - } - - pending_va_buffers_.push_back(buffer_id); - pending_buffers_destroyer_on_failure.ReplaceClosure(base::DoNothing()); - return true; -} - -bool VaapiWrapper::MapAndCopy_Locked(VABufferID va_buffer_id, - const VABufferDescriptor& va_buffer) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - MAYBE_ASSERT_ACQUIRED(va_lock_); - - DCHECK_NE(va_buffer_id, VA_INVALID_ID); - DCHECK(IsValidVABufferType(va_buffer.type)); - DCHECK(va_buffer.data); - - ScopedVABufferMapping mapping( - va_lock_, va_display_, va_buffer_id, - base::BindOnce(base::IgnoreResult(&vaDestroyBuffer), va_display_)); - if (!mapping.IsValid()) - return false; - - return memcpy(mapping.data(), va_buffer.data, va_buffer.size); -} - -void VaapiWrapper::MaybeSetLowQualityEncoding_Locked() { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - DCHECK(IsModeEncoding(mode_)); - MAYBE_ASSERT_ACQUIRED(va_lock_); - - // Query if encoding quality (VAConfigAttribEncQualityRange) is supported, and - // if so, use the associated value for lowest quality and power consumption. - VAConfigAttrib attrib{}; - attrib.type = VAConfigAttribEncQualityRange; - const VAStatus va_res = vaGetConfigAttributes(va_display_, va_profile_, - va_entrypoint_, &attrib, 1); - if (va_res != VA_STATUS_SUCCESS) { - LOG(ERROR) << "vaGetConfigAttributes failed: " << vaProfileStr(va_profile_); - return; - } - // From libva's va.h: 'A value less than or equal to 1 means that the - // encoder only has a single "quality setting,"'. - if (attrib.value == VA_ATTRIB_NOT_SUPPORTED || attrib.value <= 1u) - return; - - const size_t temp_size = sizeof(VAEncMiscParameterBuffer) + - sizeof(VAEncMiscParameterBufferQualityLevel); - std::vector temp(temp_size); - - auto* const va_buffer = - reinterpret_cast(temp.data()); - va_buffer->type = VAEncMiscParameterTypeQualityLevel; - auto* const enc_quality = - reinterpret_cast(va_buffer->data); - enc_quality->quality_level = attrib.value; - - const bool success = - SubmitBuffer_Locked({VAEncMiscParameterBufferType, temp_size, va_buffer}); - LOG_IF(ERROR, !success) << "Error setting encoding quality to " - << enc_quality->quality_level; -} - -bool VaapiWrapper::MaybeAttachProtectedSession_Locked() { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - MAYBE_ASSERT_ACQUIRED(va_lock_); - if (va_context_id_ == VA_INVALID_ID) - return true; -#if BUILDFLAG(IS_CHROMEOS_ASH) - if (va_protected_session_id_ == VA_INVALID_ID) - return true; - - VAStatus va_res = vaAttachProtectedSession(va_display_, va_context_id_, - va_protected_session_id_); - VA_LOG_ON_ERROR(va_res, VaapiFunctions::kVAAttachProtectedSession); - return va_res == VA_STATUS_SUCCESS; -#else - return true; -#endif -} - -} // namespace media diff --git a/media/gpu/vaapi/vaapi_wrapper.h b/media/gpu/vaapi/vaapi_wrapper.h deleted file mode 100644 index d30b8571..00000000 --- a/media/gpu/vaapi/vaapi_wrapper.h +++ /dev/null @@ -1,675 +0,0 @@ -// Copyright 2022 The Chromium Authors and Alex313031. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// This file contains an implementation of VaapiWrapper, used by -// VaapiVideoDecodeAccelerator and VaapiH264Decoder for decode, -// and VaapiVideoEncodeAccelerator for encode, to interface -// with libva (VA-API library for hardware video codec). - -#ifndef MEDIA_GPU_VAAPI_VAAPI_WRAPPER_H_ -#define MEDIA_GPU_VAAPI_VAAPI_WRAPPER_H_ - -#include -#include -#include - -#include -#include -#include -#include - -#include "base/files/file.h" -#include "base/gtest_prod_util.h" -#include "base/memory/raw_ptr.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_refptr.h" -#include "base/synchronization/lock.h" -#include "base/thread_annotations.h" -#include "build/chromeos_buildflags.h" -#include "media/gpu/chromeos/fourcc.h" -#include "media/gpu/media_gpu_export.h" -#include "media/gpu/vaapi/va_surface.h" -#include "media/gpu/vaapi/vaapi_utils.h" -#include "media/video/video_decode_accelerator.h" -#include "media/video/video_encode_accelerator.h" -#include "third_party/abseil-cpp/absl/types/optional.h" -#include "ui/gfx/geometry/size.h" - -#if BUILDFLAG(USE_VAAPI_X11) -#include "ui/gfx/x/xproto.h" // nogncheck -#endif // BUILDFLAG(USE_VAAPI_X11) - -namespace gfx { -enum class BufferFormat; -class NativePixmap; -class NativePixmapDmaBuf; -class Rect; -} - -#define MAYBE_ASSERT_ACQUIRED(lock) \ - if (lock) \ - lock->AssertAcquired() - -namespace media { -constexpr unsigned int kInvalidVaRtFormat = 0u; - -class VideoFrame; - -// Enum, function and callback type to allow VaapiWrapper to log errors in VA -// function calls executed on behalf of its owner. |histogram_name| is prebound -// to allow for disinguishing such owners. -enum class VaapiFunctions; -void ReportVaapiErrorToUMA(const std::string& histogram_name, - VaapiFunctions value); -using ReportErrorToUMACB = base::RepeatingCallback; - -// This struct holds a NativePixmapDmaBuf, usually the result of exporting a VA -// surface, and some associated size information needed to tell clients about -// the underlying buffer. -struct NativePixmapAndSizeInfo { - NativePixmapAndSizeInfo(); - ~NativePixmapAndSizeInfo(); - - // The VA-API internal buffer dimensions, which may be different than the - // dimensions requested at the time of creation of the surface (but always - // larger than or equal to those). This can be used for validation in, e.g., - // testing. - gfx::Size va_surface_resolution; - - // The size of the underlying Buffer Object. A use case for this is when an - // image decode is requested and the caller needs to know the size of the - // allocated buffer for caching purposes. - size_t byte_size = 0u; - - // Contains the information needed to use the surface in a graphics API, - // including the visible size (|pixmap|->GetBufferSize()) which should be no - // larger than |va_surface_resolution|. - scoped_refptr pixmap; -}; - -enum class VAImplementation { - kMesaGallium, - kIntelI965, - kIntelIHD, - kOther, - kNVIDIAVDPAU, - kInvalid, -}; - -// This class handles VA-API calls and ensures proper locking of VA-API calls -// to libva, the userspace shim to the HW codec driver. The thread safety of -// libva depends on the backend. If the backend is not thread-safe, we need to -// maintain a global lock that guards all libva calls. This class is fully -// synchronous and its constructor, all of its methods, and its destructor must -// be called on the same sequence. These methods may wait on the |va_lock_| -// which guards libva calls across all VaapiWrapper instances and other libva -// call sites. If the backend is known to be thread safe and -// |enforce_sequence_affinity_| is true when the |kGlobalVaapiLock| flag is -// disabled, |va_lock_| will be null and won't guard any libva calls. -// -// This class is responsible for managing VAAPI connection, contexts and state. -// It is also responsible for managing and freeing VABuffers (not VASurfaces), -// which are used to queue parameters and slice data to the HW codec, -// as well as underlying memory for VASurfaces themselves. -// -// Historical note: the sequence affinity characteristic was introduced as a -// pre-requisite to remove the global *|va_lock_|. However, the legacy -// VaapiVideoDecodeAccelerator is known to use its VaapiWrapper from multiple -// threads. Therefore, to avoid doing a large refactoring of a legacy class, we -// allow it to call VaapiWrapper::Create() or -// VaapiWrapper::CreateForVideoCodec() with |enforce_sequence_affinity| == false -// so that sequence affinity is not enforced. This also indicates that the -// global lock will still be in effect for the VaapiVideoDecodeAccelerator. -class MEDIA_GPU_EXPORT VaapiWrapper - : public base::RefCountedThreadSafe { - public: - enum CodecMode { - kDecode, -#if BUILDFLAG(IS_CHROMEOS_ASH) - // NOTE: A kDecodeProtected VaapiWrapper is created using the actual video - // profile and an extra VAProfileProtected, each with some special added - // VAConfigAttribs. Then when CreateProtectedSession() is called, it will - // then create a protected session using protected profile & entrypoint - // which gets attached to the decoding context (or attached when the - // decoding context is created or re-created). This then enables - // decrypt + decode support in the driver and encrypted frame data can then - // be submitted. - kDecodeProtected, // Decrypt + decode to protected surface. -#endif - kEncodeConstantBitrate, // Encode with Constant Bitrate algorithm. - kEncodeConstantQuantizationParameter, // Encode with Constant Quantization - // Parameter algorithm. - kEncodeVariableBitrate, // Encode with variable bitrate algorithm. - kVideoProcess, - kCodecModeMax, - }; - - // This is enum associated with VASurfaceAttribUsageHint. - enum class SurfaceUsageHint : int32_t { - kGeneric = VA_SURFACE_ATTRIB_USAGE_HINT_GENERIC, - kVideoDecoder = VA_SURFACE_ATTRIB_USAGE_HINT_DECODER, - kVideoEncoder = VA_SURFACE_ATTRIB_USAGE_HINT_ENCODER, - kVideoProcessWrite = VA_SURFACE_ATTRIB_USAGE_HINT_VPP_WRITE, - }; - - using InternalFormats = struct { - bool yuv420 : 1; - bool yuv420_10 : 1; - bool yuv422 : 1; - bool yuv444 : 1; - }; - - // Returns the type of the underlying VA-API implementation. - static VAImplementation GetImplementationType(); - - // Return an instance of VaapiWrapper initialized for |va_profile| and - // |mode|. |report_error_to_uma_cb| will be called independently from - // reporting errors to clients via method return values. - static scoped_refptr Create( - CodecMode mode, - VAProfile va_profile, - EncryptionScheme encryption_scheme, - const ReportErrorToUMACB& report_error_to_uma_cb, - bool enforce_sequence_affinity = true); - - // Create VaapiWrapper for VideoCodecProfile. It maps VideoCodecProfile - // |profile| to VAProfile. - // |report_error_to_uma_cb| will be called independently from reporting - // errors to clients via method return values. - static scoped_refptr CreateForVideoCodec( - CodecMode mode, - VideoCodecProfile profile, - EncryptionScheme encryption_scheme, - const ReportErrorToUMACB& report_error_to_uma_cb, - bool enforce_sequence_affinity = true); - - VaapiWrapper(const VaapiWrapper&) = delete; - VaapiWrapper& operator=(const VaapiWrapper&) = delete; - - // Returns the supported SVC scalability modes for specified profile. - static std::vector GetSupportedScalabilityModes( - VideoCodecProfile media_profile, - VAProfile va_profile); - - // Return the supported video encode profiles. - static VideoEncodeAccelerator::SupportedProfiles GetSupportedEncodeProfiles(); - - // Return the supported video decode profiles. - static VideoDecodeAccelerator::SupportedProfiles GetSupportedDecodeProfiles(); - - // Return true when decoding using |va_profile| is supported. - static bool IsDecodeSupported(VAProfile va_profile); - - // Returns the supported internal formats for decoding using |va_profile|. If - // decoding is not supported for that profile, returns InternalFormats{}. - static InternalFormats GetDecodeSupportedInternalFormats( - VAProfile va_profile); - - // Returns true if |rt_format| is supported for decoding using |va_profile|. - // Returns false if |rt_format| or |va_profile| is not supported for decoding. - static bool IsDecodingSupportedForInternalFormat(VAProfile va_profile, - unsigned int rt_format); - // Gets the minimum and maximum surface sizes allowed for |va_profile| in - // |codec_mode|. Returns true if both sizes can be obtained, false otherwise. - // Each dimension in |min_size| will be at least 1 (as long as this method - // returns true). Additionally, because of the initialization in - // VASupportedProfiles::FillProfileInfo_Locked(), the |max_size| is guaranteed - // to not be empty (as long as this method returns true). - static bool GetSupportedResolutions(VAProfile va_profile, - CodecMode codec_mode, - gfx::Size& min_size, - gfx::Size& max_size); - - // Gets the maximum surface size allowed for decoding using |va_profile|. - // Returns true if the size can be obtained, false otherwise. Because of the - // initialization in VASupportedProfiles::FillProfileInfo_Locked(), the size - // is guaranteed to not be empty (as long as this method returns true). - static bool GetDecodeMaxResolution(VAProfile va_profile, gfx::Size* max_size); - - // Obtains a suitable FOURCC that can be used in vaCreateImage() + - // vaGetImage(). |rt_format| corresponds to the JPEG's subsampling format. - // |preferred_fourcc| is the FOURCC of the format preferred by the caller. If - // it is determined that the VAAPI driver can do the conversion from the - // internal format (|rt_format|), *|suitable_fourcc| is set to - // |preferred_fourcc|. Otherwise, it is set to a supported format. Returns - // true if a suitable FOURCC could be determined, false otherwise (e.g., if - // the |rt_format| is unsupported by the driver). If |preferred_fourcc| is not - // a supported image format, *|suitable_fourcc| is set to VA_FOURCC_I420. - static bool GetJpegDecodeSuitableImageFourCC(unsigned int rt_format, - uint32_t preferred_fourcc, - uint32_t* suitable_fourcc); - - // Checks to see if VAProfileNone is supported on this decoder - static bool IsVppProfileSupported(); - - // Checks the surface size is allowed for VPP. Returns true if the size is - // supported, false otherwise. - static bool IsVppResolutionAllowed(const gfx::Size& size); - - // Returns true if the VPP supports converting from/to |fourcc|. - static bool IsVppFormatSupported(uint32_t fourcc); - - // Returns the pixel formats supported by the VPP. - static std::vector GetVppSupportedFormats(); - - // Returns true if VPP supports the format conversion from a JPEG decoded - // internal surface to a FOURCC. |rt_format| corresponds to the JPEG's - // subsampling format. |fourcc| is the output surface's FOURCC. - static bool IsVppSupportedForJpegDecodedSurfaceToFourCC( - unsigned int rt_format, - uint32_t fourcc); - - // Return true when JPEG encode is supported. - static bool IsJpegEncodeSupported(); - - // Return true when the specified image format is supported. - static bool IsImageFormatSupported(const VAImageFormat& format); - - // Returns the list of VAImageFormats supported by the driver. - static const std::vector& GetSupportedImageFormatsForTesting(); - - // Returns the list of supported profiles and entrypoints for a given |mode|. - static std::map> - GetSupportedConfigurationsForCodecModeForTesting(CodecMode mode); - - static VAEntrypoint GetDefaultVaEntryPoint(CodecMode mode, VAProfile profile); - - static uint32_t BufferFormatToVARTFormat(gfx::BufferFormat fmt); - - // Creates |num_surfaces| VASurfaceIDs of |va_format|, |size| and - // |surface_usage_hints| and, if successful, creates a |va_context_id_| of the - // same size. |surface_usage_hints| may affect an alignment and tiling of the - // created surface. Returns true if successful, with the created IDs in - // |va_surfaces|. The client is responsible for destroying |va_surfaces| via - // DestroyContextAndSurfaces() to free the allocated surfaces. - [[nodiscard]] virtual bool CreateContextAndSurfaces( - unsigned int va_format, - const gfx::Size& size, - const std::vector& surface_usage_hints, - size_t num_surfaces, - std::vector* va_surfaces); - - // Creates |num_surfaces| ScopedVASurfaces of |va_format| and |size| and, if - // successful, creates a |va_context_id_| of the same size. Returns an empty - // vector if creation failed. If |visible_size| is supplied, the returned - // ScopedVASurface's size is set to it. Otherwise, it's set to |size| (refer - // to CreateScopedVASurfaces() for details). - virtual std::vector> - CreateContextAndScopedVASurfaces( - unsigned int va_format, - const gfx::Size& size, - const std::vector& usage_hints, - size_t num_surfaces, - const absl::optional& visible_size); - - // Attempts to create a protected session that will be attached to the - // decoding context to enable encrypted video decoding. If it cannot be - // attached now, it will be attached when the decoding context is created or - // re-created. |encryption| should be the encryption scheme from the - // DecryptConfig. |hw_config| should have been obtained from the OEMCrypto - // implementation via the CdmFactoryDaemonProxy. |hw_identifier_out| is an - // output parameter which will return session specific information which can - // be passed through the ChromeOsCdmContext to retrieve encrypted key - // information. Returns true on success and false otherwise. - bool CreateProtectedSession(media::EncryptionScheme encryption, - const std::vector& hw_config, - std::vector* hw_identifier_out); - // Returns true if and only if we have created a protected session and - // querying libva indicates that our protected session is no longer alive, - // otherwise this will return false. - bool IsProtectedSessionDead(); -#if BUILDFLAG(IS_CHROMEOS_ASH) - // Returns true if and only if |va_protected_session_id| is not VA_INVALID_ID - // and querying libva indicates that the protected session identified by - // |va_protected_session_id| is no longer alive. - bool IsProtectedSessionDead(VAProtectedSessionID va_protected_session_id); - - // Returns the ID of the current protected session or VA_INVALID_ID if there's - // none. This must be called on the same sequence as other methods that use - // the protected session ID internally. - // - // TODO(b/183515581): update this documentation once we force the VaapiWrapper - // to be used on a single sequence. - VAProtectedSessionID GetProtectedSessionID() const; -#endif - // If we have a protected session, destroys it immediately. This should be - // used as part of recovering dead protected sessions. - void DestroyProtectedSession(); - - // Releases the |va_surfaces| and destroys |va_context_id_|. - void DestroyContextAndSurfaces(std::vector va_surfaces); - - // Creates a VAContextID of |size| (unless it's a Vpp context in which case - // |size| is ignored and 0x0 is used instead). The client is responsible for - // releasing said context via DestroyContext() or DestroyContextAndSurfaces(), - // or it will be released on dtor. If a valid |va_protected_session_id_| - // exists, it will be attached to the newly created |va_context_id_| as well. - [[nodiscard]] virtual bool CreateContext(const gfx::Size& size); - - // Destroys the context identified by |va_context_id_|. - virtual void DestroyContext(); - - // Requests |num_surfaces| ScopedVASurfaces of size |size|, |va_rt_format| and - // optionally |va_fourcc|. Returns self-cleaning ScopedVASurfaces or empty - // vector if creation failed. If |visible_size| is supplied, the returned - // ScopedVASurfaces' size are set to it: for example, we may want to request a - // 16x16 surface to decode a 13x12 JPEG: we may want to keep track of the - // visible size 13x12 inside the ScopedVASurface to inform the surface's users - // that that's the only region with meaningful content. If |visible_size| is - // not supplied, we store |size| in the returned ScopedVASurfaces. - virtual std::vector> CreateScopedVASurfaces( - unsigned int va_rt_format, - const gfx::Size& size, - const std::vector& usage_hints, - size_t num_surfaces, - const absl::optional& visible_size, - const absl::optional& va_fourcc); - - // Creates a self-releasing VASurface from |pixmap|. The created VASurface - // shares the ownership of the underlying buffer represented by |pixmap|. The - // ownership of the surface is transferred to the caller. A caller can destroy - // |pixmap| after this method returns and the underlying buffer will be kept - // alive by the VASurface. |protected_content| should only be true if the - // format needs VA_RT_FORMAT_PROTECTED (currently only true for AMD). - virtual scoped_refptr CreateVASurfaceForPixmap( - scoped_refptr pixmap, - bool protected_content = false); - - // Creates a self-releasing VASurface from |buffers|. The ownership of the - // surface is transferred to the caller. |buffers| should be a pointer array - // of size 1, with |buffer_size| corresponding to its size. |size| should be - // the desired surface dimensions (which does not need to map to |buffer_size| - // in any relevant way). |buffers| should be kept alive when using the - // VASurface and for accessing the data after the operation is complete. - scoped_refptr CreateVASurfaceForUserPtr(const gfx::Size& size, - uintptr_t* buffers, - size_t buffer_size); - - // Creates a self-releasing VASurface with specified usage hints. The - // ownership of the surface is transferred to the caller. |size| should be - // the desired surface dimensions. - scoped_refptr CreateVASurfaceWithUsageHints( - unsigned int va_rt_format, - const gfx::Size& size, - const std::vector& usage_hints); - - // Implementations of the pixmap exporter for both types of VASurface. - // See ExportVASurfaceAsNativePixmapDmaBufUnwrapped() for further - // documentation. - std::unique_ptr ExportVASurfaceAsNativePixmapDmaBuf( - const VASurface& va_surface); - std::unique_ptr ExportVASurfaceAsNativePixmapDmaBuf( - const ScopedVASurface& scoped_va_surface); - - // Synchronize the VASurface explicitly. This is useful when sharing a surface - // between contexts. - [[nodiscard]] bool SyncSurface(VASurfaceID va_surface_id); - - // Calls SubmitBuffer_Locked() to request libva to allocate a new VABufferID - // of |va_buffer_type| and |size|, and to map-and-copy the |data| into it. The - // allocated VABufferIDs stay alive until DestroyPendingBuffers_Locked(). Note - // that this method does not submit the buffers for execution, they are simply - // stored until ExecuteAndDestroyPendingBuffers()/Execute_Locked(). The - // ownership of |data| stays with the caller. On failure, all pending buffers - // are destroyed. - [[nodiscard]] bool SubmitBuffer(VABufferType va_buffer_type, - size_t size, - const void* data); - // Convenient templatized version of SubmitBuffer() where |size| is deduced to - // be the size of the type of |*data|. - template - [[nodiscard]] bool SubmitBuffer(VABufferType va_buffer_type, const T* data) { - CHECK(!enforce_sequence_affinity_ || - sequence_checker_.CalledOnValidSequence()); - return SubmitBuffer(va_buffer_type, sizeof(T), data); - } - // Batch-version of SubmitBuffer(), where the lock for accessing libva is - // acquired only once. - struct VABufferDescriptor { - VABufferType type; - size_t size; - const void* data; - }; - [[nodiscard]] bool SubmitBuffers( - const std::vector& va_buffers); - - // Destroys all |pending_va_buffers_| sent via SubmitBuffer*(). Useful when a - // pending job is to be cancelled (on reset or error). - void DestroyPendingBuffers(); - - // Executes job in hardware on target |va_surface_id| and destroys pending - // buffers. Returns false if Execute() fails. - [[nodiscard]] virtual bool ExecuteAndDestroyPendingBuffers( - VASurfaceID va_surface_id); - - // Maps each |va_buffers| ID and copies the data described by the associated - // VABufferDescriptor into it; then calls Execute_Locked() on |va_surface_id|. - [[nodiscard]] bool MapAndCopyAndExecute( - VASurfaceID va_surface_id, - const std::vector>& va_buffers); - -#if BUILDFLAG(USE_VAAPI_X11) - // Put data from |va_surface_id| into |x_pixmap| of size - // |dest_size|, converting/scaling to it. - [[nodiscard]] bool PutSurfaceIntoPixmap(VASurfaceID va_surface_id, - x11::Pixmap x_pixmap, - gfx::Size dest_size); -#endif // BUILDFLAG(USE_VAAPI_X11) - - // Creates a ScopedVAImage from a VASurface |va_surface_id| and map it into - // memory with the given |format| and |size|. If |format| is not equal to the - // internal format, the underlying implementation will do format conversion if - // supported. |size| should be smaller than or equal to the surface. If |size| - // is smaller, the image will be cropped. - std::unique_ptr CreateVaImage(VASurfaceID va_surface_id, - VAImageFormat* format, - const gfx::Size& size); - - // Uploads contents of |frame| into |va_surface_id| for encode. - [[nodiscard]] virtual bool UploadVideoFrameToSurface( - const VideoFrame& frame, - VASurfaceID va_surface_id, - const gfx::Size& va_surface_size); - - // Creates a buffer of |size| bytes to be used as encode output. - virtual std::unique_ptr CreateVABuffer(VABufferType type, - size_t size); - - // Gets the encoded frame linear size of the buffer with given |buffer_id|. - // |sync_surface_id| will be used as a sync point, i.e. it will have to become - // idle before starting the acquirement. |sync_surface_id| should be the - // source surface passed to the encode job. Returns 0 if it fails for any - // reason. - [[nodiscard]] virtual uint64_t GetEncodedChunkSize( - VABufferID buffer_id, - VASurfaceID sync_surface_id); - - // Downloads the contents of the buffer with given |buffer_id| into a buffer - // of size |target_size|, pointed to by |target_ptr|. The number of bytes - // downloaded will be returned in |coded_data_size|. |sync_surface_id| will - // be used as a sync point, i.e. it will have to become idle before starting - // the download. |sync_surface_id| should be the source surface passed - // to the encode job. |sync_surface_id| will be nullopt when it has already - // been synced in GetEncodedChunkSize(). In the case vaSyncSurface() - // is not executed. Returns false if it fails for any reason. For example, the - // linear size of the resulted encoded frame is larger than |target_size|. - [[nodiscard]] virtual bool DownloadFromVABuffer( - VABufferID buffer_id, - absl::optional sync_surface_id, - uint8_t* target_ptr, - size_t target_size, - size_t* coded_data_size); - - // Get the max number of reference frames for encoding supported by the - // driver. - // For H.264 encoding, the value represents the maximum number of reference - // frames for both the reference picture list 0 (bottom 16 bits) and the - // reference picture list 1 (top 16 bits). - [[nodiscard]] virtual bool GetVAEncMaxNumOfRefFrames( - VideoCodecProfile profile, - size_t* max_ref_frames); - - // Gets packed headers are supported for encoding. This is called for - // H264 encoding. |packed_sps|, |packed_pps| and |packed_slice| stands for - // whether packed slice parameter set, packed picture parameter set and packed - // slice header is supported, respectively. - [[nodiscard]] virtual bool GetSupportedPackedHeaders( - VideoCodecProfile profile, - bool& packed_sps, - bool& packed_pps, - bool& packed_slice); - - // Checks if the driver supports frame rotation. - bool IsRotationSupported(); - - // Blits a VASurface |va_surface_src| into another VASurface - // |va_surface_dest| applying pixel format conversion, rotation, cropping - // and scaling if needed. |src_rect| and |dest_rect| are optional. They can - // be used to specify the area used in the blit. If |va_protected_session_id| - // is provided and is not VA_INVALID_ID, the corresponding protected session - // is attached to the VPP context prior to submitting the VPP buffers and - // detached after submitting those buffers. - [[nodiscard]] virtual bool BlitSurface( - const VASurface& va_surface_src, - const VASurface& va_surface_dest, - absl::optional src_rect = absl::nullopt, - absl::optional dest_rect = absl::nullopt, - VideoRotation rotation = VIDEO_ROTATION_0 -#if BUILDFLAG(IS_CHROMEOS_ASH) - , - VAProtectedSessionID va_protected_session_id = VA_INVALID_ID -#endif - ); - - // Initialize static data before sandbox is enabled. - static void PreSandboxInitialization(); - - // vaDestroySurfaces() a vector or a single VASurfaceID. - virtual void DestroySurfaces(std::vector va_surfaces); - virtual void DestroySurface(VASurfaceID va_surface_id); - - protected: - explicit VaapiWrapper(CodecMode mode, bool enforce_sequence_affinity = true); - virtual ~VaapiWrapper(); - - private: - friend class base::RefCountedThreadSafe; - friend class VaapiWrapperTest; - friend class VaapiVideoEncodeAcceleratorTest; - - FRIEND_TEST_ALL_PREFIXES(VaapiTest, LowQualityEncodingSetting); - FRIEND_TEST_ALL_PREFIXES(VaapiUtilsTest, ScopedVAImage); - FRIEND_TEST_ALL_PREFIXES(VaapiUtilsTest, BadScopedVAImage); - FRIEND_TEST_ALL_PREFIXES(VaapiUtilsTest, BadScopedVABufferMapping); - FRIEND_TEST_ALL_PREFIXES(VaapiMinigbmTest, AllocateAndCompareWithMinigbm); - - [[nodiscard]] bool Initialize(VAProfile va_profile, - EncryptionScheme encryption_scheme); - void Deinitialize(); - [[nodiscard]] bool VaInitialize( - const ReportErrorToUMACB& report_error_to_uma_cb); - - // Tries to allocate |num_surfaces| VASurfaceIDs of |size| and |va_format|. - // Fills |va_surfaces| and returns true if successful, or returns false. - [[nodiscard]] bool CreateSurfaces( - unsigned int va_format, - const gfx::Size& size, - const std::vector& usage_hints, - size_t num_surfaces, - std::vector* va_surfaces); - - // Syncs and exports |va_surface_id| as a gfx::NativePixmapDmaBuf. Currently, - // the only VAAPI surface pixel formats supported are VA_FOURCC_IMC3 and - // VA_FOURCC_NV12. - // - // Notes: - // - // - For VA_FOURCC_IMC3, the format of the returned NativePixmapDmaBuf is - // gfx::BufferFormat::YVU_420 because we don't have a YUV_420 format. The - // planes are flipped accordingly, i.e., - // gfx::NativePixmapDmaBuf::GetDmaBufOffset(1) refers to the V plane. - // TODO(andrescj): revisit once crrev.com/c/1573718 lands. - // - // - For VA_FOURCC_NV12, the format of the returned NativePixmapDmaBuf is - // gfx::BufferFormat::YUV_420_BIPLANAR. - // - // Returns nullptr on failure, or if the exported surface can't contain - // |va_surface_size|. - std::unique_ptr - ExportVASurfaceAsNativePixmapDmaBufUnwrapped( - VASurfaceID va_surface_id, - const gfx::Size& va_surface_size); - - // Carries out the vaBeginPicture()-vaRenderPicture()-vaEndPicture() on target - // |va_surface_id|. Returns false if any of these calls fails. - [[nodiscard]] bool Execute_Locked(VASurfaceID va_surface_id, - const std::vector& va_buffers) - EXCLUSIVE_LOCKS_REQUIRED(va_lock_); - - virtual void DestroyPendingBuffers_Locked() - EXCLUSIVE_LOCKS_REQUIRED(va_lock_); - - // Requests libva to allocate a new VABufferID of type |va_buffer.type|, then - // maps-and-copies |va_buffer.size| contents of |va_buffer.data| to it. If a - // failure occurs, calls DestroyPendingBuffers_Locked() and returns false. - [[nodiscard]] virtual bool SubmitBuffer_Locked( - const VABufferDescriptor& va_buffer) EXCLUSIVE_LOCKS_REQUIRED(va_lock_); - - // Maps |va_buffer_id| and, if successful, copies the contents of |va_buffer| - // into it. - [[nodiscard]] bool MapAndCopy_Locked(VABufferID va_buffer_id, - const VABufferDescriptor& va_buffer) - EXCLUSIVE_LOCKS_REQUIRED(va_lock_); - - // Queries whether |va_profile_| and |va_entrypoint_| support encoding quality - // setting and, if available, configures it to its maximum value, for lower - // consumption and maximum speed. - void MaybeSetLowQualityEncoding_Locked() EXCLUSIVE_LOCKS_REQUIRED(va_lock_); - - // If a protected session is active, attaches it to the decoding context. - [[nodiscard]] bool MaybeAttachProtectedSession_Locked() - EXCLUSIVE_LOCKS_REQUIRED(va_lock_); - - const CodecMode mode_; - const bool enforce_sequence_affinity_; - base::SequenceCheckerImpl sequence_checker_; - - // If using global VA lock, this is a pointer to VADisplayState's member - // |va_lock_|. Guaranteed to be valid for the lifetime of VaapiWrapper. - raw_ptr va_lock_; - - // VA handles. - // All valid after successful Initialize() and until Deinitialize(). - VADisplay va_display_ GUARDED_BY(va_lock_); - VAConfigID va_config_id_{VA_INVALID_ID}; - // Created in CreateContext() or CreateContextAndSurfaces() and valid until - // DestroyContext() or DestroyContextAndSurfaces(). - VAContextID va_context_id_{VA_INVALID_ID}; - - // Profile and entrypoint configured for the corresponding |va_context_id_|. - VAProfile va_profile_; - VAEntrypoint va_entrypoint_; - - // Data queued up for HW codec, to be committed on next execution. - // TODO(b/166646505): let callers manage the lifetime of these buffers. - std::vector pending_va_buffers_; - - // VA buffer to be used for kVideoProcess. Allocated the first time around, - // and reused afterwards. - std::unique_ptr va_buffer_for_vpp_; - -#if BUILDFLAG(IS_CHROMEOS_ASH) - // For protected decode mode. - VAConfigID va_protected_config_id_{VA_INVALID_ID}; - VAProtectedSessionID va_protected_session_id_{VA_INVALID_ID}; -#endif - - // Called to report codec errors to UMA. Errors to clients are reported via - // return values from public methods. - ReportErrorToUMACB report_error_to_uma_cb_; -}; - -} // namespace media - -#endif // MEDIA_GPU_VAAPI_VAAPI_WRAPPER_H_