| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 "media/video/mft_h264_decode_engine.h" | 5 #include "media/video/mft_h264_decode_engine.h" |
| 6 | 6 |
| 7 #include <d3d9.h> | 7 #include <d3d9.h> |
| 8 #include <dxva2api.h> | 8 #include <dxva2api.h> |
| 9 #include <initguid.h> | 9 #include <initguid.h> |
| 10 #include <mfapi.h> | 10 #include <mfapi.h> |
| (...skipping 13 matching lines...) Expand all Loading... |
| 24 #pragma comment(lib, "mf.lib") | 24 #pragma comment(lib, "mf.lib") |
| 25 #pragma comment(lib, "mfplat.lib") | 25 #pragma comment(lib, "mfplat.lib") |
| 26 | 26 |
| 27 using base::TimeDelta; | 27 using base::TimeDelta; |
| 28 | 28 |
| 29 namespace media { | 29 namespace media { |
| 30 | 30 |
| 31 // Creates an empty Media Foundation sample with no buffers. | 31 // Creates an empty Media Foundation sample with no buffers. |
| 32 static IMFSample* CreateEmptySample() { | 32 static IMFSample* CreateEmptySample() { |
| 33 HRESULT hr; | 33 HRESULT hr; |
| 34 ScopedComPtr<IMFSample> sample; | 34 base::win::ScopedComPtr<IMFSample> sample; |
| 35 hr = MFCreateSample(sample.Receive()); | 35 hr = MFCreateSample(sample.Receive()); |
| 36 if (FAILED(hr)) { | 36 if (FAILED(hr)) { |
| 37 LOG(ERROR) << "Unable to create an empty sample"; | 37 LOG(ERROR) << "Unable to create an empty sample"; |
| 38 return NULL; | 38 return NULL; |
| 39 } | 39 } |
| 40 return sample.Detach(); | 40 return sample.Detach(); |
| 41 } | 41 } |
| 42 | 42 |
| 43 // Creates a Media Foundation sample with one buffer of length |buffer_length| | 43 // Creates a Media Foundation sample with one buffer of length |buffer_length| |
| 44 // on a |align|-byte boundary. Alignment must be a perfect power of 2 or 0. | 44 // on a |align|-byte boundary. Alignment must be a perfect power of 2 or 0. |
| 45 // If |align| is 0, then no alignment is specified. | 45 // If |align| is 0, then no alignment is specified. |
| 46 static IMFSample* CreateEmptySampleWithBuffer(int buffer_length, int align) { | 46 static IMFSample* CreateEmptySampleWithBuffer(int buffer_length, int align) { |
| 47 CHECK_GT(buffer_length, 0); | 47 CHECK_GT(buffer_length, 0); |
| 48 ScopedComPtr<IMFSample> sample; | 48 base::win::ScopedComPtr<IMFSample> sample; |
| 49 sample.Attach(CreateEmptySample()); | 49 sample.Attach(CreateEmptySample()); |
| 50 if (!sample.get()) | 50 if (!sample.get()) |
| 51 return NULL; | 51 return NULL; |
| 52 ScopedComPtr<IMFMediaBuffer> buffer; | 52 base::win::ScopedComPtr<IMFMediaBuffer> buffer; |
| 53 HRESULT hr; | 53 HRESULT hr; |
| 54 if (align == 0) { | 54 if (align == 0) { |
| 55 // Note that MFCreateMemoryBuffer is same as MFCreateAlignedMemoryBuffer | 55 // Note that MFCreateMemoryBuffer is same as MFCreateAlignedMemoryBuffer |
| 56 // with the align argument being 0. | 56 // with the align argument being 0. |
| 57 hr = MFCreateMemoryBuffer(buffer_length, buffer.Receive()); | 57 hr = MFCreateMemoryBuffer(buffer_length, buffer.Receive()); |
| 58 } else { | 58 } else { |
| 59 hr = MFCreateAlignedMemoryBuffer(buffer_length, | 59 hr = MFCreateAlignedMemoryBuffer(buffer_length, |
| 60 align - 1, | 60 align - 1, |
| 61 buffer.Receive()); | 61 buffer.Receive()); |
| 62 } | 62 } |
| (...skipping 14 matching lines...) Expand all Loading... |
| 77 // If duration and sample time are not known, provide 0. | 77 // If duration and sample time are not known, provide 0. |
| 78 // |min_size| specifies the minimum size of the buffer (might be required by | 78 // |min_size| specifies the minimum size of the buffer (might be required by |
| 79 // the decoder for input). The times here should be given in 100ns units. | 79 // the decoder for input). The times here should be given in 100ns units. |
| 80 // |alignment| specifies the buffer in the sample to be aligned. If no | 80 // |alignment| specifies the buffer in the sample to be aligned. If no |
| 81 // alignment is required, provide 0 or 1. | 81 // alignment is required, provide 0 or 1. |
| 82 static IMFSample* CreateInputSample(const uint8* stream, int size, | 82 static IMFSample* CreateInputSample(const uint8* stream, int size, |
| 83 int64 timestamp, int64 duration, | 83 int64 timestamp, int64 duration, |
| 84 int min_size, int alignment) { | 84 int min_size, int alignment) { |
| 85 CHECK(stream); | 85 CHECK(stream); |
| 86 CHECK_GT(size, 0); | 86 CHECK_GT(size, 0); |
| 87 ScopedComPtr<IMFSample> sample; | 87 base::win::ScopedComPtr<IMFSample> sample; |
| 88 sample.Attach(CreateEmptySampleWithBuffer(std::max(min_size, size), | 88 sample.Attach(CreateEmptySampleWithBuffer(std::max(min_size, size), |
| 89 alignment)); | 89 alignment)); |
| 90 if (!sample.get()) { | 90 if (!sample.get()) { |
| 91 LOG(ERROR) << "Failed to create empty buffer for input"; | 91 LOG(ERROR) << "Failed to create empty buffer for input"; |
| 92 return NULL; | 92 return NULL; |
| 93 } | 93 } |
| 94 HRESULT hr; | 94 HRESULT hr; |
| 95 if (duration > 0) { | 95 if (duration > 0) { |
| 96 hr = sample->SetSampleDuration(duration); | 96 hr = sample->SetSampleDuration(duration); |
| 97 if (FAILED(hr)) { | 97 if (FAILED(hr)) { |
| 98 LOG(ERROR) << "Failed to set sample duration"; | 98 LOG(ERROR) << "Failed to set sample duration"; |
| 99 return NULL; | 99 return NULL; |
| 100 } | 100 } |
| 101 } | 101 } |
| 102 if (timestamp > 0) { | 102 if (timestamp > 0) { |
| 103 hr = sample->SetSampleTime(timestamp); | 103 hr = sample->SetSampleTime(timestamp); |
| 104 if (FAILED(hr)) { | 104 if (FAILED(hr)) { |
| 105 LOG(ERROR) << "Failed to set sample time"; | 105 LOG(ERROR) << "Failed to set sample time"; |
| 106 return NULL; | 106 return NULL; |
| 107 } | 107 } |
| 108 } | 108 } |
| 109 ScopedComPtr<IMFMediaBuffer> buffer; | 109 base::win::ScopedComPtr<IMFMediaBuffer> buffer; |
| 110 hr = sample->GetBufferByIndex(0, buffer.Receive()); | 110 hr = sample->GetBufferByIndex(0, buffer.Receive()); |
| 111 if (FAILED(hr)) { | 111 if (FAILED(hr)) { |
| 112 LOG(ERROR) << "Failed to get buffer in sample"; | 112 LOG(ERROR) << "Failed to get buffer in sample"; |
| 113 return NULL; | 113 return NULL; |
| 114 } | 114 } |
| 115 DWORD max_length, current_length; | 115 DWORD max_length, current_length; |
| 116 uint8* destination; | 116 uint8* destination; |
| 117 hr = buffer->Lock(&destination, &max_length, ¤t_length); | 117 hr = buffer->Lock(&destination, &max_length, ¤t_length); |
| 118 if (FAILED(hr)) { | 118 if (FAILED(hr)) { |
| 119 LOG(ERROR) << "Failed to lock buffer"; | 119 LOG(ERROR) << "Failed to lock buffer"; |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 243 } | 243 } |
| 244 | 244 |
| 245 // Seek not implemented. | 245 // Seek not implemented. |
| 246 event_handler_->OnSeekComplete(); | 246 event_handler_->OnSeekComplete(); |
| 247 } | 247 } |
| 248 | 248 |
| 249 void MftH264DecodeEngine::ConsumeVideoSample(scoped_refptr<Buffer> buffer) { | 249 void MftH264DecodeEngine::ConsumeVideoSample(scoped_refptr<Buffer> buffer) { |
| 250 if (state_ == kUninitialized) { | 250 if (state_ == kUninitialized) { |
| 251 LOG(ERROR) << "ConsumeVideoSample: invalid state"; | 251 LOG(ERROR) << "ConsumeVideoSample: invalid state"; |
| 252 } | 252 } |
| 253 ScopedComPtr<IMFSample> sample; | 253 base::win::ScopedComPtr<IMFSample> sample; |
| 254 PipelineStatistics statistics; | 254 PipelineStatistics statistics; |
| 255 if (!buffer->IsEndOfStream()) { | 255 if (!buffer->IsEndOfStream()) { |
| 256 sample.Attach( | 256 sample.Attach( |
| 257 CreateInputSample(buffer->GetData(), | 257 CreateInputSample(buffer->GetData(), |
| 258 buffer->GetDataSize(), | 258 buffer->GetDataSize(), |
| 259 buffer->GetTimestamp().InMicroseconds() * 10, | 259 buffer->GetTimestamp().InMicroseconds() * 10, |
| 260 buffer->GetDuration().InMicroseconds() * 10, | 260 buffer->GetDuration().InMicroseconds() * 10, |
| 261 input_stream_info_.cbSize, | 261 input_stream_info_.cbSize, |
| 262 input_stream_info_.cbAlignment)); | 262 input_stream_info_.cbAlignment)); |
| 263 if (!sample.get()) { | 263 if (!sample.get()) { |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 319 hr = MFShutdown(); | 319 hr = MFShutdown(); |
| 320 if (FAILED(hr)) { | 320 if (FAILED(hr)) { |
| 321 LOG(WARNING) << "Warning: MF failed to shutdown"; | 321 LOG(WARNING) << "Warning: MF failed to shutdown"; |
| 322 } | 322 } |
| 323 CoUninitialize(); | 323 CoUninitialize(); |
| 324 } | 324 } |
| 325 | 325 |
| 326 bool MftH264DecodeEngine::EnableDxva() { | 326 bool MftH264DecodeEngine::EnableDxva() { |
| 327 IDirect3DDevice9* device = static_cast<IDirect3DDevice9*>( | 327 IDirect3DDevice9* device = static_cast<IDirect3DDevice9*>( |
| 328 context_->GetDevice()); | 328 context_->GetDevice()); |
| 329 ScopedComPtr<IDirect3DDeviceManager9> device_manager; | 329 base::win::ScopedComPtr<IDirect3DDeviceManager9> device_manager; |
| 330 UINT dev_manager_reset_token = 0; | 330 UINT dev_manager_reset_token = 0; |
| 331 HRESULT hr = DXVA2CreateDirect3DDeviceManager9(&dev_manager_reset_token, | 331 HRESULT hr = DXVA2CreateDirect3DDeviceManager9(&dev_manager_reset_token, |
| 332 device_manager.Receive()); | 332 device_manager.Receive()); |
| 333 if (FAILED(hr)) { | 333 if (FAILED(hr)) { |
| 334 LOG(ERROR) << "Couldn't create D3D Device manager"; | 334 LOG(ERROR) << "Couldn't create D3D Device manager"; |
| 335 return false; | 335 return false; |
| 336 } | 336 } |
| 337 | 337 |
| 338 hr = device_manager->ResetDevice(device, dev_manager_reset_token); | 338 hr = device_manager->ResetDevice(device, dev_manager_reset_token); |
| 339 if (FAILED(hr)) { | 339 if (FAILED(hr)) { |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 393 1, info_.stream_info.surface_width, info_.stream_info.surface_height, | 393 1, info_.stream_info.surface_width, info_.stream_info.surface_height, |
| 394 VideoFrame::RGBA, &output_frames_, | 394 VideoFrame::RGBA, &output_frames_, |
| 395 NewRunnableMethod(this, &MftH264DecodeEngine::OnAllocFramesDone)); | 395 NewRunnableMethod(this, &MftH264DecodeEngine::OnAllocFramesDone)); |
| 396 } | 396 } |
| 397 | 397 |
| 398 void MftH264DecodeEngine::OnAllocFramesDone() { | 398 void MftH264DecodeEngine::OnAllocFramesDone() { |
| 399 event_handler_->OnInitializeComplete(info_); | 399 event_handler_->OnInitializeComplete(info_); |
| 400 } | 400 } |
| 401 | 401 |
| 402 bool MftH264DecodeEngine::CheckDecodeEngineDxvaSupport() { | 402 bool MftH264DecodeEngine::CheckDecodeEngineDxvaSupport() { |
| 403 ScopedComPtr<IMFAttributes> attributes; | 403 base::win::ScopedComPtr<IMFAttributes> attributes; |
| 404 HRESULT hr = decode_engine_->GetAttributes(attributes.Receive()); | 404 HRESULT hr = decode_engine_->GetAttributes(attributes.Receive()); |
| 405 if (FAILED(hr)) { | 405 if (FAILED(hr)) { |
| 406 LOG(ERROR) << "Unlock: Failed to get attributes, hr = " | 406 LOG(ERROR) << "Unlock: Failed to get attributes, hr = " |
| 407 << std::hex << std::showbase << hr; | 407 << std::hex << std::showbase << hr; |
| 408 return false; | 408 return false; |
| 409 } | 409 } |
| 410 | 410 |
| 411 UINT32 dxva; | 411 UINT32 dxva; |
| 412 hr = attributes->GetUINT32(MF_SA_D3D_AWARE, &dxva); | 412 hr = attributes->GetUINT32(MF_SA_D3D_AWARE, &dxva); |
| 413 if (FAILED(hr) || !dxva) { | 413 if (FAILED(hr) || !dxva) { |
| 414 LOG(ERROR) << "Failed to get DXVA attr or decoder is not DXVA-aware, hr = " | 414 LOG(ERROR) << "Failed to get DXVA attr or decoder is not DXVA-aware, hr = " |
| 415 << std::hex << std::showbase << hr | 415 << std::hex << std::showbase << hr |
| 416 << " this might not be the right decoder."; | 416 << " this might not be the right decoder."; |
| 417 return false; | 417 return false; |
| 418 } | 418 } |
| 419 return true; | 419 return true; |
| 420 } | 420 } |
| 421 | 421 |
| 422 bool MftH264DecodeEngine::SetDecodeEngineMediaTypes() { | 422 bool MftH264DecodeEngine::SetDecodeEngineMediaTypes() { |
| 423 if (!SetDecodeEngineInputMediaType()) | 423 if (!SetDecodeEngineInputMediaType()) |
| 424 return false; | 424 return false; |
| 425 return SetDecodeEngineOutputMediaType( | 425 return SetDecodeEngineOutputMediaType( |
| 426 ConvertVideoFrameFormatToGuid(info_.stream_info.surface_format)); | 426 ConvertVideoFrameFormatToGuid(info_.stream_info.surface_format)); |
| 427 } | 427 } |
| 428 | 428 |
| 429 bool MftH264DecodeEngine::SetDecodeEngineInputMediaType() { | 429 bool MftH264DecodeEngine::SetDecodeEngineInputMediaType() { |
| 430 ScopedComPtr<IMFMediaType> media_type; | 430 base::win::ScopedComPtr<IMFMediaType> media_type; |
| 431 HRESULT hr = MFCreateMediaType(media_type.Receive()); | 431 HRESULT hr = MFCreateMediaType(media_type.Receive()); |
| 432 if (FAILED(hr)) { | 432 if (FAILED(hr)) { |
| 433 LOG(ERROR) << "Failed to create empty media type object"; | 433 LOG(ERROR) << "Failed to create empty media type object"; |
| 434 return false; | 434 return false; |
| 435 } | 435 } |
| 436 | 436 |
| 437 hr = media_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); | 437 hr = media_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); |
| 438 if (FAILED(hr)) { | 438 if (FAILED(hr)) { |
| 439 LOG(ERROR) << "SetGUID for major type failed"; | 439 LOG(ERROR) << "SetGUID for major type failed"; |
| 440 return false; | 440 return false; |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 534 | 534 |
| 535 return true; | 535 return true; |
| 536 } | 536 } |
| 537 | 537 |
| 538 bool MftH264DecodeEngine::DoDecode(const PipelineStatistics& statistics) { | 538 bool MftH264DecodeEngine::DoDecode(const PipelineStatistics& statistics) { |
| 539 if (state_ != kNormal && state_ != kEosDrain) { | 539 if (state_ != kNormal && state_ != kEosDrain) { |
| 540 LOG(ERROR) << "DoDecode: not in normal or drain state"; | 540 LOG(ERROR) << "DoDecode: not in normal or drain state"; |
| 541 return false; | 541 return false; |
| 542 } | 542 } |
| 543 scoped_refptr<VideoFrame> frame; | 543 scoped_refptr<VideoFrame> frame; |
| 544 ScopedComPtr<IMFSample> output_sample; | 544 base::win::ScopedComPtr<IMFSample> output_sample; |
| 545 if (!use_dxva_) { | 545 if (!use_dxva_) { |
| 546 output_sample.Attach( | 546 output_sample.Attach( |
| 547 CreateEmptySampleWithBuffer(output_stream_info_.cbSize, | 547 CreateEmptySampleWithBuffer(output_stream_info_.cbSize, |
| 548 output_stream_info_.cbAlignment)); | 548 output_stream_info_.cbAlignment)); |
| 549 if (!output_sample.get()) { | 549 if (!output_sample.get()) { |
| 550 LOG(ERROR) << "GetSample: failed to create empty output sample"; | 550 LOG(ERROR) << "GetSample: failed to create empty output sample"; |
| 551 event_handler_->OnError(); | 551 event_handler_->OnError(); |
| 552 return false; | 552 return false; |
| 553 } | 553 } |
| 554 } | 554 } |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 626 duration /= 10; | 626 duration /= 10; |
| 627 | 627 |
| 628 // Sanity checks for checking if there is really something in the sample. | 628 // Sanity checks for checking if there is really something in the sample. |
| 629 DWORD buf_count; | 629 DWORD buf_count; |
| 630 hr = output_sample->GetBufferCount(&buf_count); | 630 hr = output_sample->GetBufferCount(&buf_count); |
| 631 if (FAILED(hr) || buf_count != 1) { | 631 if (FAILED(hr) || buf_count != 1) { |
| 632 LOG(ERROR) << "Failed to get buffer count, or buffer count mismatch"; | 632 LOG(ERROR) << "Failed to get buffer count, or buffer count mismatch"; |
| 633 return true; | 633 return true; |
| 634 } | 634 } |
| 635 | 635 |
| 636 ScopedComPtr<IMFMediaBuffer> output_buffer; | 636 base::win::ScopedComPtr<IMFMediaBuffer> output_buffer; |
| 637 hr = output_sample->GetBufferByIndex(0, output_buffer.Receive()); | 637 hr = output_sample->GetBufferByIndex(0, output_buffer.Receive()); |
| 638 if (FAILED(hr)) { | 638 if (FAILED(hr)) { |
| 639 LOG(ERROR) << "Failed to get buffer from sample"; | 639 LOG(ERROR) << "Failed to get buffer from sample"; |
| 640 return true; | 640 return true; |
| 641 } | 641 } |
| 642 if (use_dxva_) { | 642 if (use_dxva_) { |
| 643 ScopedComPtr<IDirect3DSurface9, &IID_IDirect3DSurface9> surface; | 643 base::win::ScopedComPtr<IDirect3DSurface9, &IID_IDirect3DSurface9> surface; |
| 644 hr = MFGetService(output_buffer, MR_BUFFER_SERVICE, | 644 hr = MFGetService(output_buffer, MR_BUFFER_SERVICE, |
| 645 IID_PPV_ARGS(surface.Receive())); | 645 IID_PPV_ARGS(surface.Receive())); |
| 646 if (FAILED(hr)) { | 646 if (FAILED(hr)) { |
| 647 LOG(ERROR) << "Failed to get surface from buffer"; | 647 LOG(ERROR) << "Failed to get surface from buffer"; |
| 648 return true; | 648 return true; |
| 649 } | 649 } |
| 650 // Since we only allocated 1 frame from context. | 650 // Since we only allocated 1 frame from context. |
| 651 // TODO(imcheng): Detect error. | 651 // TODO(imcheng): Detect error. |
| 652 output_frames_[0]->SetTimestamp(TimeDelta::FromMicroseconds(timestamp)); | 652 output_frames_[0]->SetTimestamp(TimeDelta::FromMicroseconds(timestamp)); |
| 653 output_frames_[0]->SetDuration(TimeDelta::FromMicroseconds(duration)); | 653 output_frames_[0]->SetDuration(TimeDelta::FromMicroseconds(duration)); |
| (...skipping 23 matching lines...) Expand all Loading... |
| 677 return true; | 677 return true; |
| 678 uint8* dst_y = static_cast<uint8*>(frame->data(VideoFrame::kYPlane)); | 678 uint8* dst_y = static_cast<uint8*>(frame->data(VideoFrame::kYPlane)); |
| 679 | 679 |
| 680 memcpy(dst_y, src_y, current_length); | 680 memcpy(dst_y, src_y, current_length); |
| 681 CHECK(SUCCEEDED(output_buffer->Unlock())); | 681 CHECK(SUCCEEDED(output_buffer->Unlock())); |
| 682 event_handler_->ConsumeVideoFrame(frame, statistics); | 682 event_handler_->ConsumeVideoFrame(frame, statistics); |
| 683 return true; | 683 return true; |
| 684 } | 684 } |
| 685 | 685 |
| 686 void MftH264DecodeEngine::OnUploadVideoFrameDone( | 686 void MftH264DecodeEngine::OnUploadVideoFrameDone( |
| 687 ScopedComPtr<IDirect3DSurface9, &IID_IDirect3DSurface9> surface, | 687 base::win::ScopedComPtr<IDirect3DSurface9, &IID_IDirect3DSurface9> surface, |
| 688 scoped_refptr<VideoFrame> frame, | 688 scoped_refptr<VideoFrame> frame, |
| 689 PipelineStatistics statistics) { | 689 PipelineStatistics statistics) { |
| 690 // After this method is exited the reference to surface is released. | 690 // After this method is exited the reference to surface is released. |
| 691 event_handler_->ConsumeVideoFrame(frame, statistics); | 691 event_handler_->ConsumeVideoFrame(frame, statistics); |
| 692 } | 692 } |
| 693 | 693 |
| 694 } // namespace media | 694 } // namespace media |
| 695 | 695 |
| 696 DISABLE_RUNNABLE_METHOD_REFCOUNT(media::MftH264DecodeEngine); | 696 DISABLE_RUNNABLE_METHOD_REFCOUNT(media::MftH264DecodeEngine); |
| OLD | NEW |