blob: 3be4eabea458256c7a57067cd610994c5cc1a9ab [file] [log] [blame]
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "starboard/shared/starboard/media/media_support_internal.h"
#include <d3d11.h>
#include <d3d12.h>
#include <mfapi.h>
#include <mfidl.h>
#include <wrl/client.h>
#include "starboard/shared/uwp/application_uwp.h"
#include "third_party/libvpx_xb1/libvpx/vpx/vp8dx.h"
#include "third_party/libvpx_xb1/libvpx/vpx/vpx_decoder.h"
namespace {
using ::starboard::shared::uwp::ApplicationUwp;
const int kMaxVpxGpuDecodeTargetWidth = 3840;
const int kMaxVpxGpuDecodeTargetHeight = 2160;
const int kMaxVpxGpuDecoderCpuCoreUse = 4;
} // namespace
#if SB_HAS(MEDIA_WEBM_VP9_SUPPORT)
namespace {
// Cache the VP9 support status since the check may be expensive.
enum Vp9Support { kVp9SupportUnknown, kVp9SupportYes, kVp9SupportNo };
Vp9Support s_vp9_hw_support = kVp9SupportUnknown;
Vp9Support s_vp9_gpu_support = kVp9SupportUnknown;
} // namespace
// Check for VP9 support. Since this is used by a starboard function, it
// cannot depend on other modules (e.g. ANGLE).
SB_EXPORT bool IsVp9HwDecoderSupported() {
if (s_vp9_hw_support == kVp9SupportUnknown) {
// Try initializing the VP9 decoder to determine if it is supported.
HRESULT hr;
Microsoft::WRL::ComPtr<ID3D11Device> d3d_device;
hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0,
nullptr, 0, D3D11_SDK_VERSION,
d3d_device.GetAddressOf(), nullptr, nullptr);
UINT reset_token = 0;
Microsoft::WRL::ComPtr<IMFDXGIDeviceManager> device_manager;
if (SUCCEEDED(hr)) {
hr = MFCreateDXGIDeviceManager(&reset_token,
device_manager.GetAddressOf());
}
if (SUCCEEDED(hr)) {
hr = device_manager->ResetDevice(d3d_device.Get(), reset_token);
}
Microsoft::WRL::ComPtr<IMFTransform> transform;
if (SUCCEEDED(hr)) {
hr = CoCreateInstance(CLSID_MSVPxDecoder, nullptr, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(transform.GetAddressOf()));
}
if (SUCCEEDED(hr)) {
hr = transform->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER,
ULONG_PTR(device_manager.Get()));
}
s_vp9_hw_support = SUCCEEDED(hr) ? kVp9SupportYes : kVp9SupportNo;
}
return s_vp9_hw_support == kVp9SupportYes;
}
SB_EXPORT bool IsVp9GPUDecoderSupported() {
if (s_vp9_gpu_support == kVp9SupportUnknown) {
D3D12_COMMAND_QUEUE_DESC desc = {};
desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
Microsoft::WRL::ComPtr<ID3D12CommandQueue> d3d12queue;
ApplicationUwp* application_uwp = ApplicationUwp::Get();
SB_CHECK(application_uwp->GetD3D12Device());
HRESULT hr = application_uwp->GetD3D12Device()->CreateCommandQueue(
&desc, IID_PPV_ARGS(&d3d12queue));
if (FAILED(hr)) {
SB_LOG(WARNING)
<< "Could not create DX12 command queue: cannot use GPU VP9";
return false;
}
vpx_codec_ctx vpx_context = {};
vpx_codec_dec_cfg_t vpx_config = {};
vpx_config.w = kMaxVpxGpuDecodeTargetWidth;
vpx_config.h = kMaxVpxGpuDecodeTargetHeight;
vpx_config.threads = kMaxVpxGpuDecoderCpuCoreUse;
vpx_config.hw_device = application_uwp->GetD3D12Device().Get();
vpx_config.hw_command_queue = d3d12queue.Get();
vpx_codec_err_t status =
vpx_codec_dec_init(&vpx_context, vpx_codec_vp9_dx(), &vpx_config,
VPX_CODEC_USE_FRAME_THREADING);
s_vp9_gpu_support =
(status == VPX_CODEC_OK) ? kVp9SupportYes : kVp9SupportNo;
vpx_codec_destroy(&vpx_context);
}
return s_vp9_gpu_support == kVp9SupportYes;
}
#else // SB_HAS(MEDIA_WEBM_VP9_SUPPORT)
bool IsVp9HwDecoderSupported() {
return false;
}
bool IsVp9GPUDecoderSupported() {
return false;
}
#endif
SB_EXPORT bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec,
int frame_width,
int frame_height,
int64_t bitrate,
int fps) {
int max_width = 1920;
int max_height = 1080;
if (video_codec == kSbMediaVideoCodecVp9) {
// Vp9 supports 8k only in whitelisted platforms, up to 4k in the others.
#ifdef ENABLE_VP9_8K_SUPPORT
max_width = 7680;
max_height = 4320;
#else // ENABLE_VP9_8K_SUPPORT
max_width = 3840;
max_height = 2160;
#endif // ENABLE_VP9_8K_SUPPORT
} else if (video_codec == kSbMediaVideoCodecH264) {
// Not all devices can support 4k H264; some (e.g. xb1) may crash in
// the decoder if provided too high of a resolution. Therefore
// platforms must explicitly opt-in to support 4k H264.
#ifdef ENABLE_H264_4K_SUPPORT
max_width = 3840;
max_height = 2160;
#endif // ENABLE_H264_4K_SUPPORT
}
if (frame_width > max_width || frame_height > max_height) {
return false;
}
// Is bitrate in range?
if (bitrate > SB_MEDIA_MAX_VIDEO_BITRATE_IN_BITS_PER_SECOND) {
return false;
}
if (fps > 60) {
return false;
}
if (video_codec == kSbMediaVideoCodecH264) {
return true;
}
if (video_codec == kSbMediaVideoCodecVp9) {
if (IsVp9HwDecoderSupported())
return true;
if (IsVp9GPUDecoderSupported())
return true;
}
return false;
}