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