1 #include "SDL.h"
2
3 #include "MPEG.h"
4
5 #ifdef WIN32
6 #include <io.h>
7 #else
8 #include <unistd.h>
9 #endif
10 #include <fcntl.h>
11 #include <string.h>
12 #include <errno.h>
13
14 #ifndef O_BINARY
15 #define O_BINARY 0
16 #endif
17
MPEG(const char * name,bool SDLaudio)18 MPEG::MPEG(const char * name, bool SDLaudio) :
19 MPEGerror()
20 {
21 SDL_RWops *source;
22
23 mpeg_mem = 0;
24
25 source = SDL_RWFromFile(name, "rb");
26 if (!source) {
27 InitErrorState();
28 SetError(SDL_GetError());
29 return;
30 }
31 Init(source, SDLaudio);
32 }
33
MPEG(int Mpeg_FD,bool SDLaudio)34 MPEG::MPEG(int Mpeg_FD, bool SDLaudio) :
35 MPEGerror()
36 {
37 SDL_RWops *source;
38
39 mpeg_mem = 0;
40
41 // *** FIXME we're leaking a bit of memory for the FILE *
42 // best solution would be to have SDL_RWFromFD
43 FILE *file = fdopen(Mpeg_FD, "rb");
44 if (!file) {
45 InitErrorState();
46 SetError(strerror(errno));
47 return;
48 }
49
50 source = SDL_RWFromFP(file,false);
51 if (!source) {
52 InitErrorState();
53 SetError(SDL_GetError());
54 return;
55 }
56 Init(source, SDLaudio);
57 }
58
MPEG(void * data,int size,bool SDLaudio)59 MPEG::MPEG(void *data, int size, bool SDLaudio) :
60 MPEGerror()
61 {
62 SDL_RWops *source;
63
64 // The semantics are that the data passed in should be copied
65 // (?)
66 mpeg_mem = new char[size];
67 memcpy(mpeg_mem, data, size);
68
69 source = SDL_RWFromMem(mpeg_mem, size);
70 if (!source) {
71 InitErrorState();
72 SetError(SDL_GetError());
73 return;
74 }
75 Init(source, SDLaudio);
76 }
77
MPEG(SDL_RWops * mpeg_source,bool SDLaudio)78 MPEG::MPEG(SDL_RWops *mpeg_source, bool SDLaudio) :
79 MPEGerror()
80 {
81 mpeg_mem = 0;
82 Init(mpeg_source, SDLaudio);
83 }
84
Init(SDL_RWops * mpeg_source,bool SDLaudio)85 void MPEG::Init(SDL_RWops *mpeg_source, bool SDLaudio)
86 {
87 source = mpeg_source;
88 sdlaudio = SDLaudio;
89
90 /* Create the system that will parse the MPEG stream */
91 system = new MPEGsystem(source);
92
93 /* Initialize everything to invalid values for cleanup */
94 error = NULL;
95
96 audiostream = videostream = NULL;
97 audioaction = NULL;
98 videoaction = NULL;
99 audio = NULL;
100 video = NULL;
101 audioaction_enabled = videoaction_enabled = false;
102 loop = false;
103 pause = false;
104
105 parse_stream_list();
106
107 EnableAudio(audioaction_enabled);
108 EnableVideo(videoaction_enabled);
109
110 if ( ! audiostream && ! videostream ) {
111 SetError("No audio/video stream found in MPEG");
112 }
113
114 if ( system && system->WasError() ) {
115 SetError(system->TheError());
116 }
117
118 if ( audio && audio->WasError() ) {
119 SetError(audio->TheError());
120 }
121
122 if ( video && video->WasError() ) {
123 SetError(video->TheError());
124 }
125
126 if ( WasError() ) {
127 SetError(TheError());
128 }
129 }
130
InitErrorState()131 void MPEG::InitErrorState() {
132 audio = NULL;
133 video = NULL;
134 system = NULL;
135 error = NULL;
136
137 audiostream = videostream = NULL;
138 audioaction = NULL;
139 videoaction = NULL;
140 audio = NULL;
141 video = NULL;
142 audioaction_enabled = videoaction_enabled = false;
143 loop = false;
144 pause = false;
145 }
146
~MPEG()147 MPEG::~MPEG()
148 {
149 Stop();
150 if(video) delete video;
151 if(audio) delete audio;
152 if(system) delete system;
153
154 if(source) SDL_RWclose(source);
155 if ( mpeg_mem )
156 delete[] mpeg_mem;
157 }
158
AudioEnabled(void)159 bool MPEG::AudioEnabled(void) {
160 return(audioaction_enabled);
161 }
EnableAudio(bool enabled)162 void MPEG::EnableAudio(bool enabled) {
163 if ( enabled && ! audioaction ) {
164 enabled = false;
165 }
166 audioaction_enabled = enabled;
167
168 /* Stop currently playing stream, if necessary */
169 if ( audioaction && ! audioaction_enabled ) {
170 audioaction->Stop();
171 }
172 /* Set the video time source */
173 if ( videoaction ) {
174 if ( audioaction_enabled ) {
175 videoaction->SetTimeSource(audioaction);
176 } else {
177 videoaction->SetTimeSource(NULL);
178 }
179 }
180 if(audiostream)
181 audiostream->enable(enabled);
182 }
VideoEnabled(void)183 bool MPEG::VideoEnabled(void) {
184 return(videoaction_enabled);
185 }
EnableVideo(bool enabled)186 void MPEG::EnableVideo(bool enabled) {
187 if ( enabled && ! videoaction ) {
188 enabled = false;
189 }
190 videoaction_enabled = enabled;
191
192 /* Stop currently playing stream, if necessary */
193 if ( videoaction && ! videoaction_enabled ) {
194 videoaction->Stop();
195 }
196 if(videostream)
197 videostream->enable(enabled);
198 }
199
200 /* MPEG actions */
Loop(bool toggle)201 void MPEG::Loop(bool toggle) {
202 loop = toggle;
203 }
Play(void)204 void MPEG::Play(void) {
205 if ( AudioEnabled() ) {
206 audioaction->Play();
207 }
208 if ( VideoEnabled() ) {
209 videoaction->Play();
210 }
211 }
Stop(void)212 void MPEG::Stop(void) {
213 if ( VideoEnabled() ) {
214 videoaction->Stop();
215 }
216 if ( AudioEnabled() ) {
217 audioaction->Stop();
218 }
219 }
220
Rewind(void)221 void MPEG::Rewind(void) {
222 seekIntoStream(0);
223 }
224
Pause(void)225 void MPEG::Pause(void) {
226 pause = !pause;
227
228 if ( VideoEnabled() ) {
229 videoaction->Pause();
230 }
231 if ( AudioEnabled() ) {
232 audioaction->Pause();
233 }
234 }
235
236 /* Michel Darricau from eProcess <mdarricau@eprocess.fr> conflict name with popcorn */
GetStatus(void)237 MPEGstatus MPEG::GetStatus(void) {
238 MPEGstatus status;
239
240 status = MPEG_STOPPED;
241 if ( VideoEnabled() ) {
242 /* Michel Darricau from eProcess <mdarricau@eprocess.fr> conflict name with popcorn */
243 switch (videoaction->GetStatus()) {
244 case MPEG_PLAYING:
245 status = MPEG_PLAYING;
246 break;
247 default:
248 break;
249 }
250 }
251 if ( AudioEnabled() ) {
252 /* Michel Darricau from eProcess <mdarricau@eprocess.fr> conflict name with popcorn */
253 switch (audioaction->GetStatus()) {
254 case MPEG_PLAYING:
255 status = MPEG_PLAYING;
256 break;
257 default:
258 break;
259 }
260 }
261
262 if(status == MPEG_STOPPED && loop && !pause)
263 {
264 /* Here we go again */
265 Rewind();
266 Play();
267
268 if ( VideoEnabled() ) {
269 /* Michel Darricau from eProcess <mdarricau@eprocess.fr> conflict name with popcorn */
270 switch (videoaction->GetStatus()) {
271 case MPEG_PLAYING:
272 status = MPEG_PLAYING;
273 break;
274 default:
275 break;
276 }
277 }
278 if ( AudioEnabled() ) {
279 /* Michel Darricau from eProcess <mdarricau@eprocess.fr> conflict name with popcorn */
280 switch (audioaction->GetStatus()) {
281 case MPEG_PLAYING:
282 status = MPEG_PLAYING;
283 break;
284 default:
285 break;
286 }
287 }
288 }
289
290 return(status);
291 }
292
293
294 /* MPEG audio actions */
GetAudioInfo(MPEG_AudioInfo * info)295 bool MPEG::GetAudioInfo(MPEG_AudioInfo *info) {
296 if ( AudioEnabled() ) {
297 return(audioaction->GetAudioInfo(info));
298 }
299 return(false);
300 }
Volume(int vol)301 void MPEG::Volume(int vol) {
302 if ( AudioEnabled() ) {
303 audioaction->Volume(vol);
304 }
305 }
WantedSpec(SDL_AudioSpec * wanted)306 bool MPEG::WantedSpec(SDL_AudioSpec *wanted) {
307 if( audiostream ) {
308 return(GetAudio()->WantedSpec(wanted));
309 }
310 return(false);
311 }
ActualSpec(const SDL_AudioSpec * actual)312 void MPEG::ActualSpec(const SDL_AudioSpec *actual) {
313 if( audiostream ) {
314 GetAudio()->ActualSpec(actual);
315 }
316 }
GetAudio(void)317 MPEGaudio *MPEG::GetAudio(void) { // Simple accessor used in the C interface
318 return audio;
319 }
320
321 /* MPEG video actions */
GetVideoInfo(MPEG_VideoInfo * info)322 bool MPEG::GetVideoInfo(MPEG_VideoInfo *info) {
323 if ( VideoEnabled() ) {
324 return(videoaction->GetVideoInfo(info));
325 }
326 return(false);
327 }
SetDisplay(SDL_Surface * dst,SDL_mutex * lock,MPEG_DisplayCallback callback)328 bool MPEG::SetDisplay(SDL_Surface *dst, SDL_mutex *lock,
329 MPEG_DisplayCallback callback) {
330 if ( VideoEnabled() ) {
331 return(videoaction->SetDisplay(dst, lock, callback));
332 }
333 return(false);
334 }
MoveDisplay(int x,int y)335 void MPEG::MoveDisplay(int x, int y) {
336 if ( VideoEnabled() ) {
337 videoaction->MoveDisplay(x, y);
338 }
339 }
ScaleDisplayXY(int w,int h)340 void MPEG::ScaleDisplayXY(int w, int h) {
341 if ( VideoEnabled() ) {
342 videoaction->ScaleDisplayXY(w, h);
343 }
344 }
SetDisplayRegion(int x,int y,int w,int h)345 void MPEG::SetDisplayRegion(int x, int y, int w, int h) {
346 if ( VideoEnabled() ) {
347 videoaction->SetDisplayRegion(x, y, w, h);
348 }
349 }
RenderFrame(int frame)350 void MPEG::RenderFrame(int frame)
351 {
352 if ( VideoEnabled() ) {
353 videoaction->RenderFrame(frame);
354 }
355 }
RenderFinal(SDL_Surface * dst,int x,int y)356 void MPEG::RenderFinal(SDL_Surface *dst, int x, int y)
357 {
358 Stop();
359 if ( VideoEnabled() ) {
360 videoaction->RenderFinal(dst, x, y);
361 }
362 Rewind();
363 }
364
Filter(SMPEG_Filter * filter)365 SMPEG_Filter * MPEG::Filter(SMPEG_Filter * filter)
366 {
367 if ( VideoEnabled() ) {
368 return(videoaction->Filter(filter));
369 }
370 return 0;
371 }
372
Seek(int position)373 void MPEG::Seek(int position)
374 {
375 int was_playing = 0;
376
377 /* Cannot seek past end of file */
378 if((Uint32)position > system->TotalSize()) return;
379
380 /* Michel Darricau from eProcess <mdarricau@eprocess.fr> conflict name with popcorn */
381 /* get info whrether we need to restart playing at the end */
382 if( GetStatus() == MPEG_PLAYING )
383 was_playing = 1;
384
385 if(!seekIntoStream(position)) return;
386
387 /* If we were playing and not rewind then play again */
388 if (was_playing)
389 Play();
390
391 if (VideoEnabled() && !was_playing)
392 videoaction->RenderFrame(0);
393
394 if ( pause && VideoEnabled() ) {
395 videoaction->Pause();
396 }
397 if ( pause && AudioEnabled() ) {
398 audioaction->Pause();
399 }
400 }
401
seekIntoStream(int position)402 bool MPEG::seekIntoStream(int position)
403 {
404 /* First we stop everything */
405 Stop();
406
407 /* Go to the desired position into file */
408 if(!system->Seek(position)) return(false);
409
410 /* Seek first aligned data */
411 if(audiostream && audioaction_enabled)
412 while(audiostream->time() == -1)
413 if ( ! audiostream->next_packet() ) return false;
414 if(videostream && videoaction_enabled)
415 while(videostream->time() == -1)
416 if ( ! videostream->next_packet() ) return false;
417
418 /* Calculating current play time on audio only makes sense when there
419 is no video */
420 if ( audioaction && !videoaction) {
421 audioaction->Rewind();
422 audioaction->ResetSynchro(system->TimeElapsedAudio(position));
423 }
424 /* And forget what we previouly buffered */
425 else if ( audioaction ) {
426 audioaction->Rewind();
427 audioaction->ResetSynchro(audiostream->time());
428 }
429 if ( videoaction ) {
430 videoaction->Rewind();
431 videoaction->ResetSynchro(videostream->time());
432 }
433
434 return(true);
435 }
436
Skip(float seconds)437 void MPEG::Skip(float seconds)
438 {
439 if(system->get_stream(SYSTEM_STREAMID))
440 {
441 system->Skip(seconds);
442 }
443 else
444 {
445 /* No system information in MPEG */
446 if( VideoEnabled() ) videoaction->Skip(seconds);
447 if( AudioEnabled() ) audioaction->Skip(seconds);
448 }
449 }
450
GetSystemInfo(MPEG_SystemInfo * sinfo)451 void MPEG::GetSystemInfo(MPEG_SystemInfo * sinfo)
452 {
453 sinfo->total_size = system->TotalSize();
454 sinfo->current_offset = system->Tell();
455 sinfo->total_time = system->TotalTime();
456
457 /* Get current time from audio or video decoder */
458 /* TODO: move timing reference in MPEGsystem */
459 sinfo->current_time = 0;
460 if( videoaction )
461 sinfo->current_time = videoaction->Time();
462 if( audioaction )
463 sinfo->current_time = audioaction->Time();
464 }
465
parse_stream_list()466 void MPEG::parse_stream_list()
467 {
468 MPEGstream ** stream_list;
469 register int i;
470
471 /* A new thread is created for each video and audio */
472 /* stream */
473 /* TODO: support MPEG systems containing more than */
474 /* one audio or video stream */
475 i = 0;
476 do
477 {
478 /* Retreive the list of streams */
479 stream_list = system->GetStreamList();
480
481 switch(stream_list[i]->streamid)
482 {
483 case SYSTEM_STREAMID:
484 break;
485
486 case AUDIO_STREAMID:
487 audiostream = stream_list[i];
488 audioaction_enabled = true;
489 audiostream->next_packet();
490 audio = new MPEGaudio(audiostream, sdlaudio);
491 audioaction = audio;
492 break;
493
494 case VIDEO_STREAMID:
495 videostream = stream_list[i];
496 videoaction_enabled = true;
497 videostream->next_packet();
498 video = new MPEGvideo(videostream);
499 videoaction = video;
500 break;
501 }
502
503 i++;
504 }
505 while(stream_list[i]);
506 }
507