Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #define INITGUID | |
|
sandersd (OOO until July 31)
2016/12/02 00:52:57
Should this be undef'd later?
| |
| 6 #include "media/gpu/d3d11_video_decode_accelerator_win.h" | |
| 7 | |
| 8 #include <d3d11.h> | |
| 9 | |
| 10 #include "base/bits.h" | |
| 11 #include "base/memory/ptr_util.h" | |
| 12 #include "base/memory/shared_memory.h" | |
| 13 #include "base/threading/thread_task_runner_handle.h" | |
| 14 #include "base/trace_event/trace_event.h" | |
| 15 #include "base/win/scoped_comptr.h" | |
| 16 #include "media/gpu/d3d11_video_decoder.h" | |
| 17 #include "media/gpu/h264_decoder.h" | |
| 18 #include "media/gpu/h264_dpb.h" | |
| 19 #include "third_party/angle/include/EGL/egl.h" | |
| 20 #include "third_party/angle/include/EGL/eglext.h" | |
| 21 #include "ui/gfx/color_space.h" | |
| 22 #include "ui/gl/gl_bindings.h" | |
| 23 #include "ui/gl/gl_context.h" | |
| 24 #include "ui/gl/gl_surface_egl.h" | |
| 25 | |
| 26 namespace media { | |
| 27 | |
| 28 #define RETURN_ON_FAILURE(result, log, ret) \ | |
| 29 do { \ | |
| 30 if (!(result)) { \ | |
| 31 DLOG(ERROR) << log; \ | |
| 32 return ret; \ | |
| 33 } \ | |
| 34 } while (0) | |
| 35 | |
| 36 // Helper function to query the ANGLE device object. The template argument T | |
| 37 // identifies the device interface being queried. IDirect3DDevice9Ex for d3d9 | |
| 38 // and ID3D11Device for dx11. | |
| 39 template <class T> | |
| 40 base::win::ScopedComPtr<T> QueryDeviceObjectFromANGLE(int object_type) { | |
| 41 base::win::ScopedComPtr<T> device_object; | |
| 42 | |
| 43 EGLDisplay egl_display = nullptr; | |
| 44 intptr_t egl_device = 0; | |
| 45 intptr_t device = 0; | |
| 46 | |
| 47 { | |
| 48 TRACE_EVENT0("gpu", "QueryDeviceObjectFromANGLE. GetHardwareDisplay"); | |
| 49 egl_display = gl::GLSurfaceEGL::GetHardwareDisplay(); | |
| 50 } | |
| 51 | |
| 52 RETURN_ON_FAILURE(gl::GLSurfaceEGL::HasEGLExtension("EGL_EXT_device_query"), | |
| 53 "EGL_EXT_device_query missing", device_object); | |
| 54 | |
| 55 PFNEGLQUERYDISPLAYATTRIBEXTPROC QueryDisplayAttribEXT = nullptr; | |
| 56 | |
| 57 { | |
| 58 TRACE_EVENT0("gpu", "QueryDeviceObjectFromANGLE. eglGetProcAddress"); | |
| 59 | |
| 60 QueryDisplayAttribEXT = reinterpret_cast<PFNEGLQUERYDISPLAYATTRIBEXTPROC>( | |
| 61 eglGetProcAddress("eglQueryDisplayAttribEXT")); | |
| 62 | |
| 63 RETURN_ON_FAILURE( | |
| 64 QueryDisplayAttribEXT, | |
| 65 "Failed to get the eglQueryDisplayAttribEXT function from ANGLE", | |
| 66 device_object); | |
| 67 } | |
| 68 | |
| 69 PFNEGLQUERYDEVICEATTRIBEXTPROC QueryDeviceAttribEXT = nullptr; | |
| 70 | |
| 71 { | |
| 72 TRACE_EVENT0("gpu", "QueryDeviceObjectFromANGLE. eglGetProcAddress"); | |
| 73 | |
| 74 QueryDeviceAttribEXT = reinterpret_cast<PFNEGLQUERYDEVICEATTRIBEXTPROC>( | |
| 75 eglGetProcAddress("eglQueryDeviceAttribEXT")); | |
| 76 | |
| 77 RETURN_ON_FAILURE( | |
| 78 QueryDeviceAttribEXT, | |
| 79 "Failed to get the eglQueryDeviceAttribEXT function from ANGLE", | |
| 80 device_object); | |
| 81 } | |
| 82 | |
| 83 { | |
| 84 TRACE_EVENT0("gpu", "QueryDeviceObjectFromANGLE. QueryDisplayAttribEXT"); | |
| 85 | |
| 86 RETURN_ON_FAILURE( | |
| 87 QueryDisplayAttribEXT(egl_display, EGL_DEVICE_EXT, &egl_device), | |
| 88 "The eglQueryDisplayAttribEXT function failed to get the EGL device", | |
| 89 device_object); | |
| 90 } | |
| 91 | |
| 92 RETURN_ON_FAILURE(egl_device, "Failed to get the EGL device", device_object); | |
| 93 | |
| 94 { | |
| 95 TRACE_EVENT0("gpu", "QueryDeviceObjectFromANGLE. QueryDisplayAttribEXT"); | |
| 96 | |
| 97 RETURN_ON_FAILURE( | |
| 98 QueryDeviceAttribEXT(reinterpret_cast<EGLDeviceEXT>(egl_device), | |
| 99 object_type, &device), | |
| 100 "The eglQueryDeviceAttribEXT function failed to get the device", | |
| 101 device_object); | |
| 102 | |
| 103 RETURN_ON_FAILURE(device, "Failed to get the ANGLE device", device_object); | |
| 104 } | |
| 105 | |
| 106 device_object = reinterpret_cast<T*>(device); | |
| 107 return device_object; | |
| 108 } | |
| 109 | |
| 110 D3D11VideoDecodeAccelerator::D3D11VideoDecodeAccelerator( | |
| 111 const GetGLContextCallback& get_gl_context_cb, | |
| 112 const MakeGLContextCurrentCallback& make_context_current_cb) | |
| 113 : get_gl_context_cb_(get_gl_context_cb), | |
| 114 make_context_current_cb_(make_context_current_cb) {} | |
| 115 | |
| 116 D3D11VideoDecodeAccelerator::~D3D11VideoDecodeAccelerator() {} | |
| 117 | |
| 118 bool D3D11VideoDecodeAccelerator::Initialize(const Config& config, | |
| 119 Client* client) { | |
| 120 client_ = client; | |
| 121 make_context_current_cb_.Run(); | |
| 122 | |
| 123 device_ = QueryDeviceObjectFromANGLE<ID3D11Device>(EGL_D3D11_DEVICE_ANGLE); | |
| 124 device_->GetImmediateContext(device_context_.Receive()); | |
| 125 | |
| 126 HRESULT hr = device_context_.QueryInterface(video_context_.Receive()); | |
| 127 CHECK(SUCCEEDED(hr)); | |
| 128 | |
| 129 hr = device_.QueryInterface(video_device_.Receive()); | |
| 130 CHECK(SUCCEEDED(hr)); | |
| 131 | |
| 132 bool is_h264 = | |
| 133 config.profile >= H264PROFILE_MIN && config.profile <= H264PROFILE_MAX; | |
| 134 if (!is_h264) | |
| 135 return false; | |
| 136 | |
| 137 GUID needed_guid; | |
| 138 memcpy(&needed_guid, &D3D11_DECODER_PROFILE_H264_VLD_NOFGT, | |
| 139 sizeof(needed_guid)); | |
| 140 GUID decoder_guid = {}; | |
| 141 | |
| 142 { | |
| 143 // Enumerate supported video profiles and look for the H264 profile. | |
| 144 bool found = false; | |
| 145 UINT profile_count = video_device_->GetVideoDecoderProfileCount(); | |
| 146 for (UINT profile_idx = 0; profile_idx < profile_count; profile_idx++) { | |
| 147 GUID profile_id = {}; | |
| 148 hr = video_device_->GetVideoDecoderProfile(profile_idx, &profile_id); | |
| 149 if (SUCCEEDED(hr) && (profile_id == needed_guid)) { | |
| 150 decoder_guid = profile_id; | |
| 151 found = true; | |
| 152 break; | |
| 153 } | |
| 154 } | |
| 155 CHECK(found); | |
| 156 } | |
| 157 | |
| 158 D3D11_VIDEO_DECODER_DESC desc = {}; | |
| 159 desc.Guid = decoder_guid; | |
| 160 desc.SampleWidth = 1920; | |
| 161 desc.SampleHeight = 1088; | |
| 162 desc.OutputFormat = DXGI_FORMAT_NV12; | |
| 163 UINT config_count = 0; | |
| 164 hr = video_device_->GetVideoDecoderConfigCount(&desc, &config_count); | |
| 165 if (FAILED(hr) || config_count == 0) | |
| 166 CHECK(false); | |
| 167 | |
| 168 D3D11_VIDEO_DECODER_CONFIG dec_config = {}; | |
| 169 for (UINT i = 0; i < config_count; i++) { | |
| 170 hr = video_device_->GetVideoDecoderConfig(&desc, i, &dec_config); | |
| 171 if (FAILED(hr)) | |
| 172 CHECK(false); | |
| 173 if (dec_config.ConfigBitstreamRaw == 2) | |
| 174 break; | |
| 175 } | |
| 176 memcpy(&decoder_guid_, &decoder_guid, sizeof decoder_guid_); | |
| 177 | |
| 178 base::win::ScopedComPtr<ID3D11VideoDecoder> video_decoder; | |
| 179 hr = video_device_->CreateVideoDecoder(&desc, &dec_config, | |
| 180 video_decoder.Receive()); | |
| 181 CHECK(video_decoder.get()); | |
| 182 | |
| 183 h264_accelerator_.reset(new D3D11H264Accelerator( | |
| 184 this, video_decoder, video_device_, video_context_)); | |
| 185 decoder_.reset(new media::H264Decoder(h264_accelerator_.get())); | |
| 186 | |
| 187 return true; | |
| 188 } | |
| 189 | |
| 190 void D3D11VideoDecodeAccelerator::Decode( | |
| 191 const BitstreamBuffer& bitstream_buffer) { | |
| 192 input_buffer_queue_.push_back(bitstream_buffer); | |
| 193 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 194 FROM_HERE, base::Bind(&D3D11VideoDecodeAccelerator::DoDecode, | |
| 195 base::Unretained(this))); | |
| 196 } | |
| 197 | |
| 198 void D3D11VideoDecodeAccelerator::DoDecode() { | |
| 199 if (!bitstream_buffer_) { | |
| 200 if (input_buffer_queue_.empty()) | |
| 201 return; | |
| 202 BitstreamBuffer buffer = input_buffer_queue_.front(); | |
| 203 bitstream_buffer_ = | |
| 204 base::MakeUnique<base::SharedMemory>(buffer.handle(), true); | |
| 205 bitstream_buffer_->Map(buffer.size()); | |
| 206 bitstream_buffer_size_ = buffer.size(); | |
| 207 input_buffer_id_ = buffer.id(); | |
| 208 input_buffer_queue_.pop_front(); | |
| 209 decoder_->SetStream((const uint8_t*)bitstream_buffer_->memory(), | |
| 210 bitstream_buffer_size_); | |
| 211 } | |
| 212 | |
| 213 while (true) { | |
| 214 media::AcceleratedVideoDecoder::DecodeResult result = decoder_->Decode(); | |
| 215 if (result == media::AcceleratedVideoDecoder::kRanOutOfStreamData) { | |
| 216 client_->NotifyEndOfBitstreamBuffer(input_buffer_id_); | |
| 217 bitstream_buffer_.reset(); | |
| 218 break; | |
| 219 } | |
| 220 if (result == media::AcceleratedVideoDecoder::kRanOutOfSurfaces) { | |
| 221 return; | |
| 222 } | |
| 223 if (result == media::AcceleratedVideoDecoder::kAllocateNewSurfaces) { | |
| 224 client_->ProvidePictureBuffers(20, PIXEL_FORMAT_NV12, 2, | |
| 225 decoder_->GetPicSize(), | |
| 226 GL_TEXTURE_EXTERNAL_OES); | |
| 227 return; | |
| 228 | |
| 229 } else { | |
| 230 LOG(ERROR) << "VDA Error " << result; | |
| 231 CHECK(false); | |
| 232 } | |
| 233 } | |
| 234 | |
| 235 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 236 FROM_HERE, base::Bind(&D3D11VideoDecodeAccelerator::DoDecode, | |
| 237 base::Unretained(this))); | |
| 238 } | |
| 239 | |
| 240 void D3D11VideoDecodeAccelerator::AssignPictureBuffers( | |
| 241 const std::vector<PictureBuffer>& buffers) { | |
| 242 D3D11_TEXTURE2D_DESC texture_desc = {}; | |
| 243 texture_desc.Width = decoder_->GetPicSize().width(); | |
| 244 texture_desc.Height = decoder_->GetPicSize().height(); | |
| 245 texture_desc.MipLevels = 1; | |
| 246 texture_desc.ArraySize = buffers.size(); | |
| 247 texture_desc.Format = DXGI_FORMAT_NV12; | |
| 248 texture_desc.SampleDesc.Count = 1; | |
| 249 texture_desc.Usage = D3D11_USAGE_DEFAULT; | |
| 250 texture_desc.BindFlags = D3D11_BIND_DECODER | D3D11_BIND_SHADER_RESOURCE; | |
| 251 texture_desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED; | |
| 252 | |
| 253 base::win::ScopedComPtr<ID3D11Texture2D> out_texture; | |
| 254 HRESULT hr = | |
| 255 device_->CreateTexture2D(&texture_desc, nullptr, out_texture.Receive()); | |
| 256 CHECK(SUCCEEDED(hr)); | |
| 257 | |
| 258 make_context_current_cb_.Run(); | |
| 259 picture_buffers_.clear(); | |
| 260 | |
| 261 for (size_t i = 0; i < buffers.size(); i++) { | |
| 262 picture_buffers_.push_back( | |
| 263 base::MakeUnique<D3D11PictureBuffer>(buffers[i], i)); | |
| 264 picture_buffers_[i]->Init(video_device_, out_texture, decoder_guid_); | |
| 265 } | |
| 266 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 267 FROM_HERE, base::Bind(&D3D11VideoDecodeAccelerator::DoDecode, | |
| 268 base::Unretained(this))); | |
| 269 } | |
| 270 | |
| 271 void D3D11VideoDecodeAccelerator::ReusePictureBuffer( | |
| 272 int32_t picture_buffer_id) { | |
| 273 make_context_current_cb_.Run(); | |
| 274 for (auto& buffer : picture_buffers_) { | |
| 275 if (buffer->picture_buffer().id() == picture_buffer_id) { | |
| 276 buffer->set_in_client_use(false); | |
| 277 | |
| 278 break; | |
| 279 } | |
| 280 } | |
| 281 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 282 FROM_HERE, base::Bind(&D3D11VideoDecodeAccelerator::DoDecode, | |
| 283 base::Unretained(this))); | |
| 284 } | |
| 285 | |
| 286 D3D11PictureBuffer* D3D11VideoDecodeAccelerator::GetPicture() { | |
| 287 for (auto& buffer : picture_buffers_) | |
| 288 if (!buffer->in_client_use() && !buffer->in_picture_use()) { | |
| 289 return buffer.get(); | |
| 290 } | |
| 291 return nullptr; | |
| 292 } | |
| 293 | |
| 294 void D3D11VideoDecodeAccelerator::Flush() { | |
| 295 client_->NotifyFlushDone(); | |
| 296 } | |
| 297 | |
| 298 void D3D11VideoDecodeAccelerator::Reset() { | |
| 299 client_->NotifyResetDone(); | |
| 300 } | |
| 301 void D3D11VideoDecodeAccelerator::Destroy() {} | |
| 302 | |
| 303 bool D3D11VideoDecodeAccelerator::TryToSetupDecodeOnSeparateThread( | |
| 304 const base::WeakPtr<Client>& decode_client, | |
| 305 const scoped_refptr<base::SingleThreadTaskRunner>& decode_task_runner) { | |
| 306 return false; | |
| 307 } | |
| 308 | |
| 309 GLenum D3D11VideoDecodeAccelerator::GetSurfaceInternalFormat() const { | |
| 310 return GL_BGRA_EXT; | |
| 311 } | |
| 312 | |
| 313 size_t D3D11VideoDecodeAccelerator::input_buffer_id() const { | |
| 314 return input_buffer_id_; | |
| 315 } | |
| 316 | |
| 317 void D3D11VideoDecodeAccelerator::OutputResult(D3D11PictureBuffer* buffer, | |
| 318 size_t input_buffer_id) { | |
| 319 buffer->set_in_client_use(true); | |
| 320 Picture picture(buffer->picture_buffer().id(), input_buffer_id, | |
| 321 gfx::Rect(0, 0), gfx::ColorSpace(), false); | |
| 322 client_->PictureReady(picture); | |
| 323 } | |
| 324 } // namespace media | |
| OLD | NEW |