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