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 |