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