/* * thpview * Copyright (c) 2004 monk * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * very simple thp video player * - no fps limiting - will run as fast as possible * - slow * - no audio * - loads whole file to memory */ #include "thpview.h" char filename[256] = "\0"; int quit_requested = FALSE; SDL_Surface *screen, *output = NULL; SDL_Rect rect; __u32 find_marker_any (__u8 *data) { __u32 i = 0; while (data[i++] != 0xff) ; return i; } __u32 find_marker (__u8 *data, __u8 marker) { __u32 i = 0; do i += find_marker_any (&data[i]); while (data[i] != marker); return i; } void thp_set_jpeg_frame_header (JPEGFrame *jframe, __u16 width, __u16 height) { JPEGHeader *jpg = (JPEGHeader *) (jframe->data + 4); __u16 *markers = (__u16 *) jframe->data; markers[0] = BSWAP16 (JPEG_SOI_MARKER); markers[1] = BSWAP16 (JPEG_APP0_MARKER); strcpy (jpg->magic, "JFIF"); jpg->length = BSWAP16 (sizeof (JPEGHeader)); jpg->version = 0x0101; jpg->Xdensity = BSWAP16 (width); jpg->Ydensity = BSWAP16 (height); jpg->units = 0; jpg->Xthumbnail = jpg->Ythumbnail = 0; jframe->end_of_header = jframe->data + 4 + sizeof (JPEGHeader); } void thp_extract_jpeg_frame (JPEGFrame *jframe, __u8 *frame, int with_sound) { THPFrameHeader *fhdr = (THPFrameHeader *) frame; int l = 0; __u8 *p, *data; // move frame pointer after SOI marker frame += 12 + 2; if (with_sound) frame += 4; // get the EOI marker p = frame - 2 + BSWAP32 (fhdr->video_data_size); while (!*p) p--; data = jframe->end_of_header; // copy everything up to Start Of Scan marker l = find_marker (frame, SOS_MARKER) + 1; memcpy (data, frame, l); data += l; frame += l; // convert any 0xff found before EOI to 0xff 0x00 combination // (jpeglib will search for markers inside image data) while (1) { l = find_marker_any (frame); memcpy (data, frame, l); data += l; if (frame + l >= p) { *data++ = EOI_MARKER; break; } *data++ = 0; frame += l; } jframe->size = data - jframe->data; } void thp_write_jpeg_frame (JPEGFrame *jframe, char *fname) { FILE *f; f = fopen (fname, "wb"); fwrite (jframe->data, 1, jframe->size, f); fclose (f); } void thp_extract_audio_frame (AUDIOFrame *aframe, __u8 *frame) { THPFrameHeader *fhdr = (THPFrameHeader *) frame; aframe->data = frame + 12 + 2 + 4 + BSWAP32 (fhdr->video_data_size); aframe->size = BSWAP32 (fhdr->audio_data_size); } void thp_write_audio_frame (AUDIOFrame *aframe, char *fname) { FILE *f; f = fopen (fname, "wb"); fwrite (aframe->data, 1, aframe->size, f); fclose (f); } __u8 *thp_get_frame (__u8 *buff, int n) { THPHeader *hdr = THP_HEADER (buff); THPAudioInfo *ainfo = THP_AUDIO_INFO (buff); THPFrameHeader *fhdr; __u8 *frame; frame = buff + BSWAP32 (hdr->first_frame_offset); while (n--) { fhdr = (THPFrameHeader *) frame; frame = frame + BSWAP32 (fhdr->video_data_size) + 12; if (ainfo) { frame += 4; if (THP_VERSION (buff) == 0x00010000) frame += BSWAP32 (fhdr->audio_data_size); else frame += BSWAP32 (fhdr->audio_data_size) * BSWAP32 (ainfo->num_data); } while (!*((__u32 *) frame)) frame += 4; } return frame; } __u8 *thp_get_next_frame (__u8 *buff, __u8 *frame) { THPAudioInfo *ainfo = THP_AUDIO_INFO (buff); THPFrameHeader *fhdr; fhdr = (THPFrameHeader *) frame; frame = frame + BSWAP32 (fhdr->video_data_size) + 12; if (ainfo) { frame += 4; if (THP_VERSION (buff) == 0x00010000) frame += BSWAP32 (fhdr->audio_data_size); else frame += BSWAP32 (fhdr->audio_data_size) * BSWAP32 (ainfo->num_data); } while (!*((__u32 *) frame)) frame += 4; return frame; } int event_filter (const SDL_Event *event) { switch (event->type) { case SDL_MOUSEMOTION: case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: return FALSE; default: return TRUE; } } int window_open (int width, int height) { if (0 > SDL_Init (SDL_INIT_VIDEO)) { fprintf (stderr, "SDL_Init failed: %s\n", SDL_GetError ()); return FALSE; } screen = SDL_SetVideoMode (width, height, 0, SDL_DOUBLEBUF | SDL_HWSURFACE); if (!screen) { fprintf (stderr, "SDL_SetVideoMode failed: %s\n", SDL_GetError ()); return FALSE; } rect.x = rect.y = 0; rect.w = width; rect.h = height; // output = SDL_DisplayFormat (screen); output = SDL_AllocSurface (SDL_HWSURFACE, width, height, 24, #ifdef LIL_ENDIAN 0x000000ff, 0x0000ff00, 0x00ff0000, #else 0x00ff0000, 0x0000ff00, 0x000000ff, #endif 0); if (!output) { fprintf (stderr, "SDL_CreateRGBSurface failed: %s\n", SDL_GetError ()); return FALSE; } SDL_SetEventFilter (event_filter); return TRUE; } void window_close (void) { if (output) SDL_FreeSurface (output); SDL_Quit (); } float count_fps (void) { static int swaps = 0, last = 0; static float fps = 0; int t = SDL_GetTicks (); swaps++; if (t - last > 1000) { fps = (float) swaps / (0.001 * (t - last)); swaps = 0; last = t; } return fps; } void window_check_input (void) { SDL_Event event; if (SDL_PollEvent (&event)) { switch (event.type) { case SDL_QUIT: quit_requested = TRUE; break; case SDL_KEYUP: switch (event.key.keysym.sym) { case SDLK_ESCAPE: quit_requested = TRUE; break; default: ; } default: ; } } } void window_display_frame (void) { char buff[256]; SDL_BlitSurface (output, NULL, screen, &rect); SDL_Flip (screen); sprintf (buff, "[%s] fps: %.2f", filename, count_fps ()); SDL_WM_SetCaption (buff, NULL); window_check_input (); } void thp_play_movie (__u8 *buff) { THPVideoInfo *vinfo = THP_VIDEO_INFO (buff); __u8 *frame; int i = 0; JPEGFrame jframe; // max possible jpeg frame size jframe.data = malloc (BSWAP32 (THP_HEADER (buff)->max_buffer_size * 2 + 16)); thp_set_jpeg_frame_header (&jframe, BSWAP32 (vinfo->width), BSWAP32 (vinfo->height)); frame = buff + BSWAP32 (THP_HEADER (buff)->first_frame_offset); window_open (BSWAP32 (vinfo->width), BSWAP32 (vinfo->height)); for (i = 0; i < BSWAP32 (THP_HEADER (buff)->num_frames); i++) { thp_extract_jpeg_frame (&jframe, frame, THP_AUDIO_PRESENT (buff)); jpeg_decompress (jframe.data, jframe.size, output->pixels, output->pitch); window_display_frame (); if (i < BSWAP32 (THP_HEADER (buff)->num_frames) - 1) frame = thp_get_next_frame (buff, frame); if (quit_requested) break; } window_close (); free (jframe.data); } void thp_extract_data (__u8 *buff, int extract_video, int extract_audio) { THPVideoInfo *vinfo = THP_VIDEO_INFO (buff); __u8 *frame; char fname[256]; int i = 0; JPEGFrame jframe; AUDIOFrame aframe; if (extract_video) { // max possible jpeg frame size jframe.data = malloc (BSWAP32 (THP_HEADER (buff)->max_buffer_size * 2 + 16)); thp_set_jpeg_frame_header (&jframe, BSWAP32 (vinfo->width), BSWAP32 (vinfo->height)); } for (i = 0; i < BSWAP32 (THP_HEADER (buff)->num_frames); i++) { frame = thp_get_frame (buff, i); printf ("processing frame at offset 0x%.8x: %d\n", frame - buff, i); if (extract_video) { sprintf (fname, "%s-%.4d.jpg", filename, i); thp_extract_jpeg_frame (&jframe, frame, THP_AUDIO_PRESENT (buff)); thp_write_jpeg_frame (&jframe, fname); } if (extract_audio && THP_AUDIO_PRESENT (buff)) { sprintf (fname, "%s-%.4d.snd", filename, i); thp_extract_audio_frame (&aframe, frame); thp_write_audio_frame (&aframe, fname); } } if (extract_video) free (jframe.data); } void thp_show_info (char *buff) { THPHeader *hdr = THP_HEADER (buff); THPComponentData *comp = THP_COMPONENT_DATA (buff); void *info = THP_COMPONENTS_INFO (buff); int i; printf (" version: %.8x\n", BSWAP32 (hdr->version)); printf (" framerate: %f\n", BSWAPF (hdr->framerate)); printf (" frame count: %d\n", BSWAP32 (hdr->num_frames)); printf (" number of components: %d\n", BSWAP32 (comp->num_components)); for (i = 0; i < BSWAP32 (comp->num_components); i++) { switch (comp->components[i]) { case COMPONENT_VIDEO_ID: { THPVideoInfo *vinfo = (THPVideoInfo *) info; printf (" component[%2.2d]: video\n", i); printf (" width: %d\n", BSWAP32 (vinfo->width)); printf (" height: %d\n", BSWAP32 (vinfo->height)); info += COMPONENT_VIDEO_SIZE (buff); } break; case COMPONENT_AUDIO_ID: { THPAudioInfo *ainfo = (THPAudioInfo *) info; printf (" component[%2.2d]: audio\n", i); printf (" channels: %d\n", BSWAP32 (ainfo->num_channels)); printf (" frequency: %d\n", BSWAP32 (ainfo->frequency)); printf (" number of samples: %d\n", BSWAP32 (ainfo->num_samples)); info += COMPONENT_AUDIO_SIZE (buff); } break; case COMPONENT_NONE_ID: break; default: printf (" component[%2.2d]: unknown\n", i); } } } int is_thp (char *buff) { return (BSWAP32 (THP_HEADER (buff)->magic) == THP_MAGIC); } int main (int argc, char **argv) { FILE *f; char *buff; int size; if (argc < 2) { printf ("thpview %s - Simple THP video player\n", THPVIEW_VERSION); printf ("Usage: thpview file.thp\n"); return TRUE; } f = fopen (argv[1], "rb"); if (!f) { printf ("couldn't open file %s\n", argv[1]); perror (""); return TRUE; } strcpy (filename, argv[1]); filename[strlen (filename) - 4] = '\0'; fseek (f, 0, SEEK_END); size = ftell (f); fseek (f, 0, SEEK_SET); buff = malloc (size); fread (buff, 1, size, f); fclose (f); if (is_thp (buff)) { thp_show_info (buff); // thp_extract_data (buff, TRUE, FALSE); thp_play_movie (buff); } else printf ("not a thp file\n"); free (buff); return FALSE; }