1 // Copyright (c) 2010 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.
5 #include "media/mf/basic_renderer.h"
11 #include "base/message_loop.h"
12 #include "base/scoped_comptr_win.h"
13 #include "base/time.h"
14 #include "media/base/yuv_convert.h"
16 // For MFGetService and MF_BUFFER_SERVICE (getting D3D surface from buffer)
17 #pragma comment(lib, "mf.lib")
18 #pragma comment(lib, "strmiids.lib")
22 // Converts the given raw data buffer into RGB32 format, and drawing the result
23 // into the given window. This is only used when DXVA2 is not enabled.
24 // Returns: true on success.
25 bool ConvertToRGBAndDrawToWindow(HWND video_window
, uint8
* data
, int width
,
26 int height
, int stride
) {
27 CHECK(video_window
!= NULL
);
31 CHECK_GE(stride
, width
);
32 height
= (height
+ 15) & ~15;
34 uint8
* y_start
= reinterpret_cast<uint8
*>(data
);
35 uint8
* u_start
= y_start
+ height
* stride
* 5 / 4;
36 uint8
* v_start
= y_start
+ height
* stride
;
37 static uint8
* rgb_frame
= new uint8
[height
* stride
* 4];
38 int y_stride
= stride
;
39 int uv_stride
= stride
/ 2;
40 int rgb_stride
= stride
* 4;
41 ConvertYUVToRGB32(y_start
, u_start
, v_start
, rgb_frame
,
42 width
, height
, y_stride
, uv_stride
,
45 InvalidateRect(video_window
, NULL
, TRUE
);
46 HDC hdc
= BeginPaint(video_window
, &ps
);
48 hdr
.biSize
= sizeof(BITMAPINFOHEADER
);
50 hdr
.biHeight
= -height
; // minus means top-down bitmap
53 hdr
.biCompression
= BI_RGB
; // no compression
55 hdr
.biXPelsPerMeter
= 1;
56 hdr
.biYPelsPerMeter
= 1;
58 hdr
.biClrImportant
= 0;
59 int rv
= StretchDIBits(hdc
, 0, 0, width
, height
, 0, 0, width
, height
,
60 rgb_frame
, reinterpret_cast<BITMAPINFO
*>(&hdr
),
61 DIB_RGB_COLORS
, SRCCOPY
);
63 LOG(ERROR
) << "StretchDIBits failed";
64 MessageLoopForUI::current()->QuitNow();
67 EndPaint(video_window
, &ps
);
72 // Obtains the underlying raw data buffer for the given IMFMediaBuffer, and
73 // calls ConvertToRGBAndDrawToWindow() with it.
74 // Returns: true on success.
75 bool PaintMediaBufferOntoWindow(HWND video_window
, IMFMediaBuffer
* video_buffer
,
76 int width
, int height
, int stride
) {
77 CHECK(video_buffer
!= NULL
);
82 hr
= video_buffer
->Lock(&data
, &buffer_length
, &data_length
);
84 LOG(ERROR
) << "Failed to lock IMFMediaBuffer";
87 if (!ConvertToRGBAndDrawToWindow(video_window
,
88 reinterpret_cast<uint8
*>(data
),
92 LOG(ERROR
) << "Failed to convert raw buffer to RGB and draw to window";
93 video_buffer
->Unlock();
96 video_buffer
->Unlock();
100 // Obtains the D3D9 surface from the given IMFMediaBuffer, then calls methods
101 // in the D3D device to draw to the window associated with it.
102 // Returns: true on success.
103 bool PaintD3D9BufferOntoWindow(IDirect3DDevice9
* device
,
104 IMFMediaBuffer
* video_buffer
) {
105 CHECK(device
!= NULL
);
106 ScopedComPtr
<IDirect3DSurface9
> surface
;
107 HRESULT hr
= MFGetService(video_buffer
, MR_BUFFER_SERVICE
,
108 IID_PPV_ARGS(surface
.Receive()));
110 LOG(ERROR
) << "Failed to get D3D9 surface from buffer";
113 hr
= device
->Clear(0, NULL
, D3DCLEAR_TARGET
, D3DCOLOR_XRGB(0, 0, 0),
116 LOG(ERROR
) << "Device->Clear() failed";
119 ScopedComPtr
<IDirect3DSurface9
> backbuffer
;
120 hr
= device
->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO
,
121 backbuffer
.Receive());
123 LOG(ERROR
) << "Device->GetBackBuffer() failed";
126 hr
= device
->StretchRect(surface
.get(), NULL
, backbuffer
.get(), NULL
,
129 LOG(ERROR
) << "Device->StretchRect() failed";
132 hr
= device
->Present(NULL
, NULL
, NULL
, NULL
);
135 LOG(WARNING
) << "Present() returned E_FAIL";
137 static int frames_dropped
= 0;
138 LOG(ERROR
) << "Device->Present() failed "
139 << std::hex
<< std::showbase
<< hr
;
140 if (++frames_dropped
== 10) {
141 LOG(ERROR
) << "Dropped too many frames, quitting";
142 MessageLoopForUI::current()->QuitNow();
150 static void ReleaseOutputBuffer(VideoFrame
* frame
) {
152 frame
->type() == VideoFrame::TYPE_MFBUFFER
||
153 frame
->type() == VideoFrame::TYPE_DIRECT3DSURFACE
) {
154 static_cast<IMFMediaBuffer
*>(frame
->private_buffer())->Release();
160 NullRenderer::NullRenderer(MftH264Decoder
* decoder
) : MftRenderer(decoder
) {}
161 NullRenderer::~NullRenderer() {}
163 void NullRenderer::ProcessFrame(scoped_refptr
<VideoFrame
> frame
) {
164 ReleaseOutputBuffer(frame
);
165 MessageLoop::current()->PostTask(
166 FROM_HERE
, NewRunnableMethod(decoder_
.get(),
167 &MftH264Decoder::GetOutput
));
170 void NullRenderer::StartPlayback() {
171 MessageLoop::current()->PostTask(
172 FROM_HERE
, NewRunnableMethod(decoder_
.get(),
173 &MftH264Decoder::GetOutput
));
176 void NullRenderer::StopPlayback() {
177 MessageLoop::current()->Quit();
182 BasicRenderer::BasicRenderer(MftH264Decoder
* decoder
,
183 HWND window
, IDirect3DDevice9
* device
)
184 : MftRenderer(decoder
),
189 BasicRenderer::~BasicRenderer() {}
191 void BasicRenderer::ProcessFrame(scoped_refptr
<VideoFrame
> frame
) {
192 if (device_
!= NULL
) {
193 if (!PaintD3D9BufferOntoWindow(device_
,
194 static_cast<IMFMediaBuffer
*>(frame
->private_buffer()))) {
195 MessageLoopForUI::current()->QuitNow();
198 if (!PaintMediaBufferOntoWindow(
199 window_
, static_cast<IMFMediaBuffer
*>(frame
->private_buffer()),
200 frame
->width(), frame
->height(), frame
->stride(0))) {
201 MessageLoopForUI::current()->QuitNow();
204 ReleaseOutputBuffer(frame
);
205 MessageLoopForUI::current()->PostDelayedTask(
206 FROM_HERE
, NewRunnableMethod(decoder_
.get(),
207 &MftH264Decoder::GetOutput
),
208 frame
->GetDuration().InMilliseconds());
211 void BasicRenderer::StartPlayback() {
212 MessageLoopForUI::current()->PostTask(
213 FROM_HERE
, NewRunnableMethod(decoder_
.get(),
214 &MftH264Decoder::GetOutput
));
217 void BasicRenderer::StopPlayback() {
218 MessageLoopForUI::current()->Quit();