123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428 |
- // Copyright 2019 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/renderers/win/media_foundation_video_stream.h"
- #include <initguid.h> // NOLINT(build/include_order)
- #include <mfapi.h> // NOLINT(build/include_order)
- #include <mferror.h> // NOLINT(build/include_order)
- #include <wrl.h> // NOLINT(build/include_order)
- #include "base/metrics/histogram_functions.h"
- #include "base/numerics/safe_conversions.h"
- #include "media/base/video_codecs.h"
- #include "media/base/video_decoder_config.h"
- #include "media/base/win/mf_helpers.h"
- #include "media/media_buildflags.h"
- namespace media {
- using Microsoft::WRL::ComPtr;
- using Microsoft::WRL::MakeAndInitialize;
- namespace {
- // This is supported by Media Foundation.
- DEFINE_MEDIATYPE_GUID(MFVideoFormat_THEORA, FCC('theo'))
- // MF_MT_MIN_MASTERING_LUMINANCE values are in 1/10000th of a nit (0.0001 nit).
- // https://docs.microsoft.com/en-us/windows/win32/api/dxgi1_5/ns-dxgi1_5-dxgi_hdr_metadata_hdr10
- constexpr int kMasteringDispLuminanceScale = 10000;
- GUID VideoCodecToMFSubtype(VideoCodec codec, VideoCodecProfile profile) {
- switch (codec) {
- case VideoCodec::kH264:
- return MFVideoFormat_H264;
- case VideoCodec::kVC1:
- return MFVideoFormat_WVC1;
- case VideoCodec::kMPEG2:
- return MFVideoFormat_MPEG2;
- case VideoCodec::kMPEG4:
- return MFVideoFormat_MP4V;
- case VideoCodec::kTheora:
- return MFVideoFormat_THEORA;
- case VideoCodec::kVP8:
- return MFVideoFormat_VP80;
- case VideoCodec::kVP9:
- return MFVideoFormat_VP90;
- case VideoCodec::kHEVC:
- return MFVideoFormat_HEVC;
- case VideoCodec::kDolbyVision:
- if (profile == VideoCodecProfile::DOLBYVISION_PROFILE0 ||
- profile == VideoCodecProfile::DOLBYVISION_PROFILE9) {
- return MFVideoFormat_H264;
- } else {
- return MFVideoFormat_HEVC;
- }
- case VideoCodec::kAV1:
- return MFVideoFormat_AV1;
- default:
- return GUID_NULL;
- }
- }
- MFVideoRotationFormat VideoRotationToMF(VideoRotation rotation) {
- DVLOG(2) << __func__ << ": rotation=" << rotation;
- switch (rotation) {
- case VideoRotation::VIDEO_ROTATION_0:
- return MFVideoRotationFormat_0;
- case VideoRotation::VIDEO_ROTATION_90:
- return MFVideoRotationFormat_90;
- case VideoRotation::VIDEO_ROTATION_180:
- return MFVideoRotationFormat_180;
- case VideoRotation::VIDEO_ROTATION_270:
- return MFVideoRotationFormat_270;
- }
- }
- MFVideoPrimaries VideoPrimariesToMF(
- media::VideoColorSpace::PrimaryID primary_id) {
- DVLOG(2) << __func__ << ": primary_id=" << static_cast<int>(primary_id);
- switch (primary_id) {
- case VideoColorSpace::PrimaryID::INVALID:
- return MFVideoPrimaries_Unknown;
- case VideoColorSpace::PrimaryID::BT709:
- return MFVideoPrimaries_BT709;
- case VideoColorSpace::PrimaryID::UNSPECIFIED:
- return MFVideoPrimaries_Unknown;
- case VideoColorSpace::PrimaryID::BT470M:
- return MFVideoPrimaries_BT470_2_SysM;
- case VideoColorSpace::PrimaryID::BT470BG:
- return MFVideoPrimaries_BT470_2_SysBG;
- case VideoColorSpace::PrimaryID::SMPTE170M:
- return MFVideoPrimaries_SMPTE170M;
- case VideoColorSpace::PrimaryID::SMPTE240M:
- return MFVideoPrimaries_SMPTE240M;
- case VideoColorSpace::PrimaryID::FILM:
- return MFVideoPrimaries_Unknown;
- case VideoColorSpace::PrimaryID::BT2020:
- return MFVideoPrimaries_BT2020;
- case VideoColorSpace::PrimaryID::SMPTEST428_1:
- return MFVideoPrimaries_Unknown;
- case VideoColorSpace::PrimaryID::SMPTEST431_2:
- return MFVideoPrimaries_Unknown;
- case VideoColorSpace::PrimaryID::SMPTEST432_1:
- return MFVideoPrimaries_Unknown;
- case VideoColorSpace::PrimaryID::EBU_3213_E:
- return MFVideoPrimaries_EBU3213;
- default:
- DLOG(ERROR) << "VideoPrimariesToMF failed due to invalid Video Primary.";
- }
- return MFVideoPrimaries_Unknown;
- }
- MFVideoTransferFunction VideoTransferFunctionToMF(
- media::VideoColorSpace::TransferID transfer_id) {
- DVLOG(2) << __func__ << ": transfer_id=" << static_cast<int>(transfer_id);
- switch (transfer_id) {
- case VideoColorSpace::TransferID::INVALID:
- return MFVideoTransFunc_Unknown;
- case VideoColorSpace::TransferID::BT709:
- return MFVideoTransFunc_709;
- case VideoColorSpace::TransferID::UNSPECIFIED:
- return MFVideoTransFunc_Unknown;
- case VideoColorSpace::TransferID::GAMMA22:
- return MFVideoTransFunc_22;
- case VideoColorSpace::TransferID::GAMMA28:
- return MFVideoTransFunc_28;
- case VideoColorSpace::TransferID::SMPTE170M:
- return MFVideoTransFunc_709;
- case VideoColorSpace::TransferID::SMPTE240M:
- return MFVideoTransFunc_240M;
- case VideoColorSpace::TransferID::LINEAR:
- return MFVideoTransFunc_10;
- case VideoColorSpace::TransferID::LOG:
- return MFVideoTransFunc_Log_100;
- case VideoColorSpace::TransferID::LOG_SQRT:
- return MFVideoTransFunc_Unknown;
- case VideoColorSpace::TransferID::IEC61966_2_4:
- return MFVideoTransFunc_Unknown;
- case VideoColorSpace::TransferID::BT1361_ECG:
- return MFVideoTransFunc_Unknown;
- case VideoColorSpace::TransferID::IEC61966_2_1:
- return MFVideoTransFunc_sRGB;
- case VideoColorSpace::TransferID::BT2020_10:
- return MFVideoTransFunc_2020;
- case VideoColorSpace::TransferID::BT2020_12:
- return MFVideoTransFunc_2020;
- case VideoColorSpace::TransferID::SMPTEST2084:
- return MFVideoTransFunc_2084;
- case VideoColorSpace::TransferID::SMPTEST428_1:
- return MFVideoTransFunc_Unknown;
- case VideoColorSpace::TransferID::ARIB_STD_B67:
- return MFVideoTransFunc_HLG;
- default:
- DLOG(ERROR) << "VideoTransferFunctionToMF failed due to invalid Transfer "
- "Function.";
- }
- return MFVideoTransFunc_Unknown;
- }
- MT_CUSTOM_VIDEO_PRIMARIES CustomVideoPrimaryToMF(
- gfx::ColorVolumeMetadata color_volume_metadata) {
- // MT_CUSTOM_VIDEO_PRIMARIES stores value in float no scaling factor needed
- // https://docs.microsoft.com/en-us/windows/win32/api/mfapi/ns-mfapi-mt_custom_video_primaries
- MT_CUSTOM_VIDEO_PRIMARIES primaries = {0};
- primaries.fRx = color_volume_metadata.primary_r.x();
- primaries.fRy = color_volume_metadata.primary_r.y();
- primaries.fGx = color_volume_metadata.primary_g.x();
- primaries.fGy = color_volume_metadata.primary_g.y();
- primaries.fBx = color_volume_metadata.primary_b.x();
- primaries.fBy = color_volume_metadata.primary_b.y();
- primaries.fWx = color_volume_metadata.white_point.x();
- primaries.fWy = color_volume_metadata.white_point.y();
- return primaries;
- }
- #if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
- // To MediaFoundation, DolbyVision renderer profile strings are always 7
- // characters. For HEVC based profiles, it's in the format "dvhe.xx". For AVC
- // based profiles, it's in the format "dvav.xx". In both cases, "xx" is the
- // two-digit profile number. Note the DolbyVision level is ignored.
- bool GetDolbyVisionConfigurations(
- VideoCodecProfile profile,
- std::wstring& dolby_vision_profile,
- unsigned& dolby_vision_configuration_nalu_type) {
- DVLOG(2) << __func__ << ": profile=" << profile;
- switch (profile) {
- case VideoCodecProfile::DOLBYVISION_PROFILE0:
- DLOG(ERROR) << __func__ << ": Profile 0 unsupported by Media Foundation";
- return false;
- case VideoCodecProfile::DOLBYVISION_PROFILE4:
- dolby_vision_profile = L"dvhe.04";
- dolby_vision_configuration_nalu_type = 62;
- break;
- case VideoCodecProfile::DOLBYVISION_PROFILE5:
- dolby_vision_profile = L"dvhe.05";
- dolby_vision_configuration_nalu_type = 62;
- break;
- case VideoCodecProfile::DOLBYVISION_PROFILE7:
- dolby_vision_profile = L"dvhe.07";
- dolby_vision_configuration_nalu_type = 62;
- break;
- case VideoCodecProfile::DOLBYVISION_PROFILE8:
- dolby_vision_profile = L"dvhe.08";
- dolby_vision_configuration_nalu_type = 62;
- break;
- case VideoCodecProfile::DOLBYVISION_PROFILE9:
- dolby_vision_profile = L"dvav.09";
- dolby_vision_configuration_nalu_type = 28;
- break;
- default:
- DLOG(ERROR) << __func__ << ": Invalid profile (" << profile << ")";
- return false;
- }
- return true;
- }
- #endif // BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
- // https://docs.microsoft.com/en-us/windows/win32/api/mfobjects/ns-mfobjects-mfoffset
- // The value of the MFOffset number is value + (fract / 65536.0f).
- MFOffset MakeOffset(float value) {
- MFOffset offset;
- offset.value = base::checked_cast<short>(value);
- offset.fract = base::checked_cast<WORD>(65536 * (value - offset.value));
- return offset;
- }
- // TODO(frankli): investigate if VideoCodecProfile is needed by MF.
- HRESULT GetVideoType(const VideoDecoderConfig& config,
- IMFMediaType** media_type_out) {
- ComPtr<IMFMediaType> media_type;
- RETURN_IF_FAILED(MFCreateMediaType(&media_type));
- RETURN_IF_FAILED(media_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
- GUID mf_subtype = VideoCodecToMFSubtype(config.codec(), config.profile());
- if (mf_subtype == GUID_NULL) {
- DLOG(ERROR) << "Unsupported codec type: " << config.codec();
- return MF_E_TOPO_CODEC_NOT_FOUND;
- }
- RETURN_IF_FAILED(media_type->SetGUID(MF_MT_SUBTYPE, mf_subtype));
- UINT32 width = config.visible_rect().width();
- UINT32 height = config.visible_rect().height();
- RETURN_IF_FAILED(
- MFSetAttributeSize(media_type.Get(), MF_MT_FRAME_SIZE, width, height));
- UINT32 natural_width = config.natural_size().width();
- UINT32 natural_height = config.natural_size().height();
- RETURN_IF_FAILED(
- MFSetAttributeRatio(media_type.Get(), MF_MT_PIXEL_ASPECT_RATIO,
- height * natural_width, width * natural_height));
- MFVideoArea area;
- area.OffsetX = MakeOffset(static_cast<float>(config.visible_rect().x()));
- area.OffsetY = MakeOffset(static_cast<float>(config.visible_rect().y()));
- area.Area = config.natural_size().ToSIZE();
- RETURN_IF_FAILED(media_type->SetBlob(MF_MT_GEOMETRIC_APERTURE, (UINT8*)&area,
- sizeof(area)));
- if (config.video_transformation().rotation !=
- VideoRotation::VIDEO_ROTATION_0) {
- MFVideoRotationFormat mf_rotation =
- VideoRotationToMF(config.video_transformation().rotation);
- RETURN_IF_FAILED(media_type->SetUINT32(MF_MT_VIDEO_ROTATION, mf_rotation));
- }
- MFVideoTransferFunction mf_transfer_function =
- VideoTransferFunctionToMF(config.color_space_info().transfer);
- RETURN_IF_FAILED(
- media_type->SetUINT32(MF_MT_TRANSFER_FUNCTION, mf_transfer_function));
- MFVideoPrimaries mf_video_primary =
- VideoPrimariesToMF(config.color_space_info().primaries);
- RETURN_IF_FAILED(
- media_type->SetUINT32(MF_MT_VIDEO_PRIMARIES, mf_video_primary));
- UINT32 video_nominal_range =
- config.color_space_info().range == gfx::ColorSpace::RangeID::FULL
- ? MFNominalRange_0_255
- : MFNominalRange_16_235;
- RETURN_IF_FAILED(
- media_type->SetUINT32(MF_MT_VIDEO_NOMINAL_RANGE, video_nominal_range));
- if (config.hdr_metadata().has_value()) {
- UINT32 max_display_mastering_luminance =
- config.hdr_metadata()->color_volume_metadata.luminance_max;
- RETURN_IF_FAILED(media_type->SetUINT32(MF_MT_MAX_MASTERING_LUMINANCE,
- max_display_mastering_luminance));
- UINT32 min_display_mastering_luminance =
- config.hdr_metadata()->color_volume_metadata.luminance_min *
- kMasteringDispLuminanceScale;
- RETURN_IF_FAILED(media_type->SetUINT32(MF_MT_MIN_MASTERING_LUMINANCE,
- min_display_mastering_luminance));
- MT_CUSTOM_VIDEO_PRIMARIES primaries =
- CustomVideoPrimaryToMF(config.hdr_metadata()->color_volume_metadata);
- RETURN_IF_FAILED(media_type->SetBlob(MF_MT_CUSTOM_VIDEO_PRIMARIES,
- reinterpret_cast<UINT8*>(&primaries),
- sizeof(MT_CUSTOM_VIDEO_PRIMARIES)));
- }
- base::UmaHistogramEnumeration(
- "Media.MediaFoundation.VideoColorSpace.TransferID",
- config.color_space_info().transfer);
- base::UmaHistogramEnumeration(
- "Media.MediaFoundation.VideoColorSpace.PrimaryID",
- config.color_space_info().primaries);
- #if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
- if (config.codec() == VideoCodec::kDolbyVision) {
- std::wstring dolby_vision_profile;
- // This value should be 28 for AVC and 62 for HEVC.
- unsigned dolby_vision_configuration_nalu_type = 0;
- if (!GetDolbyVisionConfigurations(config.profile(), dolby_vision_profile,
- dolby_vision_configuration_nalu_type)) {
- return MF_E_TOPO_CODEC_NOT_FOUND;
- }
- media_type->SetString(MF_MT_VIDEO_RENDERER_EXTENSION_PROFILE,
- dolby_vision_profile.c_str());
- media_type->SetUINT32(MF_MT_FORWARD_CUSTOM_NALU,
- dolby_vision_configuration_nalu_type);
- media_type->SetUINT32(MF_DECODER_FWD_CUSTOM_SEI_DECODE_ORDER, TRUE);
- }
- #endif // BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
- *media_type_out = media_type.Detach();
- return S_OK;
- }
- } // namespace
- /*static*/
- HRESULT MediaFoundationVideoStream::Create(
- int stream_id,
- IMFMediaSource* parent_source,
- DemuxerStream* demuxer_stream,
- std::unique_ptr<MediaLog> media_log,
- MediaFoundationStreamWrapper** stream_out) {
- DVLOG(1) << __func__ << ": stream_id=" << stream_id;
- ComPtr<IMFMediaStream> video_stream;
- VideoCodec codec = demuxer_stream->video_decoder_config().codec();
- switch (codec) {
- #if BUILDFLAG(USE_PROPRIETARY_CODECS)
- case VideoCodec::kH264:
- RETURN_IF_FAILED(MakeAndInitialize<MediaFoundationH264VideoStream>(
- &video_stream, stream_id, parent_source, demuxer_stream,
- std::move(media_log)));
- break;
- #endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
- #if BUILDFLAG(ENABLE_PLATFORM_HEVC)
- case VideoCodec::kHEVC:
- #endif
- #if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
- case VideoCodec::kDolbyVision:
- #endif
- #if BUILDFLAG(ENABLE_PLATFORM_HEVC) || BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
- RETURN_IF_FAILED(MakeAndInitialize<MediaFoundationHEVCVideoStream>(
- &video_stream, stream_id, parent_source, demuxer_stream,
- std::move(media_log)));
- break;
- #endif // BUILDFLAG(ENABLE_PLATFORM_HEVC) ||
- // BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
- default:
- RETURN_IF_FAILED(MakeAndInitialize<MediaFoundationVideoStream>(
- &video_stream, stream_id, parent_source, demuxer_stream,
- std::move(media_log)));
- break;
- }
- *stream_out =
- static_cast<MediaFoundationStreamWrapper*>(video_stream.Detach());
- return S_OK;
- }
- bool MediaFoundationVideoStream::IsEncrypted() const {
- VideoDecoderConfig decoder_config = demuxer_stream_->video_decoder_config();
- return decoder_config.is_encrypted();
- }
- HRESULT MediaFoundationVideoStream::GetMediaType(
- IMFMediaType** media_type_out) {
- VideoDecoderConfig decoder_config = demuxer_stream_->video_decoder_config();
- return GetVideoType(decoder_config, media_type_out);
- }
- #if BUILDFLAG(USE_PROPRIETARY_CODECS)
- HRESULT MediaFoundationH264VideoStream::GetMediaType(
- IMFMediaType** media_type_out) {
- VideoDecoderConfig decoder_config = demuxer_stream_->video_decoder_config();
- RETURN_IF_FAILED(GetVideoType(decoder_config, media_type_out));
- // Enable conversion to Annex-B
- demuxer_stream_->EnableBitstreamConverter();
- return S_OK;
- }
- bool MediaFoundationH264VideoStream::AreFormatChangesEnabled() {
- // Disable explicit format change event for H264 to allow switching to the
- // new stream without a full re-create, which will be much faster. This is
- // also due to the fact that the MFT decoder can handle some format changes
- // without a format change event. For format changes that the MFT decoder
- // cannot support (e.g. codec change), the playback will fail later with
- // MF_E_INVALIDMEDIATYPE (0xC00D36B4).
- return false;
- }
- #endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
- #if BUILDFLAG(ENABLE_PLATFORM_HEVC)
- bool MediaFoundationHEVCVideoStream::AreFormatChangesEnabled() {
- // Disable explicit format change event for HEVC to allow switching to the
- // new stream without a full re-create, which will be much faster. This is
- // also due to the fact that the MFT decoder can handle some format changes
- // without a format change event. For format changes that the MFT decoder
- // cannot support (e.g. codec change), the playback will fail later with
- // MF_E_INVALIDMEDIATYPE (0xC00D36B4).
- return false;
- }
- #endif // BUILDFLAG(ENABLE_PLATFORM_HEVC)
- } // namespace media
|