1 //
2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010,
3 // 2011 Free Software Foundation, Inc.
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19 #include <cerrno>
20 #include <exception>
21 #include <gtk/gtk.h>
22 #include <gdk/gdk.h>
23
24 #include "log.h"
25 #include "Renderer.h"
26 #include "Renderer_agg.h"
27 #include "gtk_glue_agg_vaapi.h"
28
29 #include "Movie.h"
30 #include "movie_root.h"
31 #include "VM.h"
32
33 #include "VaapiDisplayX11.h"
34 #include "VaapiGlobalContext.h"
35 #include "VaapiContext.h"
36 #include "VaapiException.h"
37 #include "VaapiSurface.h"
38 #include "VaapiImage.h"
39 #include "VaapiSubpicture.h"
40 #include "GnashVaapiImage.h"
41 #include "GnashVaapiImageProxy.h"
42 #include "vaapi_utils.h"
43
44 #define dprintf(format,...)
45
46 // Defined to 1 if the stage will be scaled by the HW and not AGG
47 // XXX: disabled for now because of generic Gnash rendering problems
48 #define USE_HW_SCALING 0
49
50 namespace gnash
51 {
52
53 static const char *
find_pixel_format(VaapiImageFormat format)54 find_pixel_format(VaapiImageFormat format)
55 {
56 switch (format) {
57 case VAAPI_IMAGE_RGBA:
58 return "RGBA32";
59 case VAAPI_IMAGE_ARGB: return
60 "ARGB32";
61 case VAAPI_IMAGE_ABGR:
62 return "ABGR32";
63 case VAAPI_IMAGE_BGRA:
64 return "BGRA32";
65 default:;
66 }
67 return NULL;
68 }
69
70 class VaapiVideoWindow : public VaapiContextData {
71 GdkWindow *_window;
72 VaapiRectangle _rect;
73
74 public:
75 VaapiVideoWindow(GdkWindow *parent_window, VaapiRectangle const & rect);
76 ~VaapiVideoWindow();
77
78 /// Move and resize window
79 void moveResize(VaapiRectangle const & rect);
80
81 /// Return GDK window XID
xid() const82 XID xid() const
83 { return GDK_DRAWABLE_XID(_window); }
84
85 /// Return GDK window X coordinate
x() const86 int x() const
87 { return _rect.x; }
88
89 /// Return GDK window Y coordinate
y() const90 int y() const
91 { return _rect.y; }
92
93 /// Return GDK window width
width() const94 unsigned int width() const
95 { return _rect.width; }
96
97 /// Return GDK window height
height() const98 unsigned int height() const
99 { return _rect.height; }
100 };
101
VaapiVideoWindow(GdkWindow * parent_window,VaapiRectangle const & rect)102 VaapiVideoWindow::VaapiVideoWindow(GdkWindow *parent_window, VaapiRectangle const & rect)
103 {
104 GdkWindowAttr wattr;
105 wattr.event_mask = 0;
106 wattr.x = rect.x;
107 wattr.y = rect.y;
108 wattr.width = rect.width;
109 wattr.height = rect.height;
110 wattr.wclass = GDK_INPUT_OUTPUT;
111 wattr.window_type = GDK_WINDOW_CHILD;
112 _window = gdk_window_new(parent_window, &wattr, GDK_WA_X|GDK_WA_Y);
113 if (!_window) {
114 throw VaapiException("Could not create video child window");
115 }
116
117 gdk_window_show(_window);
118 gdk_window_raise(_window);
119 gdk_flush();
120 _rect = rect;
121 }
122
~VaapiVideoWindow()123 VaapiVideoWindow::~VaapiVideoWindow()
124 {
125 if (_window) {
126 gdk_window_destroy(_window);
127 _window = NULL;
128 }
129 }
130
131 void
moveResize(VaapiRectangle const & rect)132 VaapiVideoWindow::moveResize(VaapiRectangle const & rect)
133 {
134 if (!_window) {
135 return;
136 }
137
138 gdk_window_move_resize(_window, rect.x, rect.y, rect.width, rect.height);
139 gdk_flush();
140 _rect = rect;
141 }
142
GtkAggVaapiGlue()143 GtkAggVaapiGlue::GtkAggVaapiGlue()
144 : _agg_renderer(NULL)
145 , _vaapi_image_format(VAAPI_IMAGE_NONE)
146 , _vaapi_image_width(0)
147 , _vaapi_image_height(0)
148 , _window_width(0)
149 , _window_height(0)
150 , _window_is_setup(false)
151 {
152 }
153
~GtkAggVaapiGlue()154 GtkAggVaapiGlue::~GtkAggVaapiGlue()
155 {
156 }
157
158 bool
init(int,char ** [])159 GtkAggVaapiGlue::init(int /*argc*/, char ** /*argv*/[])
160 {
161 VaapiGlobalContext *const gvactx = VaapiGlobalContext::get();
162 if (!gvactx) {
163 log_error(_("WARNING: failed to create VA-API display."));
164 return false;
165 }
166 return true;
167 }
168
169 void
prepDrawingArea(GtkWidget * drawing_area)170 GtkAggVaapiGlue::prepDrawingArea(GtkWidget *drawing_area)
171 {
172 dprintf("GtkAggVaapiGlue::prepDrawingArea()\n");
173
174 _drawing_area = drawing_area;
175
176 // Disable double buffering. Otherwise, Gtk tries to update widget
177 // contents from its internal offscreen buffer at the end of
178 // expose events
179 gtk_widget_set_double_buffered(_drawing_area, FALSE);
180 }
181
182 Renderer*
createRenderHandler()183 GtkAggVaapiGlue::createRenderHandler()
184 {
185 dprintf("GtkAggVaapiGlue::createRenderHandler()\n");
186
187 VaapiGlobalContext * const gvactx = VaapiGlobalContext::get();
188 if (!gvactx)
189 return NULL;
190
191 std::vector<VaapiImageFormat> formats = gvactx->getSubpictureFormats();
192 for (unsigned int i = 0; i < formats.size(); i++) {
193 if (vaapi_image_format_is_rgb(formats[i])) {
194 _vaapi_image_format = formats[i];
195 break;
196 }
197 }
198 if (_vaapi_image_format == VAAPI_IMAGE_NONE)
199 return NULL;
200
201 const char *agg_pixel_format;
202 agg_pixel_format = find_pixel_format(_vaapi_image_format);
203 if (!agg_pixel_format) {
204 log_error(_("GTK-AGG: Unknown RGB format %s reported by VA-API."
205 "Please report this to the gnash-dev mailing list."),
206 string_of_FOURCC(_vaapi_image_format));
207 return NULL;
208 }
209
210 Renderer * const renderer = create_Renderer_agg(agg_pixel_format);
211 _agg_renderer = static_cast<Renderer_agg_base *>(renderer);
212 return renderer;
213 }
214
215 void
resetRenderSurface(unsigned int width,unsigned int height)216 GtkAggVaapiGlue::resetRenderSurface(unsigned int width, unsigned int height)
217 {
218 /* XXX: round up to 128-byte boundaries to workaround GMA500 bugs */
219 const unsigned int aligned_width = (width + 31) & -32U;
220
221 // dprintf("GtkAggVaapiGlue::resetRenderSurface(): size %ux%u\n",
222 // width, height);
223
224 _vaapi_surface.reset(new VaapiSurface(width, height));
225 _vaapi_image.reset(new VaapiImage(aligned_width, height, _vaapi_image_format));
226 _vaapi_image_width = width;
227 _vaapi_image_height = height;
228 _vaapi_subpicture.reset(new VaapiSubpicture(_vaapi_image));
229
230 if (!_vaapi_image->map()) {
231 log_error(_("failed to map VA-API image."));
232 return;
233 }
234
235 VaapiRectangle r(width, height);
236 if (!_vaapi_surface->associateSubpicture(_vaapi_subpicture, r, r)) {
237 log_error(_("failed to associate VA-API subpicture."));
238 return;
239 }
240 _vaapi_surface->clear();
241
242 _agg_renderer->init_buffer(
243 static_cast<unsigned char*>(_vaapi_image->getPlane(0)),
244 _vaapi_image->getPitch(0) * height,
245 width,
246 height,
247 _vaapi_image->getPitch(0));
248 }
249
250 void
setRenderHandlerSize(int width,int height)251 GtkAggVaapiGlue::setRenderHandlerSize(int width, int height)
252 {
253 dprintf("GtkAggVaapiGlue::setRenderHandlerSize(): %dx%d\n", width, height);
254
255 _window_width = width;
256 _window_height = height;
257 }
258
259 void
beforeRendering(movie_root * stage)260 GtkAggVaapiGlue::beforeRendering(movie_root* stage)
261 {
262 // Process all GDK pending operations
263 gdk_flush();
264
265 if (USE_HW_SCALING) {
266
267 static bool first = true;
268 if (first && stage) {
269 first = false;
270
271 Movie const & mi = stage->getRootMovie();
272 const unsigned int width = mi.widthPixels();
273 const unsigned int height = mi.heightPixels();
274 resetRenderSurface(width, height);
275 dprintf("GtkAggVaapiGlue::beforeRendering(): movie size %dx%d\n",
276 width, height);
277 }
278
279 // We force the scale to its original state in case the GUI
280 // changed it (in the event of a resize), because we want
281 // VA-API to do the scaling for us.
282 _agg_renderer->set_scale(1.0, 1.0);
283 }
284 else if (_vaapi_image_width != _window_width ||
285 _vaapi_image_height != _window_height)
286 resetRenderSurface(_window_width, _window_height);
287
288 if (!_vaapi_image->map()) {
289 log_error(_("failed to map VA-API image."));
290 return;
291 }
292 }
293
294 VaapiVideoWindow *
getVideoWindow(std::shared_ptr<VaapiSurface> surface,GdkWindow * parent_window,VaapiRectangle const & rect)295 GtkAggVaapiGlue::getVideoWindow(std::shared_ptr<VaapiSurface> surface,
296 GdkWindow *parent_window,
297 VaapiRectangle const & rect)
298 {
299 VaapiContext * const context = surface->getContext();
300 if (!context)
301 return NULL;
302
303 if (!context->getData()) {
304 context->setData(
305 std::unique_ptr<VaapiContextData>(
306 new VaapiVideoWindow(parent_window, rect)));
307 }
308 return dynamic_cast<VaapiVideoWindow *>(context->getData());
309 }
310
311 void
render()312 GtkAggVaapiGlue::render()
313 {
314 VaapiGlobalContext * const gvactx = VaapiGlobalContext::get();
315 if (!gvactx)
316 return;
317
318 if (!_window_is_setup)
319 return;
320
321 if (!_vaapi_image.get() || !_vaapi_surface.get())
322 return;
323
324 if (!_vaapi_image->unmap()) {
325 printf("ERROR: failed to unmap VA-API image\n");
326 return;
327 }
328
329 VAStatus status;
330 status = vaPutSurface(gvactx->display(),
331 _vaapi_surface->get(),
332 GDK_DRAWABLE_XID(_drawing_area->window),
333 0, 0,
334 _vaapi_surface->width(),
335 _vaapi_surface->height(),
336 0, 0,
337 _window_width,
338 _window_height,
339 NULL, 0,
340 VA_FRAME_PICTURE);
341 if (!vaapi_check_status(status, "vaPutSurface() canvas"))
342 return;
343
344 Renderer_agg_base::RenderImages::const_iterator img, first_img, last_img;
345 first_img = _agg_renderer->getFirstRenderImage();
346 last_img = _agg_renderer->getLastRenderImage();
347
348 if (first_img != last_img) {
349 for (img = first_img; img != last_img; ++img) {
350 std::shared_ptr<VaapiSurface> surface = (*img)->surface();
351
352 VaapiRectangle src_rect;
353 src_rect.x = (*img)->x();
354 src_rect.y = (*img)->y();
355 src_rect.width = (*img)->width();
356 src_rect.height = (*img)->height();
357
358 VaapiRectangle dst_rect;
359 const float xscale = _window_width / (float)_vaapi_image_width;
360 const float yscale = _window_height / (float)_vaapi_image_height;
361 dst_rect.x = src_rect.x * xscale;
362 dst_rect.y = src_rect.y * yscale;
363 dst_rect.width = src_rect.width * xscale;
364 dst_rect.height = src_rect.height * yscale;
365
366 VaapiVideoWindow *videoWindow;
367 videoWindow = getVideoWindow(surface, _drawing_area->window, dst_rect);
368 if (!videoWindow) {
369 log_error(_("failed to setup video window for surface 0x%08x."), surface->get());
370 continue;
371 }
372 videoWindow->moveResize(dst_rect);
373
374 VaapiRectangle pic_rect(surface->width(), surface->height());
375 if (!surface->associateSubpicture(_vaapi_subpicture, src_rect, pic_rect)) {
376 log_error(_("failed to associate subpicture to surface 0x%08x."), surface->get());
377 continue;
378 }
379
380 status = vaPutSurface(gvactx->display(),
381 surface->get(),
382 videoWindow->xid(),
383 0, 0, surface->width(), surface->height(),
384 0, 0, dst_rect.width, dst_rect.height,
385 NULL, 0,
386 VA_FRAME_PICTURE);
387 if (!vaapi_check_status(status, "vaPutSurface() video"))
388 continue;
389
390 surface->deassociateSubpicture(_vaapi_subpicture);
391 }
392
393 for (img = first_img; img != last_img; ++img) {
394 std::shared_ptr<VaapiSurface> surface = (*img)->surface();
395
396 status = vaSyncSurface(gvactx->display(), surface->get());
397 if (!vaapi_check_status(status, "vaSyncSurface() video"))
398 continue;
399 }
400 }
401 }
402
403 void
render(GdkRegion * const)404 GtkAggVaapiGlue::render(GdkRegion * const /*region*/)
405 {
406 render();
407 }
408
409 void
configure(GtkWidget * const,GdkEventConfigure * const event)410 GtkAggVaapiGlue::configure(GtkWidget *const /*widget*/, GdkEventConfigure *const event)
411 {
412 dprintf("GtkAggVaapiGlue::configure()\n");
413
414 if (_agg_renderer)
415 setRenderHandlerSize(event->width, event->height);
416
417 _window_is_setup = true;
418 }
419
420 } // namespace gnash
421
422