1 /* This is just a quick hack. But good enough for our use of displaying
2  * a short intro video when our game starts up - so might as well share
3  * it.
4  *
5  * Known bugs:
6  *
7  * - Only very crude synching. Audio is slightly delayed and some
8  *   videos seem to constantly drift off and then the audio gets all
9  *   distorted...
10  *
11  * - Seeking/Pausing doesn't really work.
12  *
13  * - Memory leaks. Easy to fix but don't have time right now.
14  *
15  * Missing features:
16  *
17  * - Stream information. For example allow selection of one of several
18  *   audio streams or subtitle overlay streams.
19  *
20  * - Non audio/video streams. For example something like:
21  *   ALLEGRO_USTR *al_get_video_subtitle(float *x, float *y);
22  *
23  * - Buffering. Right now buffering is hardcoded to a fixed size which
24  *   seemed enough for streaming 720p from disk in my tests. Obviously
25  *   when streaming higher bandwidth or from a source with high
26  *   fluctuation like an internet stream this won't work at all.
27  *
28  * - Provide an audio stream for the audio. Then could use this to
29  *   stream audio files. Right now opening an .mp3 with the video
30  *   addon will play it but only with the video API instead of Allegro's
31  *   normal audio streaming API...
32  *
33  * - Audio/Video sync. For a game user-controlled sync is probably not
34  *   too important as it can just ship with a properly synchronizeded
35  *   video. However right now the audio delay is completely ignored.
36  *
37  * - Additional drivers. Also redo the API a bit so not everything
38  *   has to be done by the driver.
39  */
40 
41 #include "allegro5/allegro5.h"
42 #include "allegro5/allegro_video.h"
43 #include "allegro5/internal/aintern_video.h"
44 #include "allegro5/internal/aintern_video_cfg.h"
45 #include "allegro5/internal/aintern_exitfunc.h"
46 
47 ALLEGRO_DEBUG_CHANNEL("video")
48 
49 
50 /* globals */
51 static bool video_inited = false;
52 
53 typedef struct VideoHandler {
54    struct VideoHandler *next;
55    const char *extension;
56    ALLEGRO_VIDEO_INTERFACE *vtable;
57 } VideoHandler;
58 
59 static VideoHandler *handlers;
60 
find_handler(const char * extension)61 static ALLEGRO_VIDEO_INTERFACE *find_handler(const char *extension)
62 {
63    VideoHandler *v = handlers;
64    while (v) {
65       if (!strcmp(extension, v->extension)) {
66          return v->vtable;
67       }
68       v = v->next;
69    }
70    return NULL;
71 }
72 
add_handler(const char * extension,ALLEGRO_VIDEO_INTERFACE * vtable)73 static void add_handler(const char *extension, ALLEGRO_VIDEO_INTERFACE *vtable)
74 {
75    VideoHandler *v;
76    if (handlers == NULL) {
77       handlers = al_calloc(1, sizeof(VideoHandler));
78       v = handlers;
79    }
80    else {
81       v = handlers;
82       while (v->next) {
83          v = v->next;
84       }
85       v->next = al_calloc(1, sizeof(VideoHandler));
86       v = v->next;
87    }
88    v->extension = extension;
89    v->vtable = vtable;
90 }
91 
92 /* Function: al_open_video
93  */
al_open_video(char const * filename)94 ALLEGRO_VIDEO *al_open_video(char const *filename)
95 {
96    ALLEGRO_VIDEO *video;
97    const char *extension = filename + strlen(filename) - 1;
98 
99    while ((extension >= filename) && (*extension != '.'))
100       extension--;
101    video = al_calloc(1, sizeof *video);
102 
103    video->vtable = find_handler(extension);
104 
105    if (video->vtable == NULL) {
106       ALLEGRO_ERROR("No handler for video extension %s - "
107          "therefore not trying to load %s.\n", extension, filename);
108       al_free(video);
109       return NULL;
110    }
111 
112    video->filename = al_create_path(filename);
113    video->playing = true;
114 
115    if (!video->vtable->open_video(video)) {
116       ALLEGRO_ERROR("Could not open %s.\n", filename);
117       al_destroy_path(video->filename);
118       al_free(video);
119       return NULL;
120    }
121 
122    al_init_user_event_source(&video->es);
123    video->es_inited = true;
124 
125    return video;
126 }
127 
128 /* Function: al_close_video
129  */
al_close_video(ALLEGRO_VIDEO * video)130 void al_close_video(ALLEGRO_VIDEO *video)
131 {
132    if (video) {
133       video->vtable->close_video(video);
134       if (video->es_inited) {
135          al_destroy_user_event_source(&video->es);
136       }
137       al_destroy_path(video->filename);
138       al_free(video);
139    }
140 }
141 
142 /* Function: al_get_video_event_source
143  */
al_get_video_event_source(ALLEGRO_VIDEO * video)144 ALLEGRO_EVENT_SOURCE *al_get_video_event_source(ALLEGRO_VIDEO *video)
145 {
146    ASSERT(video);
147    return &video->es;
148 }
149 
150 /* Function: al_start_video
151  */
al_start_video(ALLEGRO_VIDEO * video,ALLEGRO_MIXER * mixer)152 void al_start_video(ALLEGRO_VIDEO *video, ALLEGRO_MIXER *mixer)
153 {
154    ASSERT(video);
155 
156    /* XXX why is this not just a parameter? */
157    video->mixer = mixer;
158    video->vtable->start_video(video);
159 }
160 
161 /* Function: al_start_video_with_voice
162  */
al_start_video_with_voice(ALLEGRO_VIDEO * video,ALLEGRO_VOICE * voice)163 void al_start_video_with_voice(ALLEGRO_VIDEO *video, ALLEGRO_VOICE *voice)
164 {
165    ASSERT(video);
166 
167    /* XXX why is voice not just a parameter? */
168    video->voice = voice;
169    video->vtable->start_video(video);
170 }
171 
172 /* Function: al_set_video_playing
173  */
al_set_video_playing(ALLEGRO_VIDEO * video,bool play)174 void al_set_video_playing(ALLEGRO_VIDEO *video, bool play)
175 {
176    ASSERT(video);
177 
178    if (play != video->playing) {
179       video->playing = play;
180       video->vtable->set_video_playing(video);
181    }
182 }
183 
184 /* Function: al_is_video_playing
185  */
al_is_video_playing(ALLEGRO_VIDEO * video)186 bool al_is_video_playing(ALLEGRO_VIDEO *video)
187 {
188    ASSERT(video);
189 
190    return video->playing;
191 }
192 
193 /* Function: al_get_video_frame
194  */
al_get_video_frame(ALLEGRO_VIDEO * video)195 ALLEGRO_BITMAP *al_get_video_frame(ALLEGRO_VIDEO *video)
196 {
197    ASSERT(video);
198 
199    video->vtable->update_video(video);
200    return video->current_frame;
201 }
202 
203 /* Function: al_get_video_position
204  */
al_get_video_position(ALLEGRO_VIDEO * video,ALLEGRO_VIDEO_POSITION_TYPE which)205 double al_get_video_position(ALLEGRO_VIDEO *video, ALLEGRO_VIDEO_POSITION_TYPE which)
206 {
207    ASSERT(video);
208 
209    if (which == ALLEGRO_VIDEO_POSITION_VIDEO_DECODE)
210       return video->video_position;
211    if (which == ALLEGRO_VIDEO_POSITION_AUDIO_DECODE)
212       return video->audio_position;
213    return video->position;
214 }
215 
216 /* Function: al_seek_video
217  */
al_seek_video(ALLEGRO_VIDEO * video,double pos_in_seconds)218 bool al_seek_video(ALLEGRO_VIDEO *video, double pos_in_seconds)
219 {
220    ASSERT(video);
221 
222    return video->vtable->seek_video(video, pos_in_seconds);
223 }
224 
225 /* Function: al_get_video_audio_rate
226  */
al_get_video_audio_rate(ALLEGRO_VIDEO * video)227 double al_get_video_audio_rate(ALLEGRO_VIDEO *video)
228 {
229    ASSERT(video);
230    return video->audio_rate;
231 }
232 
233 /* Function: al_get_video_fps
234  */
al_get_video_fps(ALLEGRO_VIDEO * video)235 double al_get_video_fps(ALLEGRO_VIDEO *video)
236 {
237    ASSERT(video);
238    return video->fps;
239 }
240 
241 /* Function: al_get_video_scaled_width
242  */
al_get_video_scaled_width(ALLEGRO_VIDEO * video)243 float al_get_video_scaled_width(ALLEGRO_VIDEO *video)
244 {
245    ASSERT(video);
246    return video->scaled_width;
247 }
248 
249 /* Function: al_get_video_scaled_height
250  */
al_get_video_scaled_height(ALLEGRO_VIDEO * video)251 float al_get_video_scaled_height(ALLEGRO_VIDEO *video)
252 {
253    ASSERT(video);
254    return video->scaled_height;
255 }
256 
257 /* Function: al_init_video_addon
258  */
al_init_video_addon(void)259 bool al_init_video_addon(void)
260 {
261    if (video_inited)
262       return true;
263 
264 #ifdef ALLEGRO_CFG_VIDEO_HAVE_OGV
265    add_handler(".ogv", _al_video_ogv_vtable());
266 #endif
267 
268    if (handlers == NULL) {
269       ALLEGRO_WARN("No video handlers available!\n");
270       return false;
271    }
272 
273    _al_add_exit_func(al_shutdown_video_addon, "al_shutdown_video_addon");
274 
275    return true;
276 }
277 
278 
279 /* Function: al_is_video_addon_initialized
280  */
al_is_video_addon_initialized(void)281 bool al_is_video_addon_initialized(void)
282 {
283    return video_inited;
284 }
285 
286 
287 /* Function: al_shutdown_video_addon
288  */
al_shutdown_video_addon(void)289 void al_shutdown_video_addon(void)
290 {
291    if (!video_inited)
292       return;
293 
294    VideoHandler *v = handlers;
295    while (v) {
296       VideoHandler *next = v->next;
297       al_free(v);
298       v = next;
299    }
300    video_inited = false;
301    handlers = NULL;
302 }
303 
304 
305 /* Function: al_get_allegro_video_version
306  */
al_get_allegro_video_version(void)307 uint32_t al_get_allegro_video_version(void)
308 {
309    return ALLEGRO_VERSION_INT;
310 }
311 
312 /* The returned width and height are always greater than or equal to the frame
313  * width and height. */
_al_compute_scaled_dimensions(int frame_w,int frame_h,float aspect_ratio,float * scaled_w,float * scaled_h)314 void _al_compute_scaled_dimensions(int frame_w, int frame_h, float aspect_ratio,
315                                    float *scaled_w, float *scaled_h)
316 {
317    if (aspect_ratio > 1.0) {
318       *scaled_w = frame_h * aspect_ratio;
319       *scaled_h = frame_h;
320    }
321    else {
322       *scaled_w = frame_w;
323       *scaled_h = frame_w / aspect_ratio;
324    }
325 }
326 
327 /* vim: set sts=3 sw=3 et: */
328