]> git.sesse.net Git - vlc/blob - modules/video_output/qte/qte.cpp
1be40e803ed836e56f9e6a9253e189ed229289f1
[vlc] / modules / video_output / qte / qte.cpp
1 /*****************************************************************************
2  * qte.cpp : QT Embedded plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 1998-2003 VideoLAN
5  * $Id: qte.cpp,v 1.22 2004/03/03 20:39:52 gbazin Exp $
6  *
7  * Authors: Gerald Hansink <gerald.hansink@ordain.nl>
8  *          Jean-Paul Saman <jpsaman@wxs.nl>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 /*****************************************************************************
30  * notes:
31  * - written for ipaq, so hardcoded assumptions specific for ipaq...
32  * - runs full screen
33  * - no "mouse events" handling
34  * - etc.
35  *****************************************************************************/
36
37 extern "C"
38 {
39 #include <errno.h>                                                 /* ENOMEM */
40 #include <stdlib.h>                                                /* free() */
41 #include <string.h>                                                /* strerror() */
42
43 #include <vlc/vlc.h>
44 #include <vlc/intf.h>
45 #include <vlc/vout.h>
46
47 #ifdef HAVE_MACHINE_PARAM_H
48     /* BSD */
49 #   include <machine/param.h>
50 #   include <sys/types.h>                                  /* typedef ushort */
51 #   include <sys/ipc.h>
52 #endif
53
54 #ifndef WIN32
55 #   include <netinet/in.h>                            /* BSD: struct in_addr */
56 #endif
57
58 #ifdef HAVE_SYS_SHM_H
59 #   include <sys/shm.h>                                /* shmget(), shmctl() */
60 #endif
61 } /* extern "C" */
62
63 #include <qapplication.h>
64 #include <qpainter.h>
65
66 #ifdef Q_WS_QWS
67 #   define USE_DIRECT_PAINTER
68 #   include <qdirectpainter_qws.h>
69 #   include <qgfxraster_qws.h>
70 #endif
71
72 extern "C"
73 {
74 #include "qte.h"
75
76 /*****************************************************************************
77  * Module descriptor
78  *****************************************************************************/
79 #define DISPLAY_TEXT N_("QT Embedded display name")
80 #define DISPLAY_LONGTEXT N_( \
81     "Specify the Qt Embedded hardware display you want to use. " \
82     "By default VLC will use the value of the DISPLAY environment variable.")
83
84 /*****************************************************************************
85  * Local prototypes
86  *****************************************************************************/
87 static int  Open      ( vlc_object_t * );
88 static void Close     ( vlc_object_t * );
89 static void Render    ( vout_thread_t *, picture_t * );
90 static void Display   ( vout_thread_t *, picture_t * );
91 static int  Manage    ( vout_thread_t * );
92 static int  Init      ( vout_thread_t * );
93 static void End       ( vout_thread_t * );
94
95 static int  OpenDisplay ( vout_thread_t * );
96 static void CloseDisplay( vout_thread_t * );
97
98 static int  NewPicture     ( vout_thread_t *, picture_t * );
99 static void FreePicture    ( vout_thread_t *, picture_t * );
100
101 static void ToggleFullScreen      ( vout_thread_t * );
102
103 static void RunQtThread( event_thread_t *p_event );
104 } /* extern "C" */
105
106 /*****************************************************************************
107 * Exported prototypes
108 *****************************************************************************/
109 extern "C"
110 {
111
112 vlc_module_begin();
113 //    add_category_hint( N_("QT Embedded"), NULL );
114 //    add_string( "qte-display", "landscape", NULL, DISPLAY_TEXT, DISPLAY_LONGTEXT);
115     set_description( _("QT Embedded video output") );
116     set_capability( "video output", 70 );
117     add_shortcut( "qte" );
118     set_callbacks( Open, Close);
119 vlc_module_end();
120
121 } /* extern "C" */
122
123 /*****************************************************************************
124  * Seeking function TODO: put this in a generic location !
125  *****************************************************************************/
126 static inline void vout_Seek( off_t i_seek )
127 {
128 }
129
130 /*****************************************************************************
131  * Open: allocate video thread output method
132  *****************************************************************************/
133 static int Open( vlc_object_t *p_this )
134 {
135     vout_thread_t * p_vout = (vout_thread_t *)p_this;
136
137     /* Allocate structure */
138     p_vout->p_sys = (struct vout_sys_t*) malloc( sizeof( struct vout_sys_t ) );
139
140     if( p_vout->p_sys == NULL )
141     {
142         msg_Err( p_vout, "out of memory" );
143         return( 1 );
144     }
145
146     p_vout->pf_init    = Init;
147     p_vout->pf_end     = End;
148     p_vout->pf_manage  = Manage;
149     p_vout->pf_render  = NULL; //Render;
150     p_vout->pf_display = Display;
151
152 #ifdef NEED_QTE_MAIN
153     p_vout->p_sys->p_qte_main =
154         module_Need( p_this, "gui-helper", "qte", VLC_TRUE );
155     if( p_vout->p_sys->p_qte_main == NULL )
156     {
157         free( p_vout->p_sys );
158         return VLC_ENOMOD;
159     }
160 #endif
161
162     if (OpenDisplay(p_vout))
163     {
164         msg_Err( p_vout, "Cannot set up qte video output" );
165         Close(p_this);
166         return( -1 );
167     }
168     return( 0 );
169 }
170
171 /*****************************************************************************
172  * CloseVideo: destroy Sys video thread output method
173  *****************************************************************************
174  * Terminate an output method created by Open
175  *****************************************************************************/
176 static void Close ( vlc_object_t *p_this )
177 {
178     vout_thread_t * p_vout = (vout_thread_t *)p_this;
179
180     msg_Dbg( p_vout, "close" );
181     if( p_vout->p_sys->p_event )
182     {
183         vlc_object_detach( p_vout->p_sys->p_event );
184
185         /* Kill RunQtThread */
186         p_vout->p_sys->p_event->b_die = VLC_TRUE;
187         CloseDisplay(p_vout);
188
189         vlc_thread_join( p_vout->p_sys->p_event );
190         vlc_object_destroy( p_vout->p_sys->p_event );
191     }
192
193 #ifdef NEED_QTE_MAIN
194     msg_Dbg( p_vout, "releasing gui-helper" );
195     module_Unneed( p_vout, p_vout->p_sys->p_qte_main );
196 #endif
197
198     if( p_vout->p_sys )
199     {
200         free( p_vout->p_sys );
201         p_vout->p_sys = NULL;
202     }
203 }
204
205 /*****************************************************************************
206  * Init: initialize video thread output method
207  *****************************************************************************
208  * This function create the buffers needed by the output thread. It is called
209  * at the beginning of the thread, but also each time the window is resized.
210  *****************************************************************************/
211 static int Init( vout_thread_t *p_vout )
212 {
213     int         i_index;
214     picture_t*  p_pic;
215     int         dd = QPixmap::defaultDepth();
216
217     I_OUTPUTPICTURES = 0;
218
219     p_vout->output.i_chroma = (dd == 16) ? VLC_FOURCC('R','V','1','6'): VLC_FOURCC('R','V','3','2');
220     p_vout->output.i_rmask  = 0xf800;
221     p_vout->output.i_gmask  = 0x07e0;
222     p_vout->output.i_bmask  = 0x001f;
223
224     /* All we have is an RGB image with square pixels */
225     p_vout->output.i_width  = p_vout->p_sys->i_width;
226     p_vout->output.i_height = p_vout->p_sys->i_height;
227     if( !p_vout->b_fullscreen )
228     {
229         p_vout->output.i_aspect = p_vout->output.i_width
230                                    * VOUT_ASPECT_FACTOR
231                                    / p_vout->output.i_height;
232     }
233     else
234     {
235         p_vout->output.i_aspect = p_vout->render.i_aspect;
236     }
237 #if 0
238     msg_Dbg( p_vout, "Init (h=%d,w=%d,aspect=%d)",p_vout->output.i_height,p_vout->output.i_width,p_vout->output.i_aspect );
239 #endif
240     /* Try to initialize MAX_DIRECTBUFFERS direct buffers */
241     while( I_OUTPUTPICTURES < QTE_MAX_DIRECTBUFFERS )
242     {
243         p_pic = NULL;
244
245         /* Find an empty picture slot */
246         for( i_index = 0 ; i_index < VOUT_MAX_PICTURES ; i_index++ )
247         {
248             if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
249             {
250                 p_pic = p_vout->p_picture + i_index;
251                 break;
252             }
253         }
254
255         /* Allocate the picture */
256         if( p_pic == NULL ||  NewPicture( p_vout, p_pic ) )
257         {
258             break;
259         }
260
261         p_pic->i_status = DESTROYED_PICTURE;
262         p_pic->i_type   = DIRECT_PICTURE;
263
264         PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
265
266         I_OUTPUTPICTURES++;
267     }
268
269     return( 0 );
270 }
271
272
273 /*****************************************************************************
274  * Render: render previously calculated output
275  *****************************************************************************/
276 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
277 {
278     ;
279 }
280
281 /*****************************************************************************
282  * Display: displays previously rendered output
283  *****************************************************************************
284  * This function sends the currently rendered image to screen.
285  *****************************************************************************/
286 static void Display( vout_thread_t *p_vout, picture_t *p_pic )
287 {
288     unsigned int x, y, w, h;
289
290     vout_PlacePicture( p_vout, p_vout->output.i_width, p_vout->output.i_height,
291                        &x, &y, &w, &h );
292 #if 0
293     msg_Dbg(p_vout, "+qte::Display( p_vout, i_width=%d, i_height=%d, x=%u, y=%u, w=%u, h=%u",
294         p_vout->output.i_width, p_vout->output.i_height, x, y, w, h );
295 #endif
296
297     if(p_vout->p_sys->p_VideoWidget)
298     {
299 // shameless borrowed from opie mediaplayer....
300 #ifndef USE_DIRECT_PAINTER
301         msg_Dbg(p_vout, "not using direct painter");
302         QPainter p(p_vout->p_sys->p_VideoWidget);
303
304         /* rotate frame */
305         int dd      = QPixmap::defaultDepth();
306         int bytes   = ( dd == 16 ) ? 2 : 4;
307         int rw = h, rh = w;
308
309         QImage rotatedFrame( rw, rh, bytes << 3 );
310
311         ushort* in  = (ushort*)p_pic->p_sys->pQImage->bits();
312         ushort* out = (ushort*)rotatedFrame.bits();
313
314         int spl = rotatedFrame.bytesPerLine() / bytes;
315         for (int x=0; x<h; x++)
316         {
317             if ( bytes == 2 )
318             {
319                 ushort* lout = out++ + (w - 1)*spl;
320                 for (int y=0; y<w; y++)
321                 {
322                     *lout=*in++;
323                     lout-=spl;
324                 }
325             }
326             else
327             {
328                 ulong* lout = ((ulong *)out)++ + (w - 1)*spl;
329                 for (int y=0; y<w; y++)
330                 {
331                     *lout=*((ulong*)in)++;
332                     lout-=spl;
333                 }
334             }
335         }
336
337         p.drawImage( x, y, rotatedFrame, 0, 0, rw, rh );
338 #else
339         QDirectPainter p(p_vout->p_sys->p_VideoWidget);
340         p.transformOrientation();
341         // just copy the image to the frame buffer...
342         memcpy(p.frameBuffer(), (p_pic->p_sys->pQImage->jumpTable())[0], h * p.lineStep());
343 #endif
344     }
345 }
346
347 /*****************************************************************************
348  * Manage: handle Qte events
349  *****************************************************************************
350  * This function should be called regularly by video output thread. It manages
351  * Qte events and allows window resizing. It returns a non null value on
352  * error.
353  *****************************************************************************/
354 static int Manage( vout_thread_t *p_vout )
355 {
356 //    msg_Dbg( p_vout, "Manage" );
357
358     /* Fullscreen change */
359     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
360     {
361         p_vout->b_fullscreen = ! p_vout->b_fullscreen;
362
363 //        p_vout->p_sys->b_cursor_autohidden = 0;
364 //        SDL_ShowCursor( p_vout->p_sys->b_cursor &&
365 //                        ! p_vout->p_sys->b_cursor_autohidden );
366
367         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
368         p_vout->i_changes |= VOUT_SIZE_CHANGE;
369     }
370
371     /*
372      * Size change
373      */
374     if( p_vout->i_changes & VOUT_SIZE_CHANGE )
375     {
376         msg_Dbg( p_vout, "video display resized (%dx%d)",
377                  p_vout->p_sys->i_width, p_vout->p_sys->i_height );
378
379         CloseDisplay( p_vout );
380         OpenDisplay( p_vout );
381
382         /* We don't need to signal the vout thread about the size change if
383          * we can handle rescaling ourselves */
384         p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
385     }
386
387     /* Pointer change */
388 //    if( ! p_vout->p_sys->b_cursor_autohidden &&
389 //        ( mdate() - p_vout->p_sys->i_lastmoved > 2000000 ) )
390 //    {
391 //        /* Hide the mouse automatically */
392 //        p_vout->p_sys->b_cursor_autohidden = 1;
393 //        SDL_ShowCursor( 0 );
394 //    }
395 //
396 //    if( p_vout->p_vlc->b_die )
397 //        p_vout->p_sys->bRunning = FALSE;
398
399     return 0;
400 }
401
402 /*****************************************************************************
403  * End: terminate video thread output method
404  *****************************************************************************
405  * Destroy the buffers created by vout_Init. It is called at the end of
406  * the thread, but also each time the window is resized.
407  *****************************************************************************/
408 static void End( vout_thread_t *p_vout )
409 {
410     int i_index;
411
412     /* Free the direct buffers we allocated */
413     for( i_index = I_OUTPUTPICTURES ; i_index ; )
414     {
415         i_index--;
416         FreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
417     }
418 }
419
420
421 /*****************************************************************************
422  * NewPicture: allocate a picture
423  *****************************************************************************
424  * Returns 0 on success, -1 otherwise
425  *****************************************************************************/
426 static int NewPicture( vout_thread_t *p_vout, picture_t *p_pic )
427 {
428     int dd = QPixmap::defaultDepth();
429
430     p_pic->p_sys = (picture_sys_t*) malloc( sizeof( picture_sys_t ) );
431     if( p_pic->p_sys == NULL )
432     {
433         return -1;
434     }
435
436     /* Create the display */
437     p_pic->p_sys->pQImage = new QImage(p_vout->output.i_width,
438                                        p_vout->output.i_height, dd );
439
440     if(p_pic->p_sys->pQImage == NULL)
441     {
442         return -1;
443     }
444
445     switch( dd )
446     {
447         case 8:
448             p_pic->p->i_pixel_pitch = 1;
449             break;
450         case 15:
451         case 16:
452             p_pic->p->i_pixel_pitch = 2;
453             break;
454         case 24:
455         case 32:
456             p_pic->p->i_pixel_pitch = 4;
457             break;
458         default:
459             return( -1 );
460     }
461
462     p_pic->p->p_pixels = (p_pic->p_sys->pQImage->jumpTable())[0];
463     p_pic->p->i_pitch = p_pic->p_sys->pQImage->bytesPerLine();
464     p_pic->p->i_lines = p_vout->output.i_height;
465     p_pic->p->i_visible_pitch =
466             p_pic->p->i_pixel_pitch * p_vout->output.i_width;
467
468     p_pic->i_planes = 1;
469
470     return 0;
471 }
472
473 /*****************************************************************************
474  * FreePicture: destroy a picture allocated with NewPicture
475  *****************************************************************************/
476 static void FreePicture( vout_thread_t *p_vout, picture_t *p_pic )
477 {
478     delete p_pic->p_sys->pQImage;
479 }
480
481 /*****************************************************************************
482  * ToggleFullScreen: Enable or disable full screen mode
483  *****************************************************************************
484  * This function will switch between fullscreen and window mode.
485  *
486  *****************************************************************************/
487 static void ToggleFullScreen ( vout_thread_t *p_vout )
488 {
489     if ( p_vout->b_fullscreen )
490        p_vout->p_sys->p_VideoWidget->showFullScreen();
491     else
492        p_vout->p_sys->p_VideoWidget->showNormal();
493
494     p_vout->b_fullscreen = !p_vout->b_fullscreen;
495 }
496
497 /*****************************************************************************
498  * OpenDisplay: create qte applicaton / window
499  *****************************************************************************
500  * Create a window according to video output given size, and set other
501  * properties according to the display properties.
502  *****************************************************************************/
503 static int OpenDisplay( vout_thread_t *p_vout )
504 {
505     /* for displaying the vout in a qt window we need the QtApplication */
506     p_vout->p_sys->p_QApplication = NULL;
507     p_vout->p_sys->p_VideoWidget = NULL;
508
509     p_vout->p_sys->p_event = (event_thread_t*) vlc_object_create( p_vout, sizeof(event_thread_t) );
510     p_vout->p_sys->p_event->p_vout = p_vout;
511
512     /* Initializations */
513 #if 1 /* FIXME: I need an event queue to handle video output size changes. */
514     p_vout->b_fullscreen = VLC_TRUE;
515 #endif
516
517     /* Set main window's size */
518     QWidget *desktop = p_vout->p_sys->p_QApplication->desktop();
519     p_vout->p_sys->i_width = p_vout->b_fullscreen ? desktop->height() :
520                                                     p_vout->i_window_width;
521     p_vout->p_sys->i_height = p_vout->b_fullscreen ? desktop->width() :
522                                                      p_vout->i_window_height;
523
524 #if 0 /* FIXME: I need an event queue to handle video output size changes. */
525     /* Update dimensions */
526     p_vout->i_changes |= VOUT_SIZE_CHANGE;
527     p_vout->i_window_width = p_vout->p_sys->i_width;
528     p_vout->i_window_height = p_vout->p_sys->i_height;
529 #endif
530
531     msg_Dbg( p_vout, "OpenDisplay (h=%d,w=%d)",p_vout->p_sys->i_height,p_vout->p_sys->i_width);
532
533     /* create thread to exec the qpe application */
534     if ( vlc_thread_create( p_vout->p_sys->p_event, "QT Embedded Thread",
535                             RunQtThread,
536                             VLC_THREAD_PRIORITY_OUTPUT, VLC_TRUE) )
537     {
538         msg_Err( p_vout, "cannot create QT Embedded Thread" );
539         vlc_object_destroy( p_vout->p_sys->p_event );
540         p_vout->p_sys->p_event = NULL;
541         return -1;
542     }
543
544     if( p_vout->p_sys->p_event->b_error )
545     {
546         msg_Err( p_vout, "RunQtThread failed" );
547         return -1;
548     }
549
550     vlc_object_attach( p_vout->p_sys->p_event, p_vout );
551     msg_Dbg( p_vout, "RunQtThread running" );
552
553     // just wait until the crew is complete...
554     while(p_vout->p_sys->p_VideoWidget == NULL)
555     {
556         msleep(1);
557     }
558     return VLC_SUCCESS;
559 }
560
561
562 /*****************************************************************************
563  * CloseDisplay: destroy the window
564  *****************************************************************************/
565 static void CloseDisplay( vout_thread_t *p_vout )
566 {
567     // quit qt application loop
568     msg_Dbg( p_vout, "destroying Qt Window" );
569 #ifdef NEED_QTE_MAIN
570     if(p_vout->p_sys->p_QApplication)
571     {
572         p_vout->p_sys->bRunning = FALSE;
573         while(p_vout->p_sys->p_VideoWidget)
574         {
575             msleep(1);
576         }
577     }
578 #else
579     if (p_vout->p_sys->p_QApplication)
580        p_vout->p_sys->p_QApplication->quit();
581 #endif
582 }
583
584 /*****************************************************************************
585  * main loop of qtapplication
586  *****************************************************************************/
587 static void RunQtThread(event_thread_t *p_event)
588 {
589     msg_Dbg( p_event->p_vout, "RunQtThread Starting" );
590
591 #ifdef NEED_QTE_MAIN
592     if (qApp)
593     {
594         p_event->p_vout->p_sys->p_QApplication = qApp;
595         p_event->p_vout->p_sys->bOwnsQApp = FALSE;
596         p_event->p_vout->p_sys->p_VideoWidget = qApp->mainWidget();
597         msg_Dbg( p_event->p_vout, "RunQtThread applicaton attached" );
598     }
599 #else
600     if (qApp==NULL)
601     {
602         int argc = 0;
603         QApplication* pApp = new QApplication(argc, NULL);
604         if(pApp)
605         {
606             p_event->p_vout->p_sys->p_QApplication = pApp;
607             p_event->p_vout->p_sys->bOwnsQApp = TRUE;
608         }
609         QWidget* pWidget = new QWidget();
610         if (pWidget)
611             {
612             p_event->p_vout->p_sys->p_VideoWidget = pWidget;
613         }
614     }
615 #endif
616     /* signal the creation of the window */
617     vlc_thread_ready( p_event );
618     msg_Dbg( p_event->p_vout, "RunQtThread ready" );
619
620     if (p_event->p_vout->p_sys->p_QApplication)
621     {
622         /* Set default window width and heigh to exactly preferred size. */
623             QWidget *desktop = p_event->p_vout->p_sys->p_QApplication->desktop();
624             p_event->p_vout->p_sys->p_VideoWidget->setMinimumWidth( 10 );
625              p_event->p_vout->p_sys->p_VideoWidget->setMinimumHeight( 10 );
626             p_event->p_vout->p_sys->p_VideoWidget->setBaseSize( p_event->p_vout->p_sys->i_width,
627             p_event->p_vout->p_sys->i_height );
628         p_event->p_vout->p_sys->p_VideoWidget->setMaximumWidth( desktop->width() );
629         p_event->p_vout->p_sys->p_VideoWidget->setMaximumHeight( desktop->height() );
630         /* Check on fullscreen */
631         if (p_event->p_vout->b_fullscreen)
632                   p_event->p_vout->p_sys->p_VideoWidget->showFullScreen();
633         else
634                 p_event->p_vout->p_sys->p_VideoWidget->showNormal();
635
636         p_event->p_vout->p_sys->p_VideoWidget->show();
637         p_event->p_vout->p_sys->bRunning = TRUE;
638
639 #ifdef NEED_QTE_MAIN
640         while(!p_event->b_die && p_event->p_vout->p_sys->bRunning)
641               {
642                /* Check if we are asked to exit */
643            if( p_event->b_die )
644                break;
645
646                msleep(100);
647             }
648 #else
649         // run the main loop of qtapplication until someone says: 'quit'
650         p_event->p_vout->p_sys->pcQApplication->exec();
651 #endif
652     }
653
654 #ifndef NEED_QTE_MAIN
655     if(p_event->p_vout->p_sys->p_QApplication)
656     {
657         delete p_event->p_vout->p_sys->p_VideoWidget;
658         p_event->p_vout->p_sys->p_VideoWidget = NULL;
659         delete p_event->p_vout->p_sys->p_QApplication;
660         p_event->p_vout->p_sys->p_QApplication = NULL;
661     }
662 #else
663     p_event->p_vout->p_sys->p_VideoWidget = NULL;
664 #endif
665
666     msg_Dbg( p_event->p_vout, "RunQtThread terminating" );
667 }
668