| // Copyright 2018 The Cobalt Authors. 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/player/filter/player_components.h" |
| |
| #include <wrl/client.h> |
| |
| #include <functional> |
| |
| #include "starboard/common/device_type.h" |
| #include "starboard/common/log.h" |
| #include "starboard/common/ref_counted.h" |
| #include "starboard/common/scoped_ptr.h" |
| #include "starboard/common/system_property.h" |
| #include "starboard/shared/opus/opus_audio_decoder.h" |
| #include "starboard/shared/starboard/media/media_support_internal.h" |
| #include "starboard/shared/starboard/media/media_util.h" |
| #include "starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.h" |
| #include "starboard/shared/starboard/player/filter/audio_decoder_internal.h" |
| #include "starboard/shared/starboard/player/filter/audio_renderer_internal_pcm.h" |
| #include "starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h" |
| #include "starboard/shared/starboard/player/filter/media_time_provider_impl.h" |
| #include "starboard/shared/starboard/player/filter/video_decoder_internal.h" |
| #include "starboard/shared/starboard/player/filter/video_render_algorithm.h" |
| #include "starboard/shared/starboard/player/filter/video_render_algorithm_impl.h" |
| #include "starboard/shared/starboard/player/filter/video_renderer_internal_impl.h" |
| #include "starboard/shared/starboard/player/filter/video_renderer_sink.h" |
| #include "starboard/shared/uwp/application_uwp.h" |
| #include "starboard/shared/uwp/audio_renderer_passthrough.h" |
| #include "starboard/shared/uwp/extended_resources_manager.h" |
| #include "starboard/shared/win32/audio_decoder.h" |
| #include "starboard/xb1/shared/video_decoder_uwp.h" |
| |
| #if defined(INTERNAL_BUILD) |
| #include "internal/starboard/xb1/dav1d_video_decoder.h" |
| #include "internal/starboard/xb1/vpx_video_decoder.h" |
| #endif // defined(INTERNAL_BUILD) |
| |
| namespace starboard { |
| namespace shared { |
| namespace starboard { |
| namespace player { |
| namespace filter { |
| |
| namespace { |
| |
| using ::starboard::shared::uwp::AudioRendererPassthrough; |
| |
| double GetRefreshRate() { |
| return static_cast<double>(uwp::ApplicationUwp::Get()->GetRefreshRate()); |
| } |
| |
| bool IsHdrVideo(const media::VideoStreamInfo& video_stream_info) { |
| const auto& mime = video_stream_info.mime; |
| const auto& primaries = video_stream_info.color_metadata.primaries; |
| return mime.find("codecs=\"vp9.2") != mime.npos || |
| mime.find("codecs=\"vp09.02") != mime.npos || |
| primaries == kSbMediaPrimaryIdBt2020; |
| } |
| |
| class PlayerComponentsPassthrough |
| : public ::starboard::shared::starboard::player::filter::PlayerComponents { |
| public: |
| PlayerComponentsPassthrough( |
| scoped_ptr<AudioRendererPassthrough> audio_renderer, |
| scoped_ptr<VideoRenderer> video_renderer) |
| : audio_renderer_(audio_renderer.Pass()), |
| video_renderer_(video_renderer.Pass()) {} |
| |
| private: |
| // PlayerComponents methods |
| MediaTimeProvider* GetMediaTimeProvider() override { |
| return audio_renderer_.get(); |
| } |
| AudioRenderer* GetAudioRenderer() override { return audio_renderer_.get(); } |
| VideoRenderer* GetVideoRenderer() override { return video_renderer_.get(); } |
| |
| scoped_ptr<AudioRendererPassthrough> audio_renderer_; |
| scoped_ptr<VideoRenderer> video_renderer_; |
| }; |
| |
| class PlayerComponentsFactory : public PlayerComponents::Factory { |
| using AudioRendererPassthrough = |
| ::starboard::shared::uwp::AudioRendererPassthrough; |
| scoped_ptr<PlayerComponents> CreateComponents( |
| const CreationParameters& creation_parameters, |
| std::string* error_message) override { |
| SB_DCHECK(creation_parameters.audio_codec() != kSbMediaAudioCodecNone || |
| creation_parameters.video_codec() != kSbMediaVideoCodecNone); |
| SB_DCHECK(error_message); |
| |
| if (creation_parameters.audio_codec() != kSbMediaAudioCodecAc3 && |
| creation_parameters.audio_codec() != kSbMediaAudioCodecEac3) { |
| SB_LOG(INFO) << "Creating non pass-through components."; |
| return PlayerComponents::Factory::CreateComponents(creation_parameters, |
| error_message); |
| } |
| |
| SB_LOG(INFO) << "Creating pass-through components."; |
| |
| scoped_ptr<AudioDecoder> audio_decoder; |
| scoped_ptr<AudioRendererPassthrough> audio_renderer; |
| scoped_ptr<AudioRendererSink> audio_renderer_sink; |
| scoped_ptr<VideoDecoder> video_decoder; |
| scoped_ptr<VideoRenderAlgorithm> video_render_algorithm; |
| scoped_refptr<VideoRendererSink> video_renderer_sink; |
| scoped_ptr<VideoRendererImpl> video_renderer; |
| |
| if (!CreateSubComponents(creation_parameters, &audio_decoder, |
| &audio_renderer_sink, &video_decoder, |
| &video_render_algorithm, &video_renderer_sink, |
| error_message)) { |
| return scoped_ptr<PlayerComponents>(); |
| } |
| audio_renderer = |
| scoped_ptr<AudioRendererPassthrough>(new AudioRendererPassthrough( |
| audio_decoder.Pass(), creation_parameters.audio_stream_info())); |
| if (creation_parameters.video_codec() != kSbMediaVideoCodecNone) { |
| SB_DCHECK(video_decoder); |
| SB_DCHECK(video_render_algorithm); |
| |
| MediaTimeProvider* media_time_provider = audio_renderer.get(); |
| video_renderer.reset(new VideoRendererImpl( |
| video_decoder.Pass(), media_time_provider, |
| video_render_algorithm.Pass(), video_renderer_sink)); |
| } |
| |
| return scoped_ptr<PlayerComponents>(new PlayerComponentsPassthrough( |
| audio_renderer.Pass(), video_renderer.Pass())); |
| } |
| |
| bool CreateSubComponents( |
| const CreationParameters& creation_parameters, |
| scoped_ptr<AudioDecoder>* audio_decoder, |
| scoped_ptr<AudioRendererSink>* audio_renderer_sink, |
| scoped_ptr<VideoDecoder>* video_decoder, |
| scoped_ptr<VideoRenderAlgorithm>* video_render_algorithm, |
| scoped_refptr<VideoRendererSink>* video_renderer_sink, |
| std::string* error_message) override { |
| SB_DCHECK(error_message); |
| |
| if (creation_parameters.audio_codec() != kSbMediaAudioCodecNone) { |
| SB_DCHECK(audio_decoder); |
| SB_DCHECK(audio_renderer_sink); |
| |
| auto decoder_creator = [](const media::AudioStreamInfo& audio_stream_info, |
| SbDrmSystem drm_system) { |
| using AacAudioDecoder = ::starboard::shared::win32::AudioDecoder; |
| using OpusAudioDecoder = ::starboard::shared::opus::OpusAudioDecoder; |
| |
| if (audio_stream_info.codec == kSbMediaAudioCodecAac) { |
| return scoped_ptr<AudioDecoder>( |
| new AacAudioDecoder(audio_stream_info, drm_system)); |
| } else if (audio_stream_info.codec == kSbMediaAudioCodecOpus) { |
| scoped_ptr<OpusAudioDecoder> audio_decoder_impl( |
| new OpusAudioDecoder(audio_stream_info)); |
| if (audio_decoder_impl->is_valid()) { |
| return audio_decoder_impl.PassAs<AudioDecoder>(); |
| } |
| } else { |
| SB_NOTREACHED(); |
| } |
| return scoped_ptr<AudioDecoder>(); |
| }; |
| |
| auto audio_codec = creation_parameters.audio_stream_info().codec; |
| if (audio_codec != kSbMediaAudioCodecAc3 && |
| audio_codec != kSbMediaAudioCodecEac3) { |
| audio_decoder->reset(new AdaptiveAudioDecoder( |
| creation_parameters.audio_stream_info(), |
| creation_parameters.drm_system(), decoder_creator)); |
| } else { |
| // Use win32::AudioDecoder to decrypt and reformat the bitstream for the |
| // passthrough AudioRenderer. |
| audio_decoder->reset(new ::starboard::shared::win32::AudioDecoder( |
| creation_parameters.audio_stream_info(), |
| creation_parameters.drm_system())); |
| } |
| audio_renderer_sink->reset(new AudioRendererSinkImpl); |
| } |
| |
| const auto video_codec = creation_parameters.video_codec(); |
| if (video_codec == kSbMediaVideoCodecNone) { |
| return true; |
| } |
| |
| using MftVideoDecoder = ::starboard::xb1::shared::VideoDecoderUwp; |
| |
| const auto output_mode = creation_parameters.output_mode(); |
| const auto is_hdr_video = |
| IsHdrVideo(creation_parameters.video_stream_info()); |
| |
| if (video_codec == kSbMediaVideoCodecH264 || |
| (video_codec == kSbMediaVideoCodecVp9 && |
| MftVideoDecoder::IsHardwareVp9DecoderSupported()) || |
| (video_codec == kSbMediaVideoCodecAv1 && |
| MftVideoDecoder::IsHardwareAv1DecoderSupported())) { |
| video_render_algorithm->reset( |
| new VideoRenderAlgorithmImpl(std::bind(GetRefreshRate))); |
| video_decoder->reset(new MftVideoDecoder( |
| video_codec, output_mode, |
| creation_parameters.decode_target_graphics_context_provider(), |
| creation_parameters.drm_system())); |
| return true; |
| } |
| |
| #if !SB_HAS(GPU_DECODERS_ON_DESKTOP) |
| #if SB_API_VERSION < 15 |
| if (SbSystemGetDeviceType() == kSbSystemDeviceTypeDesktopPC) { |
| SB_LOG(WARNING) << "GPU decoder disabled on Desktop."; |
| return false; |
| } |
| #else |
| if (GetSystemPropertyString(kSbSystemPropertyDeviceType) == |
| kSystemDeviceTypeDesktopPC) { |
| SB_LOG(WARNING) << "GPU decoder disabled on Desktop."; |
| return false; |
| } |
| #endif |
| #endif // !SB_HAS(GPU_DECODERS_ON_DESKTOP) |
| if (video_codec != kSbMediaVideoCodecVp9 && |
| video_codec != kSbMediaVideoCodecAv1) { |
| return false; |
| } |
| SB_DCHECK(output_mode == kSbPlayerOutputModeDecodeToTexture); |
| |
| Microsoft::WRL::ComPtr<ID3D12Device> d3d12device; |
| Microsoft::WRL::ComPtr<ID3D12Heap> d3d12buffer_heap; |
| void* d3d12queue = nullptr; |
| if (!uwp::ExtendedResourcesManager::GetInstance()->GetD3D12Objects( |
| &d3d12device, &d3d12buffer_heap, &d3d12queue)) { |
| // Somehow extended resources get lost. Returns directly to trigger an |
| // error to the player. |
| *error_message = |
| "Failed to obtain D3D12Device and/or D3D12queue required for " |
| "instantiating GPU based decoders."; |
| SB_LOG(ERROR) << *error_message; |
| return false; |
| } |
| SB_DCHECK(d3d12device); |
| SB_DCHECK(d3d12buffer_heap); |
| SB_DCHECK(d3d12queue); |
| |
| #if defined(INTERNAL_BUILD) |
| using GpuVp9VideoDecoder = ::starboard::xb1::shared::VpxVideoDecoder; |
| using GpuAv1VideoDecoder = ::starboard::xb1::shared::Dav1dVideoDecoder; |
| |
| if (video_codec == kSbMediaVideoCodecVp9) { |
| video_decoder->reset(new GpuVp9VideoDecoder( |
| creation_parameters.decode_target_graphics_context_provider(), |
| creation_parameters.video_stream_info(), is_hdr_video, d3d12device, |
| d3d12buffer_heap, d3d12queue)); |
| } |
| |
| if (video_codec == kSbMediaVideoCodecAv1) { |
| video_decoder->reset(new GpuAv1VideoDecoder( |
| creation_parameters.decode_target_graphics_context_provider(), |
| creation_parameters.video_stream_info(), is_hdr_video, d3d12device, |
| d3d12buffer_heap, d3d12queue)); |
| } |
| #endif // defined(INTERNAL_BUILD) |
| |
| if (video_decoder) { |
| video_render_algorithm->reset( |
| new VideoRenderAlgorithmImpl(std::bind(GetRefreshRate))); |
| return true; |
| } |
| |
| *error_message = FormatString( |
| "Unsupported video codec %d or insufficient resources to its creation.", |
| video_codec); |
| SB_LOG(ERROR) << *error_message; |
| return false; |
| } |
| }; |
| |
| } // namespace |
| |
| // static |
| scoped_ptr<PlayerComponents::Factory> PlayerComponents::Factory::Create() { |
| return make_scoped_ptr<PlayerComponents::Factory>( |
| new PlayerComponentsFactory); |
| } |
| |
| // static |
| bool PlayerComponents::Factory::OutputModeSupported( |
| SbPlayerOutputMode output_mode, |
| SbMediaVideoCodec codec, |
| SbDrmSystem drm_system) { |
| return output_mode == kSbPlayerOutputModeDecodeToTexture; |
| } |
| |
| } // namespace filter |
| } // namespace player |
| } // namespace starboard |
| } // namespace shared |
| } // namespace starboard |