OLD | NEW |
| (Empty) |
1 /* | |
2 * libjingle | |
3 * Copyright 2004 Google Inc. | |
4 * | |
5 * Redistribution and use in source and binary forms, with or without | |
6 * modification, are permitted provided that the following conditions are met: | |
7 * | |
8 * 1. Redistributions of source code must retain the above copyright notice, | |
9 * this list of conditions and the following disclaimer. | |
10 * 2. Redistributions in binary form must reproduce the above copyright notice, | |
11 * this list of conditions and the following disclaimer in the documentation | |
12 * and/or other materials provided with the distribution. | |
13 * 3. The name of the author may not be used to endorse or promote products | |
14 * derived from this software without specific prior written permission. | |
15 * | |
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | |
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | |
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | |
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
26 */ | |
27 | |
28 // Implementation of GdiVideoRenderer on Windows | |
29 | |
30 #ifdef WIN32 | |
31 | |
32 #include "talk/media/devices/gdivideorenderer.h" | |
33 | |
34 #include "talk/media/base/videocommon.h" | |
35 #include "talk/media/base/videoframe.h" | |
36 #include "webrtc/base/scoped_ptr.h" | |
37 #include "webrtc/base/thread.h" | |
38 #include "webrtc/base/win32window.h" | |
39 | |
40 namespace cricket { | |
41 | |
42 ///////////////////////////////////////////////////////////////////////////// | |
43 // Definition of private class VideoWindow. We use a worker thread to manage | |
44 // the window. | |
45 ///////////////////////////////////////////////////////////////////////////// | |
46 class GdiVideoRenderer::VideoWindow : public rtc::Win32Window { | |
47 public: | |
48 VideoWindow(int x, int y, int width, int height); | |
49 virtual ~VideoWindow(); | |
50 | |
51 // Called when the video size changes. If it is called the first time, we | |
52 // create and start the thread. Otherwise, we send kSetSizeMsg to the thread. | |
53 // Context: non-worker thread. | |
54 bool SetSize(int width, int height); | |
55 | |
56 // Called when a new frame is available. Upon this call, we send | |
57 // kRenderFrameMsg to the window thread. Context: non-worker thread. It may be | |
58 // better to pass RGB bytes to VideoWindow. However, we pass VideoFrame to put | |
59 // all the thread synchronization within VideoWindow. | |
60 bool RenderFrame(const VideoFrame* frame); | |
61 | |
62 protected: | |
63 // Override virtual method of rtc::Win32Window. Context: worker Thread. | |
64 virtual bool OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, | |
65 LRESULT& result); | |
66 | |
67 private: | |
68 enum { kSetSizeMsg = WM_USER, kRenderFrameMsg}; | |
69 | |
70 class WindowThread : public rtc::Thread { | |
71 public: | |
72 explicit WindowThread(VideoWindow* window) : window_(window) {} | |
73 | |
74 virtual ~WindowThread() { | |
75 Stop(); | |
76 } | |
77 | |
78 // Override virtual method of rtc::Thread. Context: worker Thread. | |
79 virtual void Run() { | |
80 // Initialize the window | |
81 if (!window_ || !window_->Initialize()) { | |
82 return; | |
83 } | |
84 // Run the message loop | |
85 MSG msg; | |
86 while (GetMessage(&msg, NULL, 0, 0) > 0) { | |
87 TranslateMessage(&msg); | |
88 DispatchMessage(&msg); | |
89 } | |
90 } | |
91 | |
92 private: | |
93 VideoWindow* window_; | |
94 }; | |
95 | |
96 // Context: worker Thread. | |
97 bool Initialize(); | |
98 void OnPaint(); | |
99 void OnSize(int width, int height, bool frame_changed); | |
100 void OnRenderFrame(const VideoFrame* frame); | |
101 | |
102 BITMAPINFO bmi_; | |
103 rtc::scoped_ptr<uint8_t[]> image_; | |
104 rtc::scoped_ptr<WindowThread> window_thread_; | |
105 // The initial position of the window. | |
106 int initial_x_; | |
107 int initial_y_; | |
108 }; | |
109 | |
110 ///////////////////////////////////////////////////////////////////////////// | |
111 // Implementation of class VideoWindow | |
112 ///////////////////////////////////////////////////////////////////////////// | |
113 GdiVideoRenderer::VideoWindow::VideoWindow( | |
114 int x, int y, int width, int height) | |
115 : initial_x_(x), | |
116 initial_y_(y) { | |
117 memset(&bmi_.bmiHeader, 0, sizeof(bmi_.bmiHeader)); | |
118 bmi_.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); | |
119 bmi_.bmiHeader.biPlanes = 1; | |
120 bmi_.bmiHeader.biBitCount = 32; | |
121 bmi_.bmiHeader.biCompression = BI_RGB; | |
122 bmi_.bmiHeader.biWidth = width; | |
123 bmi_.bmiHeader.biHeight = -height; | |
124 bmi_.bmiHeader.biSizeImage = width * height * 4; | |
125 | |
126 image_.reset(new uint8_t[bmi_.bmiHeader.biSizeImage]); | |
127 } | |
128 | |
129 GdiVideoRenderer::VideoWindow::~VideoWindow() { | |
130 // Context: caller Thread. We cannot call Destroy() since the window was | |
131 // created by another thread. Instead, we send WM_CLOSE message. | |
132 if (handle()) { | |
133 SendMessage(handle(), WM_CLOSE, 0, 0); | |
134 } | |
135 } | |
136 | |
137 bool GdiVideoRenderer::VideoWindow::SetSize(int width, int height) { | |
138 if (!window_thread_.get()) { | |
139 // Create and start the window thread. | |
140 window_thread_.reset(new WindowThread(this)); | |
141 return window_thread_->Start(); | |
142 } else if (width != bmi_.bmiHeader.biWidth || | |
143 height != -bmi_.bmiHeader.biHeight) { | |
144 SendMessage(handle(), kSetSizeMsg, 0, MAKELPARAM(width, height)); | |
145 } | |
146 return true; | |
147 } | |
148 | |
149 bool GdiVideoRenderer::VideoWindow::RenderFrame(const VideoFrame* video_frame) { | |
150 if (!handle()) { | |
151 return false; | |
152 } | |
153 | |
154 const VideoFrame* frame = video_frame->GetCopyWithRotationApplied(); | |
155 | |
156 if (!SetSize(static_cast<int>(frame->GetWidth()), | |
157 static_cast<int>(frame->GetHeight()))) { | |
158 return false; | |
159 } | |
160 | |
161 SendMessage(handle(), kRenderFrameMsg, reinterpret_cast<WPARAM>(frame), 0); | |
162 return true; | |
163 } | |
164 | |
165 bool GdiVideoRenderer::VideoWindow::OnMessage(UINT uMsg, WPARAM wParam, | |
166 LPARAM lParam, LRESULT& result) { | |
167 switch (uMsg) { | |
168 case WM_PAINT: | |
169 OnPaint(); | |
170 return true; | |
171 | |
172 case WM_DESTROY: | |
173 PostQuitMessage(0); // post WM_QUIT to end the message loop in Run() | |
174 return false; | |
175 | |
176 case WM_SIZE: // The window UI was resized. | |
177 OnSize(LOWORD(lParam), HIWORD(lParam), false); | |
178 return true; | |
179 | |
180 case kSetSizeMsg: // The video resolution changed. | |
181 OnSize(LOWORD(lParam), HIWORD(lParam), true); | |
182 return true; | |
183 | |
184 case kRenderFrameMsg: | |
185 OnRenderFrame(reinterpret_cast<const VideoFrame*>(wParam)); | |
186 return true; | |
187 } | |
188 return false; | |
189 } | |
190 | |
191 bool GdiVideoRenderer::VideoWindow::Initialize() { | |
192 if (!rtc::Win32Window::Create( | |
193 NULL, L"Video Renderer", | |
194 WS_OVERLAPPEDWINDOW | WS_SIZEBOX, | |
195 WS_EX_APPWINDOW, | |
196 initial_x_, initial_y_, | |
197 bmi_.bmiHeader.biWidth, -bmi_.bmiHeader.biHeight)) { | |
198 return false; | |
199 } | |
200 OnSize(bmi_.bmiHeader.biWidth, -bmi_.bmiHeader.biHeight, false); | |
201 return true; | |
202 } | |
203 | |
204 void GdiVideoRenderer::VideoWindow::OnPaint() { | |
205 RECT rcClient; | |
206 GetClientRect(handle(), &rcClient); | |
207 PAINTSTRUCT ps; | |
208 HDC hdc = BeginPaint(handle(), &ps); | |
209 StretchDIBits(hdc, | |
210 0, 0, rcClient.right, rcClient.bottom, // destination rect | |
211 0, 0, bmi_.bmiHeader.biWidth, -bmi_.bmiHeader.biHeight, // source rect | |
212 image_.get(), &bmi_, DIB_RGB_COLORS, SRCCOPY); | |
213 EndPaint(handle(), &ps); | |
214 } | |
215 | |
216 void GdiVideoRenderer::VideoWindow::OnSize(int width, int height, | |
217 bool frame_changed) { | |
218 // Get window and client sizes | |
219 RECT rcClient, rcWindow; | |
220 GetClientRect(handle(), &rcClient); | |
221 GetWindowRect(handle(), &rcWindow); | |
222 | |
223 // Find offset between window size and client size | |
224 POINT ptDiff; | |
225 ptDiff.x = (rcWindow.right - rcWindow.left) - rcClient.right; | |
226 ptDiff.y = (rcWindow.bottom - rcWindow.top) - rcClient.bottom; | |
227 | |
228 // Resize client | |
229 MoveWindow(handle(), rcWindow.left, rcWindow.top, | |
230 width + ptDiff.x, height + ptDiff.y, false); | |
231 UpdateWindow(handle()); | |
232 ShowWindow(handle(), SW_SHOW); | |
233 | |
234 if (frame_changed && (width != bmi_.bmiHeader.biWidth || | |
235 height != -bmi_.bmiHeader.biHeight)) { | |
236 // Update the bmi and image buffer | |
237 bmi_.bmiHeader.biWidth = width; | |
238 bmi_.bmiHeader.biHeight = -height; | |
239 bmi_.bmiHeader.biSizeImage = width * height * 4; | |
240 image_.reset(new uint8_t[bmi_.bmiHeader.biSizeImage]); | |
241 } | |
242 } | |
243 | |
244 void GdiVideoRenderer::VideoWindow::OnRenderFrame(const VideoFrame* frame) { | |
245 if (!frame) { | |
246 return; | |
247 } | |
248 // Convert frame to ARGB format, which is accepted by GDI | |
249 frame->ConvertToRgbBuffer(cricket::FOURCC_ARGB, image_.get(), | |
250 bmi_.bmiHeader.biSizeImage, | |
251 bmi_.bmiHeader.biWidth * 4); | |
252 InvalidateRect(handle(), 0, 0); | |
253 } | |
254 | |
255 ///////////////////////////////////////////////////////////////////////////// | |
256 // Implementation of class GdiVideoRenderer | |
257 ///////////////////////////////////////////////////////////////////////////// | |
258 GdiVideoRenderer::GdiVideoRenderer(int x, int y) | |
259 : initial_x_(x), | |
260 initial_y_(y) { | |
261 } | |
262 GdiVideoRenderer::~GdiVideoRenderer() {} | |
263 | |
264 bool GdiVideoRenderer::SetSize(int width, int height, int reserved) { | |
265 if (!window_.get()) { // Create the window for the first frame | |
266 window_.reset(new VideoWindow(initial_x_, initial_y_, width, height)); | |
267 } | |
268 return window_->SetSize(width, height); | |
269 } | |
270 | |
271 bool GdiVideoRenderer::RenderFrame(const VideoFrame* frame) { | |
272 if (!frame || !window_.get()) { | |
273 return false; | |
274 } | |
275 return window_->RenderFrame(frame); | |
276 } | |
277 | |
278 } // namespace cricket | |
279 #endif // WIN32 | |
OLD | NEW |