1 /*
2     SMPEG - SDL MPEG Player Library
3     Copyright (C) 1999  Loki Entertainment Software
4 
5     - Modified by Michel Darricau from eProcess <mdarricau@eprocess.fr>  for popcorn -
6 
7     This library is free software; you can redistribute it and/or
8     modify it under the terms of the GNU Library General Public
9     License as published by the Free Software Foundation; either
10     version 2 of the License, or (at your option) any later version.
11 
12     This library is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15     Library General Public License for more details.
16 
17     You should have received a copy of the GNU Library General Public
18     License along with this library; if not, write to the Free
19     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21 
22 /*
23  * Copyright (c) 1995 The Regents of the University of California.
24  * All rights reserved.
25  *
26  * Permission to use, copy, modify, and distribute this software and its
27  * documentation for any purpose, without fee, and without written agreement is
28  * hereby granted, provided that the above copyright notice and the following
29  * two paragraphs appear in all copies of this software.
30  *
31  * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
32  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
33  * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
34  * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  *
36  * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
37  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
38  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
39  * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
40  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
41  */
42 
43 /*
44  * Portions of this software Copyright (c) 1995 Brown University.
45  * All rights reserved.
46  *
47  * Permission to use, copy, modify, and distribute this software and its
48  * documentation for any purpose, without fee, and without written agreement
49  * is hereby granted, provided that the above copyright notice and the
50  * following two paragraphs appear in all copies of this software.
51  *
52  * IN NO EVENT SHALL BROWN UNIVERSITY BE LIABLE TO ANY PARTY FOR
53  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
54  * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF BROWN
55  * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
56  *
57  * BROWN UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
58  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
59  * PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
60  * BASIS, AND BROWN UNIVERSITY HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
61  * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
62  */
63 
64 
65 /*
66    Changes to make the code reentrant:
67      Got rid of setjmp, longjmp
68      deglobalized: EOF_flag, FilmState, curVidStream, bitOffset, bitLength,
69      bitBuffer, sys_layer, input, seekValue, window, X Windows globals (to
70      xinfo), curBits, ditherType, matched_depth, totNumFrames, realTimeStart
71 
72    Additional changes:
73      Ability to play >1 movie (w/out CONTROLS)
74      Make sure we do a full frame for each movie
75      DISABLE_DITHER #ifdef to avoid compiling dithering code
76      Changes to deal with non-MPEG streams
77      Now deals with NO_DITHER, PPM_DITHER and noDisplayFlag==1
78      CONTROLS version now can deal with >1 movie
79    -lsh@cs.brown.edu (Loring Holden)
80 */
81 
82 
83 #include <limits.h>
84 #include <string.h>
85 
86 #include "video.h"
87 #include "proto.h"
88 #include "dither.h"
89 #include "util.h"
90 
91 #include "MPEGvideo.h"
92 
93 /*--------------------------------------------------------------*/
94 
95 
96 /* Define buffer length. */
97 #define BUF_LENGTH 80000
98 
99 
100 /* TODO: Eliminate these globals so multiple movies can be played. */
101 
102 /* Quiet flag (verbose). */
103 int quietFlag = 1;
104 
105 /* Framerate, -1: specified in stream (default)
106                0: as fast as possible
107                N (N>0): N frames/sec
108                */
109 #ifdef TIME_MPEG
110 int framerate = 0;
111 #else
112 int framerate = -1;
113 #endif
114 
115 /* Flag for high quality at the expense of speed */
116 #ifdef QUALITY
117 int qualityFlag = 1;
118 #else
119 int qualityFlag = 0;
120 #endif
121 
122 /*--------------------------------------------------------------*/
123 
MPEGvideo(MPEGstream * stream)124 MPEGvideo::MPEGvideo(MPEGstream *stream)
125 {
126     Uint32 start_code;
127     MPEGstream_marker *marker;
128 
129     /* Set the MPEG data stream */
130     mpeg = stream;
131     time_source = NULL;
132 
133     /* Set default playback variables */
134     _thread = NULL;
135     _stream = NULL;
136 
137     /* Mark the data to leave the stream unchanged */
138     /* after parsing */
139     marker = mpeg->new_marker(0);
140 
141     /* Get the width and height of the video */
142     start_code = mpeg->copy_byte();
143     start_code <<= 8;
144     start_code |= mpeg->copy_byte();
145     start_code <<= 8;
146     start_code |= mpeg->copy_byte();
147     while ( ! mpeg->eof() && (start_code != SEQ_START_CODE) ) {
148         start_code <<= 8;
149         start_code |= mpeg->copy_byte();
150     }
151     if ( start_code == SEQ_START_CODE ) {
152         Uint8 buf[4];
153 
154         /* Get the width and height of the video */
155         mpeg->copy_data(buf, 4);
156         _w = (buf[0]<<4)|(buf[1]>>4);    /* 12 bits of width */
157         _h = ((buf[1]&0xF)<<8)|buf[2];   /* 12 bits of height */
158 	switch(buf[3]&0xF)                /*  4 bits of fps */
159 	{
160 	  case 1: _fps = 23.97f; break;
161 	  case 2: _fps = 24.00f; break;
162 	  case 3: _fps = 25.00f; break;
163 	  case 4: _fps = 29.97f; break;
164 	  case 5: _fps = 30.00f; break;
165 	  case 6: _fps = 50.00f; break;
166 	  case 7: _fps = 59.94f; break;
167 	  case 8: _fps = 60.00f; break;
168 	  case 9: _fps = 15.00f; break;
169 	  default: _fps = 30.00f; break;
170 	}
171     } else {
172         _w = 0;
173         _h = 0;
174 	_fps = 0.00;
175         SetError("Not a valid MPEG video stream");
176     }
177     /* Rewind back to the old position */
178     mpeg->seek_marker(marker);
179     mpeg->delete_marker(marker);
180 
181     /* Keep original width and height in _ow and _oh */
182     _ow = _w;
183     _oh = _h;
184 
185     /* Now round up width and height to a multiple   */
186     /* of a macroblock size (16 pixels) to keep the  */
187     /* video decoder happy */
188     _w = (_w + 15) & ~15;
189     _h = (_h + 15) & ~15;
190 
191     _frame.w = _ow;
192     _frame.h = _oh;
193     _frame.image_width = _w;
194     _frame.image_height = _h;
195     _frame.image = (Uint8*)SDL_malloc((_w * _h) + (_w * _h)/4 + (_w * _h)/4);
196 
197     _callback = 0;
198     _callback_data = 0;
199     _callback_lock = 0;
200 }
201 
~MPEGvideo()202 MPEGvideo:: ~MPEGvideo()
203 {
204     /* Stop it before we free everything */
205     Stop();
206 
207     /* Free actual video stream */
208     if( _stream )
209         DestroyVidStream( _stream );
210 }
211 
212 /* Simple thread play function */
Play_MPEGvideo(void * udata)213 int Play_MPEGvideo( void *udata )
214 {
215     MPEGvideo *mpeg = (MPEGvideo *)udata;
216 
217     /* Get the time the playback started */
218     mpeg->_stream->realTimeStart += ReadSysClock();
219 
220 #ifdef TIME_MPEG
221     int start_frames, stop_frames;
222     int total_frames;
223     Uint32 start_time, stop_time;
224     float total_time;
225 
226     start_frames = mpeg->_stream->totNumFrames;
227     start_time = SDL_GetTicks();
228 #endif
229     mpeg->force_exit = false;
230     while( mpeg->playing && !mpeg->force_exit )
231     {
232         int mark = mpeg->_stream->totNumFrames;
233 
234         /* make sure we do a whole frame */
235         while( (mark == mpeg->_stream->totNumFrames) && mpeg->playing && !mpeg->force_exit )
236         {
237             mpegVidRsrc( 0, mpeg->_stream, 0 );
238         }
239 
240         if( mpeg->_stream->film_has_ended || mpeg->force_exit )
241         {
242             mpeg->playing = false;
243         }
244     }
245     /* Get the time the playback stopped */
246     mpeg->_stream->realTimeStart -= ReadSysClock();
247 #ifdef TIME_MPEG
248     stop_time = SDL_GetTicks();
249     stop_frames = mpeg->_stream->totNumFrames;
250     total_frames = (stop_frames-start_frames);
251     total_time = (float)(stop_time-start_time)/1000.0;
252     if ( total_time > 0 ) {
253         printf("%d frames in %2.2f seconds (%2.2f FPS)\n",
254                total_frames, total_time, (float)total_frames/total_time);
255     }
256 #endif
257     return(0);
258 }
259 
260 void
Play(void)261 MPEGvideo:: Play(void)
262 {
263     ResetPause();
264     if ( _stream ) {
265 		if ( playing ) {
266 			Stop();
267 		}
268         playing = true;
269 #ifdef DISABLE_VIDEO_CALLBACK_THREAD
270 		Play_MPEGvideo(this);
271 #else
272         _thread = SDL_CreateThread( Play_MPEGvideo, "MPEG video decode", this );
273         if ( !_thread ) {
274             playing = false;
275         }
276 #endif
277     }
278 }
279 
280 void
Stop(void)281 MPEGvideo:: Stop(void)
282 {
283     if ( _thread ) {
284         force_exit = true;
285         SDL_WaitThread(_thread, NULL);
286         _thread = NULL;
287     }
288 
289     playing = false;
290 
291     ResetPause();
292 }
293 
294 void
Rewind(void)295 MPEGvideo:: Rewind(void)
296 {
297     Stop();
298     if ( _stream ) {
299       /* Reinitialize vid_stream pointers */
300       ResetVidStream( _stream );
301 #ifdef ANALYSIS
302       init_stats();
303 #endif
304     }
305 }
306 
307 void
ResetSynchro(double time)308 MPEGvideo:: ResetSynchro(double time)
309 {
310   if( _stream )
311   {
312     _stream->_jumpFrame = -1;
313     _stream->realTimeStart = -time;
314     play_time = time;
315     if (time > 0) {
316 	double oneframetime;
317 	if (_stream->_oneFrameTime == 0)
318 		oneframetime = 1.0 / _stream->_smpeg->_fps;
319 	else
320 		oneframetime = _stream->_oneFrameTime;
321 
322 	/* time -> frame */
323 	_stream->totNumFrames = (int)(time / oneframetime);
324 
325 	/* Set Current Frame To 0 & Frame Adjust Frag Set */
326 	_stream->current_frame = 0;
327 	_stream->need_frameadjust=true;
328     }
329   }
330 }
331 
332 
333 void
Skip(float seconds)334 MPEGvideo::Skip(float seconds)
335 {
336   int frame;
337 
338   /* Called only when there is no timestamp info in the MPEG */
339   /* This is quite slow however */
340   printf("Video: Skipping %f seconds...\n", seconds);
341   frame = (int) (_fps * seconds);
342 
343   if( _stream )
344   {
345     _stream->_jumpFrame = frame;
346     while( (_stream->totNumFrames < frame) &&
347 	   ! _stream->film_has_ended )
348     {
349       mpegVidRsrc( 0, _stream, 0 );
350     }
351     ResetSynchro(0);
352   }
353 }
354 
355 	/* Michel Darricau from eProcess <mdarricau@eprocess.fr> conflict name in popcorn */
356 MPEGstatus
GetStatus(void)357 MPEGvideo:: GetStatus(void)
358 {
359     if ( _stream ) {
360         if( !_thread || (_stream->film_has_ended ) ) {
361             return MPEG_STOPPED;
362         } else {
363             return MPEG_PLAYING;
364         }
365     }
366     return MPEG_ERROR;
367 }
368 
369 bool
GetVideoInfo(MPEG_VideoInfo * info)370 MPEGvideo:: GetVideoInfo(MPEG_VideoInfo *info)
371 {
372     if ( info ) {
373         info->width = _ow;
374         info->height = _oh;
375         if ( _stream ) {
376             info->current_frame = _stream->current_frame;
377 #ifdef CALCULATE_FPS
378 
379             /* Get the appropriate indices for the timestamps */
380             /* Calculate the frames-per-second from the timestamps */
381             if ( _stream->frame_time[_stream->timestamp_index] ) {
382                 double *timestamps;
383                 double  time_diff;
384                 int this_index;
385                 int last_index;
386 
387                 timestamps = _stream->frame_time;
388                 last_index = _stream->timestamp_index;
389                 this_index = last_index - 1;
390                 if ( this_index < 0 ) {
391                     this_index = FPS_WINDOW-1;
392                 }
393                 time_diff = timestamps[this_index] - timestamps[last_index];
394                 info->current_fps = (double)FPS_WINDOW / time_diff;
395             } else {
396                 info->current_fps = 0.0;
397             }
398 #else
399             info->current_fps = _stream->totNumFrames /
400                                 (ReadSysClock() - _stream->realTimeStart);
401 #endif
402         } else {
403             info->current_frame = 0;
404             info->current_fps = 0.0;
405         }
406     }
407     return(!WasError());
408 }
409 
410 /*
411    Returns zero if fails.
412 
413    lock - lock is held while MPEG stream is playing
414    callback - called on every frame, for display update
415 */
416 bool
SetDisplay(MPEG_DisplayCallback callback,void * data,SDL_mutex * lock)417 MPEGvideo:: SetDisplay(MPEG_DisplayCallback callback, void *data, SDL_mutex *lock)
418 {
419     _callback = callback;
420     _callback_data = data;
421     _callback_lock = lock;
422 
423     if ( !_stream ) {
424         decodeInitTables();
425 
426         InitCrop();
427         InitIDCT();
428 
429         _stream = NewVidStream( (unsigned int) BUF_LENGTH );
430         if( _stream ) {
431             _stream->_smpeg        = this;
432             _stream->ditherType    = FULL_COLOR_DITHER;
433 
434             if( mpegVidRsrc( 0, _stream, 1 ) == NULL ) {
435                 SetError("Not an MPEG video stream");
436                 return false;
437             }
438         }
439 
440         if ( ! InitPictImages(_stream, _w, _h) )
441             return false;
442     }
443     return true;
444 }
445 
446 void
RenderFrame(int frame)447 MPEGvideo:: RenderFrame( int frame )
448 {
449     _stream->need_frameadjust = true;
450 
451     if( _stream->current_frame > frame ) {
452         mpeg->rewind_stream();
453         mpeg->next_packet();
454         Rewind();
455     }
456 
457     _stream->_jumpFrame = frame;
458 
459     while( (_stream->current_frame < frame) &&
460            ! _stream->film_has_ended )
461     {
462         mpegVidRsrc( 0, _stream, 0 );
463     }
464 
465     _stream->_jumpFrame = -1;
466 }
467 
468 void
RenderFinal()469 MPEGvideo:: RenderFinal()
470 {
471     /* This operation can only be performed when stopped */
472     Stop();
473 
474     if ( ! _stream->film_has_ended ) {
475         /* Search for the last "group of pictures" start code */
476         Uint32 start_code;
477         MPEGstream_marker * marker, * oldmarker;
478 
479         marker = 0;
480         start_code = mpeg->copy_byte();
481         start_code <<= 8;
482         start_code |= mpeg->copy_byte();
483         start_code <<= 8;
484         start_code |= mpeg->copy_byte();
485 
486         while ( ! mpeg->eof() ) {
487             start_code <<= 8;
488             start_code |= mpeg->copy_byte();
489             if ( start_code == GOP_START_CODE ) {
490 	          oldmarker = marker;
491         	  marker = mpeg->new_marker(-4);
492         	  if( oldmarker ) mpeg->delete_marker( oldmarker );
493        		  mpeg->garbage_collect();
494             }
495         }
496 
497         /* Set the stream to the last spot marked */
498         if ( ! mpeg->seek_marker( marker ) ) {
499             mpeg->rewind_stream();
500             mpeg->next_packet();
501         }
502 
503         mpeg->delete_marker( marker );
504         _stream->buf_length = 0;
505         _stream->bit_offset = 0;
506 
507         /* Process all frames without displaying any */
508         _stream->_skipFrame = 1;
509 
510         RenderFrame( INT_MAX );
511 
512         mpeg->garbage_collect();
513     }
514 
515     /* Display the frame */
516     DisplayFrame(_stream);
517 }
518 
519 /* EOF */
520