| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/common/gpu/media/dxva_video_decode_accelerator.h" | 5 #include "content/common/gpu/media/dxva_video_decode_accelerator.h" |
| 6 | 6 |
| 7 #if !defined(OS_WIN) | 7 #if !defined(OS_WIN) |
| 8 #error This file should only be built on Windows. | 8 #error This file should only be built on Windows. |
| 9 #endif // !defined(OS_WIN) | 9 #endif // !defined(OS_WIN) |
| 10 | 10 |
| 11 #include <ks.h> | 11 #include <ks.h> |
| 12 #include <codecapi.h> | 12 #include <codecapi.h> |
| 13 #include <d3dx9tex.h> | |
| 14 #include <mfapi.h> | 13 #include <mfapi.h> |
| 15 #include <mferror.h> | 14 #include <mferror.h> |
| 16 #include <wmcodecdsp.h> | 15 #include <wmcodecdsp.h> |
| 17 | 16 |
| 18 #include "base/bind.h" | 17 #include "base/bind.h" |
| 19 #include "base/callback.h" | 18 #include "base/callback.h" |
| 20 #include "base/command_line.h" | 19 #include "base/command_line.h" |
| 21 #include "base/debug/trace_event.h" | 20 #include "base/debug/trace_event.h" |
| 22 #include "base/logging.h" | 21 #include "base/logging.h" |
| 23 #include "base/memory/scoped_handle.h" | 22 #include "base/memory/scoped_handle.h" |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 151 base::SharedMemory shm(bitstream_buffer.handle(), true); | 150 base::SharedMemory shm(bitstream_buffer.handle(), true); |
| 152 RETURN_ON_FAILURE(shm.Map(bitstream_buffer.size()), | 151 RETURN_ON_FAILURE(shm.Map(bitstream_buffer.size()), |
| 153 "Failed in base::SharedMemory::Map", NULL); | 152 "Failed in base::SharedMemory::Map", NULL); |
| 154 | 153 |
| 155 return CreateInputSample(reinterpret_cast<const uint8*>(shm.memory()), | 154 return CreateInputSample(reinterpret_cast<const uint8*>(shm.memory()), |
| 156 bitstream_buffer.size(), | 155 bitstream_buffer.size(), |
| 157 stream_size, | 156 stream_size, |
| 158 alignment); | 157 alignment); |
| 159 } | 158 } |
| 160 | 159 |
| 161 // Helper function to read the bitmap from the D3D surface passed in. | |
| 162 static bool GetBitmapFromSurface(IDirect3DDevice9Ex* device, | |
| 163 IDirect3DSurface9* surface, | |
| 164 scoped_array<char>* bits) { | |
| 165 // TODO(ananta) | |
| 166 // The code below may not be necessary once we have an ANGLE extension which | |
| 167 // allows us to pass the Direct 3D surface directly for rendering. | |
| 168 | |
| 169 // The decoded bits in the source direct 3d surface are in the YUV | |
| 170 // format. Angle does not support that. As a workaround we create an | |
| 171 // offscreen surface in the RGB format and copy the source surface | |
| 172 // to this surface. | |
| 173 D3DSURFACE_DESC surface_desc; | |
| 174 HRESULT hr = surface->GetDesc(&surface_desc); | |
| 175 RETURN_ON_HR_FAILURE(hr, "Failed to get surface description", false); | |
| 176 | |
| 177 base::win::ScopedComPtr<IDirect3DSurface9> dest_surface; | |
| 178 hr = device->CreateOffscreenPlainSurface(surface_desc.Width, | |
| 179 surface_desc.Height, | |
| 180 D3DFMT_A8R8G8B8, | |
| 181 D3DPOOL_DEFAULT, | |
| 182 dest_surface.Receive(), | |
| 183 NULL); | |
| 184 RETURN_ON_HR_FAILURE(hr, "Failed to create offscreen surface", false); | |
| 185 | |
| 186 hr = D3DXLoadSurfaceFromSurface(dest_surface, NULL, NULL, surface, NULL, | |
| 187 NULL, D3DX_DEFAULT, 0); | |
| 188 RETURN_ON_HR_FAILURE(hr, "D3DXLoadSurfaceFromSurface failed", false); | |
| 189 | |
| 190 // Get the currently loaded bitmap from the DC. | |
| 191 HDC hdc = NULL; | |
| 192 hr = dest_surface->GetDC(&hdc); | |
| 193 RETURN_ON_HR_FAILURE(hr, "Failed to get HDC from surface", false); | |
| 194 | |
| 195 HBITMAP bitmap = | |
| 196 reinterpret_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP)); | |
| 197 if (!bitmap) { | |
| 198 NOTREACHED() << "Failed to get bitmap from DC"; | |
| 199 dest_surface->ReleaseDC(hdc); | |
| 200 return false; | |
| 201 } | |
| 202 | |
| 203 BITMAP bitmap_basic_info = {0}; | |
| 204 if (!GetObject(bitmap, sizeof(BITMAP), &bitmap_basic_info)) { | |
| 205 NOTREACHED() << "Failed to read bitmap info"; | |
| 206 dest_surface->ReleaseDC(hdc); | |
| 207 return false; | |
| 208 } | |
| 209 BITMAPINFO bitmap_info = {0}; | |
| 210 bitmap_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); | |
| 211 bitmap_info.bmiHeader.biWidth = bitmap_basic_info.bmWidth; | |
| 212 bitmap_info.bmiHeader.biHeight = bitmap_basic_info.bmHeight; | |
| 213 bitmap_info.bmiHeader.biPlanes = 1; | |
| 214 bitmap_info.bmiHeader.biBitCount = bitmap_basic_info.bmBitsPixel; | |
| 215 bitmap_info.bmiHeader.biCompression = BI_RGB; | |
| 216 bitmap_info.bmiHeader.biSizeImage = 0; | |
| 217 bitmap_info.bmiHeader.biClrUsed = 0; | |
| 218 | |
| 219 int ret = GetDIBits(hdc, bitmap, 0, 0, NULL, &bitmap_info, DIB_RGB_COLORS); | |
| 220 if (!ret || bitmap_info.bmiHeader.biSizeImage <= 0) { | |
| 221 NOTREACHED() << "Failed to read bitmap size"; | |
| 222 dest_surface->ReleaseDC(hdc); | |
| 223 return false; | |
| 224 } | |
| 225 | |
| 226 bits->reset(new char[bitmap_info.bmiHeader.biSizeImage]); | |
| 227 ret = GetDIBits(hdc, bitmap, 0, bitmap_basic_info.bmHeight, bits->get(), | |
| 228 &bitmap_info, DIB_RGB_COLORS); | |
| 229 if (!ret) { | |
| 230 NOTREACHED() << "Failed to retrieve bitmap bits."; | |
| 231 } | |
| 232 dest_surface->ReleaseDC(hdc); | |
| 233 return !!ret; | |
| 234 } | |
| 235 | |
| 236 // Maintains information about a DXVA picture buffer, i.e. whether it is | 160 // Maintains information about a DXVA picture buffer, i.e. whether it is |
| 237 // available for rendering, the texture information, etc. | 161 // available for rendering, the texture information, etc. |
| 238 struct DXVAVideoDecodeAccelerator::DXVAPictureBuffer { | 162 struct DXVAVideoDecodeAccelerator::DXVAPictureBuffer { |
| 239 public: | 163 public: |
| 240 static linked_ptr<DXVAPictureBuffer> Create( | 164 static linked_ptr<DXVAPictureBuffer> Create( |
| 241 const media::PictureBuffer& buffer, EGLConfig egl_config); | 165 const media::PictureBuffer& buffer, EGLConfig egl_config); |
| 242 ~DXVAPictureBuffer(); | 166 ~DXVAPictureBuffer(); |
| 243 | 167 |
| 244 void ReusePictureBuffer(); | 168 void ReusePictureBuffer(); |
| 245 // Copies the output sample data to the picture buffer provided by the | 169 // Copies the output sample data to the picture buffer provided by the |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 364 NOTREACHED() << "Decode surface of different dimension than texture"; | 288 NOTREACHED() << "Decode surface of different dimension than texture"; |
| 365 return false; | 289 return false; |
| 366 } | 290 } |
| 367 | 291 |
| 368 hr = d3d9_->CheckDeviceFormatConversion(D3DADAPTER_DEFAULT, | 292 hr = d3d9_->CheckDeviceFormatConversion(D3DADAPTER_DEFAULT, |
| 369 D3DDEVTYPE_HAL, | 293 D3DDEVTYPE_HAL, |
| 370 surface_desc.Format, | 294 surface_desc.Format, |
| 371 D3DFMT_X8R8G8B8); | 295 D3DFMT_X8R8G8B8); |
| 372 bool device_supports_format_conversion = (hr == S_OK); | 296 bool device_supports_format_conversion = (hr == S_OK); |
| 373 | 297 |
| 298 RETURN_ON_FAILURE(device_supports_format_conversion, |
| 299 "Device does not support format converision", |
| 300 false); |
| 301 |
| 374 // This function currently executes in the context of IPC handlers in the | 302 // This function currently executes in the context of IPC handlers in the |
| 375 // GPU process which ensures that there is always an OpenGL context. | 303 // GPU process which ensures that there is always an OpenGL context. |
| 376 GLint current_texture = 0; | 304 GLint current_texture = 0; |
| 377 glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_texture); | 305 glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_texture); |
| 378 | 306 |
| 379 glBindTexture(GL_TEXTURE_2D, picture_buffer_.texture_id()); | 307 glBindTexture(GL_TEXTURE_2D, picture_buffer_.texture_id()); |
| 380 | 308 |
| 381 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | 309 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| 382 | 310 |
| 383 if (device_supports_format_conversion) { | 311 base::win::ScopedComPtr<IDirect3DSurface9> d3d_surface; |
| 384 base::win::ScopedComPtr<IDirect3DSurface9> d3d_surface; | 312 hr = decoding_texture_->GetSurfaceLevel(0, d3d_surface.Receive()); |
| 385 HRESULT hr = decoding_texture_->GetSurfaceLevel(0, d3d_surface.Receive()); | 313 RETURN_ON_HR_FAILURE(hr, "Failed to get surface from texture", false); |
| 386 RETURN_ON_HR_FAILURE(hr, "Failed to get surface from texture", false); | |
| 387 | 314 |
| 388 hr = device_->StretchRect(dest_surface, | 315 hr = device_->StretchRect(dest_surface, |
| 389 NULL, | 316 NULL, |
| 390 d3d_surface, | 317 d3d_surface, |
| 391 NULL, | 318 NULL, |
| 392 D3DTEXF_NONE); | 319 D3DTEXF_NONE); |
| 393 RETURN_ON_HR_FAILURE(hr, "Colorspace conversion via StretchRect failed", | 320 RETURN_ON_HR_FAILURE(hr, "Colorspace conversion via StretchRect failed", |
| 394 false); | 321 false); |
| 395 // Ideally, this should be done immediately before the draw call that uses | 322 |
| 396 // the texture. Flush it once here though. | 323 // Ideally, this should be done immediately before the draw call that uses |
| 397 hr = query_->Issue(D3DISSUE_END); | 324 // the texture. Flush it once here though. |
| 398 RETURN_ON_HR_FAILURE(hr, "Failed to issue END", false); | 325 hr = query_->Issue(D3DISSUE_END); |
| 399 do { | 326 RETURN_ON_HR_FAILURE(hr, "Failed to issue END", false); |
| 400 hr = query_->GetData(NULL, 0, D3DGETDATA_FLUSH); | 327 do { |
| 401 if (hr == S_FALSE) | 328 hr = query_->GetData(NULL, 0, D3DGETDATA_FLUSH); |
| 402 Sleep(1); // Poor-man's Yield(). | 329 if (hr == S_FALSE) |
| 403 } while (hr == S_FALSE); | 330 Sleep(1); // Poor-man's Yield(). |
| 404 eglBindTexImage( | 331 } while (hr == S_FALSE); |
| 405 static_cast<EGLDisplay*>(eglGetDisplay(EGL_DEFAULT_DISPLAY)), | 332 eglBindTexImage( |
| 406 decoding_surface_, | 333 static_cast<EGLDisplay*>(eglGetDisplay(EGL_DEFAULT_DISPLAY)), |
| 407 EGL_BACK_BUFFER); | 334 decoding_surface_, |
| 408 } else { | 335 EGL_BACK_BUFFER); |
| 409 scoped_array<char> bits; | 336 |
| 410 RETURN_ON_FAILURE(GetBitmapFromSurface(DXVAVideoDecodeAccelerator::device_, | |
| 411 dest_surface, &bits), | |
| 412 "Failed to get bitmap from surface for rendering", | |
| 413 false); | |
| 414 glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, surface_desc.Width, | |
| 415 surface_desc.Height, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, | |
| 416 reinterpret_cast<GLvoid*>(bits.get())); | |
| 417 } | |
| 418 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | 337 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| 419 glBindTexture(GL_TEXTURE_2D, current_texture); | 338 glBindTexture(GL_TEXTURE_2D, current_texture); |
| 420 return true; | 339 return true; |
| 421 } | 340 } |
| 422 | 341 |
| 423 DXVAVideoDecodeAccelerator::PendingSampleInfo::PendingSampleInfo( | 342 DXVAVideoDecodeAccelerator::PendingSampleInfo::PendingSampleInfo( |
| 424 int32 buffer_id, IMFSample* sample) | 343 int32 buffer_id, IMFSample* sample) |
| 425 : input_buffer_id(buffer_id) { | 344 : input_buffer_id(buffer_id) { |
| 426 output_sample.Attach(sample); | 345 output_sample.Attach(sample); |
| 427 } | 346 } |
| 428 | 347 |
| 429 DXVAVideoDecodeAccelerator::PendingSampleInfo::~PendingSampleInfo() {} | 348 DXVAVideoDecodeAccelerator::PendingSampleInfo::~PendingSampleInfo() {} |
| 430 | 349 |
| 431 // static | 350 // static |
| 432 void DXVAVideoDecodeAccelerator::PreSandboxInitialization() { | 351 void DXVAVideoDecodeAccelerator::PreSandboxInitialization() { |
| 433 // Should be called only once during program startup. | 352 // Should be called only once during program startup. |
| 434 DCHECK(!pre_sandbox_init_done_); | 353 DCHECK(!pre_sandbox_init_done_); |
| 435 | 354 |
| 436 static wchar_t* decoding_dlls[] = { | 355 static wchar_t* decoding_dlls[] = { |
| 437 L"d3d9.dll", | 356 L"d3d9.dll", |
| 438 L"d3dx9_43.dll", | |
| 439 L"dxva2.dll", | 357 L"dxva2.dll", |
| 440 L"mf.dll", | 358 L"mf.dll", |
| 441 L"mfplat.dll", | 359 L"mfplat.dll", |
| 442 L"msmpeg2vdec.dll", | 360 L"msmpeg2vdec.dll", |
| 443 }; | 361 }; |
| 444 | 362 |
| 445 for (int i = 0; i < arraysize(decoding_dlls); ++i) { | 363 for (int i = 0; i < arraysize(decoding_dlls); ++i) { |
| 446 if (!::LoadLibrary(decoding_dlls[i])) { | 364 if (!::LoadLibrary(decoding_dlls[i])) { |
| 447 DLOG(ERROR) << "Failed to load decoder dll: " << decoding_dlls[i] | 365 DLOG(ERROR) << "Failed to load decoder dll: " << decoding_dlls[i] |
| 448 << ", Error: " << ::GetLastError(); | 366 << ", Error: " << ::GetLastError(); |
| (...skipping 689 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1138 return; | 1056 return; |
| 1139 } | 1057 } |
| 1140 | 1058 |
| 1141 MessageLoop::current()->PostTask(FROM_HERE, base::Bind( | 1059 MessageLoop::current()->PostTask(FROM_HERE, base::Bind( |
| 1142 &DXVAVideoDecodeAccelerator::NotifyFlushDone, base::AsWeakPtr(this))); | 1060 &DXVAVideoDecodeAccelerator::NotifyFlushDone, base::AsWeakPtr(this))); |
| 1143 | 1061 |
| 1144 state_ = kNormal; | 1062 state_ = kNormal; |
| 1145 } | 1063 } |
| 1146 | 1064 |
| 1147 } // namespace content | 1065 } // namespace content |
| OLD | NEW |