1 // _________ __ __
2 // / _____// |_____________ _/ |______ ____ __ __ ______
3 // \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
4 // / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
5 // /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
6 // \/ \/ \//_____/ \/
7 // ______________________ ______________________
8 // T H E W A R B E G I N S
9 // Stratagus - A free fantasy real time strategy game engine
10 //
11 /**@name movie.cpp - Movie playback functions. */
12 //
13 // (c) Copyright 2005-2011 by Nehal Mistry, Jimmy Salmon and Pali Rohár
14 //
15 // This program is free software; you can redistribute it and/or modify
16 // it under the terms of the GNU General Public License as published by
17 // the Free Software Foundation; only version 2 of the License.
18 //
19 // This program is distributed in the hope that it will be useful,
20 // but WITHOUT ANY WARRANTY; without even the implied warranty of
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 // GNU General Public License for more details.
23 //
24 // You should have received a copy of the GNU General Public License
25 // along with this program; if not, write to the Free Software
26 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 // 02111-1307, USA.
28 //
29
30 //@{
31
32 #ifdef USE_THEORA
33
34 /*----------------------------------------------------------------------------
35 -- Includes
36 ----------------------------------------------------------------------------*/
37
38 #include "stratagus.h"
39
40 #include "movie.h"
41
42 #include "iocompat.h"
43 #include "iolib.h"
44 #include "network.h"
45 #include "sound.h"
46 #include "sound_server.h"
47 #include "video.h"
48
49 #include "SDL.h"
50
51 /*----------------------------------------------------------------------------
52 -- Defines
53 ----------------------------------------------------------------------------*/
54
55 /*----------------------------------------------------------------------------
56 -- Variables
57 ----------------------------------------------------------------------------*/
58
59 extern SDL_Surface *TheScreen;
60 static bool MovieStop;
61
62 /*----------------------------------------------------------------------------
63 -- Functions
64 ----------------------------------------------------------------------------*/
65
66 /**
67 ** Callbacks for movie input.
68 */
69
MovieCallbackButtonPressed(unsigned)70 static void MovieCallbackButtonPressed(unsigned)
71 {
72 MovieStop = true;
73 }
74
MovieCallbackButtonReleased(unsigned)75 static void MovieCallbackButtonReleased(unsigned)
76 {
77 }
78
MovieCallbackKeyPressed(unsigned,unsigned)79 static void MovieCallbackKeyPressed(unsigned, unsigned)
80 {
81 MovieStop = true;
82 }
83
84
MovieCallbackKeyReleased(unsigned,unsigned)85 static void MovieCallbackKeyReleased(unsigned, unsigned)
86 {
87 }
88
MovieCallbackKeyRepeated(unsigned,unsigned)89 static void MovieCallbackKeyRepeated(unsigned, unsigned)
90 {
91 }
92
MovieCallbackMouseMove(const PixelPos &)93 static void MovieCallbackMouseMove(const PixelPos &)
94 {
95 }
96
MovieCallbackMouseExit()97 static void MovieCallbackMouseExit()
98 {
99 }
100
101 /**
102 ** Draw Ogg data to the overlay
103 */
OutputTheora(OggData * data,SDL_Overlay * yuv_overlay,SDL_Rect * rect)104 static int OutputTheora(OggData *data, SDL_Overlay *yuv_overlay, SDL_Rect *rect)
105 {
106 yuv_buffer yuv;
107
108 theora_decode_YUVout(&data->tstate, &yuv);
109
110 if (SDL_MUSTLOCK(TheScreen)) {
111 if (SDL_LockSurface(TheScreen) < 0) {
112 return - 1;
113 }
114 }
115
116 if (SDL_LockYUVOverlay(yuv_overlay) < 0) {
117 return -1;
118 }
119
120 int crop_offset = data->tinfo.offset_x + yuv.y_stride * data->tinfo.offset_y;
121 for (int i = 0; i < yuv_overlay->h; ++i) {
122 memcpy(yuv_overlay->pixels[0] + yuv_overlay->pitches[0] * i,
123 yuv.y + crop_offset + yuv.y_stride * i, yuv_overlay->w);
124 }
125
126 crop_offset = (data->tinfo.offset_x / 2) + (yuv.uv_stride) *
127 (data->tinfo.offset_y / 2);
128 for (int i = 0; i < yuv_overlay->h / 2; ++i) {
129 memcpy(yuv_overlay->pixels[1] + yuv_overlay->pitches[1] * i,
130 yuv.v + yuv.uv_stride * i, yuv_overlay->w / 2);
131 memcpy(yuv_overlay->pixels[2] + yuv_overlay->pitches[2] * i,
132 yuv.u + crop_offset + yuv.uv_stride * i, yuv_overlay->w / 2);
133 }
134
135 if (SDL_MUSTLOCK(TheScreen)) {
136 SDL_UnlockSurface(TheScreen);
137 }
138 SDL_UnlockYUVOverlay(yuv_overlay);
139
140 SDL_DisplayYUVOverlay(yuv_overlay, rect);
141
142 return 0;
143 }
144
145 /**
146 ** Process Ogg data
147 */
TheoraProcessData(OggData * data)148 static int TheoraProcessData(OggData *data)
149 {
150 ogg_packet packet;
151
152 while (1) {
153 if (ogg_stream_packetout(&data->vstream, &packet) != 1) {
154 if (OggGetNextPage(&data->page, &data->sync, data->File)) {
155 // EOF
156 return -1;
157 }
158 ogg_stream_pagein(&data->vstream, &data->page);
159 } else {
160 theora_decode_packetin(&data->tstate, &packet);
161 return 0;
162 }
163 }
164 }
165
166
167 /**
168 ** Play a video file.
169 **
170 ** @param name Filename of movie file.
171 **
172 ** @return Non-zero if file isn't a supported movie.
173 */
PlayMovie(const std::string & name)174 int PlayMovie(const std::string &name)
175 {
176 int videoWidth, videoHeight;
177 #if defined(USE_OPENGL) || defined(USE_GLES)
178 videoWidth = Video.ViewportWidth;
179 videoHeight = Video.ViewportHeight;
180 #else
181 videoWidth = Video.Width;
182 videoHeight = Video.Height;
183 #endif
184
185 const std::string filename = LibraryFileName(name.c_str());
186
187 CFile f;
188 if (f.open(filename.c_str(), CL_OPEN_READ) == -1) {
189 fprintf(stderr, "Can't open file '%s'\n", name.c_str());
190 return 0;
191 }
192
193 OggData data;
194 memset(&data, 0, sizeof(data));
195 if (OggInit(&f, &data) || !data.video) {
196 OggFree(&data);
197 f.close();
198 return -1;
199 }
200
201 data.File = &f;
202 SDL_Rect rect;
203
204 if (data.tinfo.frame_width * 300 / 4 > data.tinfo.frame_height * 100) {
205 rect.w = videoWidth;
206 rect.h = videoWidth * data.tinfo.frame_height / data.tinfo.frame_width;
207 rect.x = 0;
208 rect.y = (videoHeight - rect.h) / 2;
209 } else {
210 rect.w = videoHeight * data.tinfo.frame_width / data.tinfo.frame_height;
211 rect.h = videoHeight;
212 rect.x = (videoWidth - rect.w) / 2;
213 rect.y = 0;
214 }
215
216 #ifdef USE_OPENGL
217 // When SDL_OPENGL is used, it is not possible to call SDL_CreateYUVOverlay, so turn temporary OpenGL off
218 // With GLES is all ok
219 if (UseOpenGL) {
220 SDL_SetVideoMode(Video.ViewportWidth, Video.ViewportHeight, Video.Depth, SDL_GetVideoSurface()->flags & ~SDL_OPENGL);
221 }
222 #endif
223
224 SDL_FillRect(SDL_GetVideoSurface(), nullptr, 0);
225 Video.ClearScreen();
226 SDL_Overlay *yuv_overlay = SDL_CreateYUVOverlay(data.tinfo.frame_width, data.tinfo.frame_height, SDL_YV12_OVERLAY, TheScreen);
227
228 if (yuv_overlay == nullptr) {
229 fprintf(stderr, "SDL_CreateYUVOverlay: %s\n", SDL_GetError());
230 OggFree(&data);
231 f.close();
232 return 0;
233 }
234
235 StopMusic();
236 CSample *sample = LoadVorbis(filename.c_str(), PlayAudioStream);
237 if (sample) {
238 if ((sample->Channels != 1 && sample->Channels != 2) || sample->SampleSize != 16) {
239 fprintf(stderr, "Unsupported sound format in movie\n");
240 delete sample;
241 SDL_FreeYUVOverlay(yuv_overlay);
242 OggFree(&data);
243 f.close();
244 return 0;
245 }
246 PlayMusic(sample);
247 }
248
249 EventCallback callbacks;
250
251 callbacks.ButtonPressed = MovieCallbackButtonPressed;
252 callbacks.ButtonReleased = MovieCallbackButtonReleased;
253 callbacks.MouseMoved = MovieCallbackMouseMove;
254 callbacks.MouseExit = MovieCallbackMouseExit;
255 callbacks.KeyPressed = MovieCallbackKeyPressed;
256 callbacks.KeyReleased = MovieCallbackKeyReleased;
257 callbacks.KeyRepeated = MovieCallbackKeyRepeated;
258 callbacks.NetworkEvent = NetworkEvent;
259
260 const EventCallback *old_callbacks = GetCallbacks();
261 SetCallbacks(&callbacks);
262
263 Invalidate();
264 RealizeVideoMemory();
265
266 MovieStop = false;
267 const unsigned int start_ticks = SDL_GetTicks();
268 bool need_data = true;
269 while (!MovieStop) {
270 if (need_data) {
271 if (TheoraProcessData(&data)) {
272 break;
273 }
274 need_data = false;
275 }
276
277 const int diff = SDL_GetTicks() - start_ticks
278 - static_cast<int>(theora_granule_time(&data.tstate, data.tstate.granulepos) * 1000);
279
280 if (diff > 100) {
281 // too far behind, skip some frames
282 need_data = true;
283 continue;
284 }
285 if (diff > 0) {
286 OutputTheora(&data, yuv_overlay, &rect);
287 need_data = true;
288 }
289
290 WaitEventsOneFrame();
291 }
292
293 StopMusic();
294 SDL_FreeYUVOverlay(yuv_overlay);
295
296 OggFree(&data);
297 f.close();
298
299 #ifdef USE_OPENGL
300 if (UseOpenGL) {
301 SDL_SetVideoMode(Video.ViewportWidth, Video.ViewportHeight, Video.Depth, SDL_GetVideoSurface()->flags | SDL_OPENGL);
302 ReloadOpenGL();
303 }
304 #endif
305
306 SetCallbacks(old_callbacks);
307
308 return 0;
309 }
310
311 #else
312
313 #include <string>
314 #include <string.h>
315 #include <stdio.h>
316
317 /**
318 ** Play a video file.
319 **
320 ** @param name Filename of movie file.
321 **
322 ** @return Non-zero if file isn't a supported movie.
323 */
PlayMovie(const std::string & name)324 int PlayMovie(const std::string &name)
325 {
326 if (strstr(name.c_str(), ".ogg") || strstr(name.c_str(), ".ogv")) {
327 fprintf(stderr, "PlayMovie() '%s' is not supported, please recompile stratagus with theora support\n", name.c_str());
328 return 0;
329 }
330 return -1;
331 }
332
333 #endif
334
335 //@}
336