/* dumpmpeg - A program to dump frames from an MPEG movie. Copyright (C) 2000, 2001 David Hedbor Based on the Loki Entertainment Software SMPEG plaympeg program. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ static char cvs_version[]="$Id: dumpmpeg.c,v 1.10 2001/05/07 17:41:27 neotron Exp $"; #include "config.h" #include #ifdef STCD_HEADERS #include #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #include #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef TIME_WITH_SYS_TIME #include #endif #if defined(HAVE_SELECT) && defined(HAVE_LINUX_CDROM_H) #define VCD_SUPPORT /* Video CD support */ #endif #ifdef VCD_SUPPORT #include #include #include #endif #include "smpeg.h" int quiet=0, dumpyet = 0; unsigned char *file_prefix; SMPEG *mpeg; void usage(char *argv0) { printf( "Usage: %s [options] file\n" "Where the options are one of:\n" " --quiet or -q Quiet execution. Only print warnings.\n" " --dump N Dump every Nth frame from the movie as bmp.\n" " If left out, all frames will be dumped.\n" " --prefix N Set dump filename prefix.\n" " --start N or -s N Start dumping on this frame.\n" " --help or -h\n" " --version or -v\n" "Specifying - as filename will use stdin for input\n", argv0); } #ifdef VCD_SUPPORT int vcd_read(int fd, int lba, unsigned char *buf) { struct cdrom_msf *msf; int rc; msf = (struct cdrom_msf*) buf; msf->cdmsf_min0 = (lba + CD_MSF_OFFSET) / CD_FRAMES / CD_SECS; msf->cdmsf_sec0 = (lba + CD_MSF_OFFSET) / CD_FRAMES % CD_SECS; msf->cdmsf_frame0 = (lba + CD_MSF_OFFSET) % CD_FRAMES; return(ioctl(fd, CDROMREADMODE2, buf)); } int vcd_open(char * arg) { struct stat buf; struct cdrom_tocentry toc; char *pip; int track; int pipe_fd[2]; int fd; int pid, parent; unsigned char * buffer; /* Track defaults to 02, unless requested otherwise */ track = 02; pip = (char *)strrchr(arg, ':'); if ( pip ) { *pip = '\0'; track = atoi(pip+1) + 1; } /* See if the CD-ROM device file exists */ if ( (stat(arg, &buf) < 0) || !S_ISBLK(buf.st_mode) ) { if ( pip ) { *pip = ':'; } return(0); } fd = open(arg, O_RDONLY, 0); if ( fd < 0 ) { if ( pip ) { *pip = ':'; } return(0); } /* Track 02 (changed to 'track') contains MPEG data */ if ( track < 2 ) { printf("Warning: VCD data normally starts on track 2\n"); } toc.cdte_track = track; toc.cdte_format = CDROM_LBA; if(ioctl(fd, CDROMREADTOCENTRY, &toc) < 0) return(0); if(pipe(pipe_fd) < 0) return(0); parent = getpid(); pid = fork(); if(pid < 0) return(0); if(!pid) { /* Child process fills the pipe */ int pos; struct timeval timeout; fd_set fdset; buffer = (unsigned char *) malloc(CD_FRAMESIZE_RAW0); if( buffer == NULL ) return (0); for(pos = toc.cdte_addr.lba; vcd_read(fd, pos, buffer) >= 0; pos ++) { if(kill(parent, 0) < 0) break; FD_ZERO(&fdset); FD_SET(pipe_fd[1], &fdset); timeout.tv_sec = 10; timeout.tv_usec = 0; if(select(pipe_fd[1]+1, NULL, &fdset, NULL, &timeout) <= 0) break; if(write(pipe_fd[1], buffer, CD_FRAMESIZE_RAW0) < 0) break; } free(buffer); exit(0); } return(pipe_fd[0]); } #endif void save_frame(SDL_Surface *screen, Sint32 x, Sint32 y, Uint32 w, Uint32 h) { unsigned char *save_file_name; SMPEG_Info info; if(!dumpyet) return; SMPEG_getinfo( mpeg, &info ); if(mpeg && info.total_size > 0) { save_file_name = (char *)malloc(strlen(file_prefix) + 15); if( save_file_name == NULL ) { fprintf(stderr, "\nOut of memory!\n"); exit(1); } SMPEG_getinfo( mpeg, &info ); if(!quiet) { fprintf(stdout, "\rSaving file %s_%05d.bmp ", file_prefix, info.current_frame); fflush(stdout); } if(snprintf(save_file_name, strlen(file_prefix) + 24, "%s_%05d.bmp", file_prefix, info.current_frame) == -1) { fprintf(stderr, "\nSave file name buffer overflow.\n"); exit(1); } else { if(SDL_SaveBMP(screen, save_file_name) == -1) { fprintf(stderr, "\nFailed to save file %s!\n", save_file_name); exit(1); } } free(save_file_name); } } int main(int argc, char *argv[]) { int i, start; char *basefile; SDL_version sdlver; SMPEG_version smpegver; SDL_Surface *screen; SMPEG_Info info; int fd; int frame_dump_frequency, dumped = 0; frame_dump_frequency = 0; file_prefix = NULL; fd = 0; start = 0; /* If there were no arguments just print the usage */ if (argc == 1) { usage(argv[0]); exit(0); } /* Get the command line options */ for ( i=1; argv[i] && (argv[i][0] == '-') && (argv[i][1] != 0); ++i ) { if ((strcmp(argv[i], "--quiet") == 0) || (strcmp(argv[i], "-q") == 0)) { quiet = 1; } else if ((strcmp(argv[i], "--dump") == 0)) { ++i; if (i >= argc) { fprintf(stderr, "Please specify a frame dump frequency when using --dump\n"); exit(1); } if ( argv[i] ) { frame_dump_frequency = atol(argv[i]); } if(frame_dump_frequency <= 0) { fprintf(stderr, "Please specify a positive frame dump frequency number.\n"); exit(1); } } else if ((strcmp(argv[i], "--start") == 0) || (strcmp(argv[i], "-s") == 0)) { ++i; if (i >= argc) { fprintf(stderr, "Please specify a start frame!\n"); exit(1); } if ( argv[i] ) { start = atol(argv[i]); } if(start <= 0) { fprintf(stderr, "Please specify a positive start frame.\n"); exit(1); } fprintf(stdout, "Starting at frame %d.\n", start); } else if ((strcmp(argv[i], "--prefix") == 0)) { ++i; if ( argv[i] ) { file_prefix = (char *)malloc(strlen(argv[i])+1); if( file_prefix == NULL ){ fprintf(stderr,"\nOut of memory!\n"); exit(1); } strcpy(file_prefix, argv[i]); } if(file_prefix == NULL) { fprintf(stderr, "Please specify file prefix.\n"); } } else if ((strcmp(argv[i], "--version") == 0) || (strcmp(argv[i], "-v") == 0)) { SDL_VERSION(&sdlver); SMPEG_VERSION(&smpegver); printf("dumpmpeg version: %s\n" "CVS version: %s\n" "SDL version: %d.%d.%d\n" "SMPEG version: %d.%d.%d\n", VERSION, cvs_version+5, sdlver.major, sdlver.minor, sdlver.patch, smpegver.major, smpegver.minor, smpegver.patch); exit(0); } else if ((strcmp(argv[i], "--help") == 0) || (strcmp(argv[i], "-h") == 0)) { usage(argv[0]); exit(0); } else { fprintf(stderr, "Warning: Unknown option: %s\n", argv[i]); } } if (argc == i) { usage(argv[0]); exit(0); } if(!frame_dump_frequency) frame_dump_frequency = 1; if(file_prefix == NULL) { file_prefix = (char *)malloc(11); if( file_prefix == NULL ){ fprintf(stderr,"\nOut of memory!\n"); exit(1); } strcpy(file_prefix, "framedump"); } /* Dump frames from the mpeg file below. */ /* Initialize SDL */ if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) { fprintf(stderr, "Error: Couldn't init SDL video: %s\n", SDL_GetError()); exit(1); } /* Create the MPEG stream */ #ifdef VCD_SUPPORT /* Check if source is a CDROM device */ if((fd = vcd_open(argv[i])) != 0) mpeg = SMPEG_new_descr(fd, &info, 1); else #endif { if(strcmp(argv[i], "-") == 0) /* Use stdin for input */ mpeg = SMPEG_new_descr(0, &info, 1); else mpeg = SMPEG_new(argv[i], &info, 1); } if ( SMPEG_error(mpeg) ) { fprintf(stderr, "%s: %s\n", argv[i], SMPEG_error(mpeg)); SMPEG_delete(mpeg); exit(1); } /* Enable video, but don't enable audio. We don't want / need it. */ SMPEG_enableaudio(mpeg, 0); SMPEG_enablevideo(mpeg, 1); /* Print information about the video */ if(!quiet) { basefile = (char *)strrchr(argv[i], '/'); if ( basefile ) { ++basefile; } else { basefile = argv[i]; } if ( info.has_audio && info.has_video ) { printf("%s: MPEG system stream (audio/video)\n", basefile); } else if ( info.has_audio ) { printf("%s: MPEG audio stream\n", basefile); } else if ( info.has_video ) { printf("%s: MPEG video stream\n", basefile); } if ( info.has_video ) { printf("\tVideo %dx%d resolution\n", info.width, info.height); } if ( info.has_audio ) { printf("\tAudio %s\n", info.audio_string); } if ( info.total_size ) { printf("\tSize: %d\n", info.total_size); } } /* Set up video display if needed */ if ( info.has_video ) { Uint32 video_flags; int video_bpp; video_bpp = 32; video_flags = SDL_SWSURFACE; screen = SDL_SetVideoMode(info.width, info.height, video_bpp, video_flags); if ( screen == NULL ) { fprintf(stderr, "Unable to set %dx%d video mode: %s\n", info.width, info.height, SDL_GetError()); exit(1); } if ( screen->flags & SDL_FULLSCREEN ) { SDL_ShowCursor(0); } SMPEG_setdisplay(mpeg, screen, NULL, save_frame); SMPEG_scaleXY(mpeg, screen->w, screen->h); } dumped = start; // Set first frame to dump. if(dumped) { SMPEG_renderFrame(mpeg, dumped); } dumpyet = 1; if(frame_dump_frequency) { int i, done; done = 0; while(!done) { SDL_Event event; while ( SDL_PollEvent(&event) ) { switch (event.type) { case SDL_KEYDOWN: if ((event.key.keysym.sym == SDLK_ESCAPE) || (event.key.keysym.sym == SDLK_q) ) { // Quit done = 1; } break; case SDL_QUIT: done = 1; break; } } SMPEG_renderFrame(mpeg, dumped); SMPEG_getinfo( mpeg, &info ); if(info.current_frame != dumped) { if(info.current_frame == (dumped - frame_dump_frequency)) done = 1; else dumped = info.current_frame; } dumped += frame_dump_frequency; } if(!quiet) fprintf(stdout, "\rDone! \n"); } SMPEG_delete(mpeg); SDL_Quit(); if(file_prefix) free(file_prefix); if(fd) close(fd); exit(0); }