optimizations and refactoring to get a small (1-2%) speed gain
[divverent/darkplaces.git] / cl_video.c
1
2 #include "quakedef.h"
3 #include "cl_video.h"
4 #include "dpvsimpledecode.h"
5
6 // constants (and semi-constants)
7 static int  cl_videormask;
8 static int  cl_videobmask;
9 static int  cl_videogmask;
10 static int      cl_videobytesperpixel;
11
12 static int cl_activevideos;
13 static clvideo_t videoarray[ MAXCLVIDEOS ];
14 static rtexturepool_t *cl_videotexturepool;
15
16 static qboolean OpenStream( clvideo_t * video )
17 {
18         char *errorstring;
19         video->stream = dpvsimpledecode_open( video->filename, &errorstring);
20         if (!video->stream )
21         {
22                 Con_Printf("unable to open \"%s\", error: %s\n", video->filename, errorstring);
23                 return false;
24         }
25         return true;
26 }
27
28 static void SuspendVideo( clvideo_t * video )
29 {
30         if( video->suspended )
31                 return;
32         video->suspended = true;
33         // free the texture
34         R_FreeTexture( video->cpif.tex );
35         // free the image data
36         Mem_Free( video->imagedata );
37         // if we are in firstframe mode, also close the stream
38         if( video->state == CLVIDEO_FIRSTFRAME )
39                 dpvsimpledecode_close( video->stream );
40 }
41
42 static qboolean WakeVideo( clvideo_t * video )
43 {
44         if( !video->suspended )
45                 return true;
46         video->suspended = false;
47
48         if( video->state == CLVIDEO_FIRSTFRAME )
49                 if( !OpenStream( video ) ) {
50                         video->state = CLVIDEO_UNUSED;
51                         return false;
52                 }
53
54         video->imagedata = Mem_Alloc( cl_mempool, video->cpif.width * video->cpif.height * cl_videobytesperpixel );
55         video->cpif.tex = R_LoadTexture2D( cl_videotexturepool, video->cpif.name,
56                 video->cpif.width, video->cpif.height, NULL, TEXTYPE_RGBA, 0, NULL );
57
58         // update starttime
59         video->starttime += realtime - video->lasttime;
60
61         return true;
62 }
63
64 clvideo_t* CL_OpenVideo( const char *filename, const char *name, int owner, qboolean cinematic )
65 {
66         int i;
67         clvideo_t *video;
68
69         if (cinematic)
70         {
71                 video = videoarray;
72                 i = 0;
73         }
74         else
75         {
76                 for (i = 1, video = videoarray; i < cl_activevideos;i++, video++)
77                         if (videoarray[i].state == CLVIDEO_UNUSED)
78                                 break;
79                 if (i == MAXCLVIDEOS)
80                 {
81                         Con_Printf( "unable to open video \"%s\" - video limit reached\n", filename );
82                         return NULL;
83                 }
84         }
85
86         strncpy( video->filename, filename, MAX_QPATH );
87         video->ownertag = owner;
88
89         if( strncmp( name, CLVIDEOPREFIX, sizeof( CLVIDEOPREFIX ) - 1 ) )
90                 return NULL;
91
92         strncpy( video->cpif.name, name, MAX_QPATH );
93
94         if( !OpenStream( video ) )
95                 return NULL;
96
97         video->state = CLVIDEO_FIRSTFRAME;
98         video->framenum = -1;
99         video->framerate = dpvsimpledecode_getframerate( video->stream );
100         video->lasttime = realtime;
101
102         video->cpif.width = dpvsimpledecode_getwidth( video->stream );
103         video->cpif.height = dpvsimpledecode_getheight( video->stream );
104         video->cpif.tex = R_LoadTexture2D( cl_videotexturepool, video->cpif.name, video->cpif.width, video->cpif.height, NULL, TEXTYPE_RGBA, 0, NULL );
105
106         video->imagedata = Mem_Alloc( cl_mempool, video->cpif.width * video->cpif.height * cl_videobytesperpixel );
107
108         // expand the active range to include the new entry
109         cl_activevideos = max(cl_activevideos, i + 1);
110         return video;
111 }
112
113 clvideo_t* CL_GetVideo( const char *name )
114 {
115         int i;
116         clvideo_t *video;
117
118         for( i = 0 ; i < cl_activevideos ; i++ )
119                 if( videoarray[ i ].state != CLVIDEO_UNUSED
120                         &&      !strcmp( videoarray[ i ].cpif.name , name ) )
121                         break;
122         if( i == cl_activevideos )
123                 return NULL;
124         video = &videoarray[ i ];
125
126         if( video->suspended )
127         {
128                 if( !WakeVideo( video ) )
129                         return NULL;
130                 else if( video->state == CLVIDEO_RESETONWAKEUP )
131                         video->framenum = -1;
132         }
133
134         video->lasttime = realtime;
135
136         return video;
137 }
138
139 void CL_SetVideoState( clvideo_t *video, clvideostate_t state )
140 {
141         if( !video )
142                 return;
143
144         video->lasttime = realtime;
145         video->state = state;
146         if( state == CLVIDEO_FIRSTFRAME )
147                 CL_RestartVideo( video );
148 }
149
150 void CL_RestartVideo( clvideo_t *video )
151 {
152         if( !video )
153                 return;
154
155         video->starttime = video->lasttime = realtime;
156         video->framenum = -1;
157
158         dpvsimpledecode_close( video->stream );
159         if( !OpenStream( video ) )
160                 video->state = CLVIDEO_UNUSED;
161 }
162
163 void CL_CloseVideo( clvideo_t * video )
164 {
165         if( !video || video->state == CLVIDEO_UNUSED )
166                 return;
167
168         if( !video->suspended || video->state != CLVIDEO_FIRSTFRAME )
169                 dpvsimpledecode_close( video->stream );
170         if( !video->suspended ) {
171                 Mem_Free( video->imagedata );
172                 R_FreeTexture( video->cpif.tex );
173         }
174
175         video->state = CLVIDEO_UNUSED;
176 }
177
178 static void VideoFrame( clvideo_t *video )
179 {
180         int destframe;
181
182         if( video->state == CLVIDEO_FIRSTFRAME )
183                 destframe = 0;
184         else
185                 destframe = (realtime - video->starttime) * video->framerate;
186         if( destframe < 0 )
187                 destframe = 0;
188         if( video->framenum < destframe ) {
189                 do {
190                         video->framenum++;
191                         if( dpvsimpledecode_video( video->stream, video->imagedata, cl_videormask,
192                                 cl_videogmask, cl_videobmask, cl_videobytesperpixel,
193                                 cl_videobytesperpixel * video->cpif.width )
194                                 ) { // finished?
195                                 CL_RestartVideo( video );
196                                 if( video->state == CLVIDEO_PLAY )
197                                                 video->state = CLVIDEO_FIRSTFRAME;
198                                 return;
199                         }
200                 } while( video->framenum < destframe );
201                 R_UpdateTexture( video->cpif.tex, (unsigned char *)video->imagedata );
202         }
203 }
204
205 void CL_VideoFrame( void ) // update all videos
206 {
207         int i;
208         clvideo_t *video;
209
210         for( video = videoarray, i = 0 ; i < cl_activevideos ; video++, i++ )
211                 if( video->state != CLVIDEO_UNUSED && !video->suspended )
212                 {
213                         if( realtime - video->lasttime > CLTHRESHOLD )
214                                 SuspendVideo( video );
215                         else if( video->state == CLVIDEO_PAUSE )
216                                 video->starttime = realtime - video->framenum * video->framerate;
217                         else
218                                 VideoFrame( video );
219                 }
220
221         if( videoarray->state == CLVIDEO_FIRSTFRAME )
222                 CL_VideoStop();
223
224         // reduce range to exclude unnecessary entries
225         while (cl_activevideos > 0 && videoarray[cl_activevideos-1].state == CLVIDEO_UNUSED)
226                 cl_activevideos--;
227 }
228
229 void CL_Video_Shutdown( void )
230 {
231         int i;
232         for( i = 0 ; i < cl_activevideos ; i++ )
233                 CL_CloseVideo( &videoarray[ i ] );
234 }
235
236 void CL_PurgeOwner( int owner )
237 {
238         int i;
239         for( i = 0 ; i < cl_activevideos ; i++ )
240                 if( videoarray[ i ].ownertag == owner )
241                         CL_CloseVideo( &videoarray[ i ] );
242 }
243
244 int cl_videoplaying = false; // old, but still supported
245
246 void CL_DrawVideo(void)
247 {
248         if (cl_videoplaying)
249                 DrawQ_Pic(0, 0, videoarray->cpif.name, vid_conwidth.integer, vid_conheight.integer, 1, 1, 1, 1, 0);
250 }
251
252 void CL_VideoStart(char *filename)
253 {
254         Host_StartVideo();
255
256         if( videoarray->state != CLVIDEO_UNUSED )
257                 CL_CloseVideo( videoarray );
258         if( !CL_OpenVideo( filename, va( CLVIDEOPREFIX "%s", filename ), 0, true ) )
259                 return;
260
261         cl_videoplaying = true;
262
263         CL_SetVideoState( videoarray, CLVIDEO_PLAY );
264         CL_RestartVideo( videoarray );
265 }
266
267 void CL_VideoStop(void)
268 {
269         cl_videoplaying = false;
270
271         CL_CloseVideo( videoarray );
272 }
273
274 static void CL_PlayVideo_f(void)
275 {
276         char name[MAX_QPATH];
277
278         Host_StartVideo();
279
280         if (Cmd_Argc() != 2)
281         {
282                 Con_Print("usage: playvideo <videoname>\nplays video named video/<videoname>.dpv\n");
283                 return;
284         }
285
286         sprintf(name, "video/%s.dpv", Cmd_Argv(1));
287         CL_VideoStart(name);
288 }
289
290 static void CL_StopVideo_f(void)
291 {
292         CL_VideoStop();
293 }
294
295 static void cl_video_start( void )
296 {
297         int i;
298         clvideo_t *video;
299
300         cl_videotexturepool = R_AllocTexturePool();
301
302         for( video = videoarray, i = 0 ; i < cl_activevideos ; i++, video++ )
303                 if( video->state != CLVIDEO_UNUSED && !video->suspended )
304                         video->cpif.tex = R_LoadTexture2D( cl_videotexturepool, video->cpif.name,
305                                 video->cpif.width, video->cpif.height, NULL, TEXTYPE_RGBA, 0, NULL );
306 }
307
308 static void cl_video_shutdown( void )
309 {
310         R_FreeTexturePool( &cl_videotexturepool );
311 }
312
313 static void cl_video_newmap( void )
314 {
315 }
316
317 void CL_Video_Init( void )
318 {
319         cl_activevideos = 0;
320         cl_videobytesperpixel = 4;
321         cl_videormask = BigLong(0xFF000000);
322         cl_videogmask = BigLong(0x00FF0000);
323         cl_videobmask = BigLong(0x0000FF00);
324
325         Cmd_AddCommand( "playvideo", CL_PlayVideo_f, "play a .dpv video file" );
326         Cmd_AddCommand( "stopvideo", CL_StopVideo_f, "stop playing a .dpv video file" );
327
328         R_RegisterModule( "CL_Video", cl_video_start, cl_video_shutdown, cl_video_newmap );
329 }
330