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