/* * GPAC - Multimedia Framework C SDK * * Copyright (c) Jean Le Feuvre 2000-2005 * All rights reserved * * This file is part of GPAC / command-line mp4 toolbox * * GPAC is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * GPAC 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include #include #ifdef WIN32 #include #define GPAC_CFG_FILE "GPAC.cfg" #else #include typedef struct tagBITMAPFILEHEADER { u16 bfType; u32 bfSize; u16 bfReserved1; u16 bfReserved2; u32 bfOffBits; } BITMAPFILEHEADER; typedef struct tagBITMAPINFOHEADER{ u32 biSize; s32 biWidth; s32 biHeight; u16 biPlanes; u16 biBitCount; u32 biCompression; u32 biSizeImage; s32 biXPelsPerMeter; s32 biYPelsPerMeter; u32 biClrUsed; u32 biClrImportant; } BITMAPINFOHEADER; #define BI_RGB 0L #define GPAC_CFG_FILE ".gpacrc" #endif #include #include void PrintVersion() { printf ("MP42AVI - GPAC version %s\n", GPAC_VERSION); } void PrintUsage() { printf ("MP42AVI [option] input\n" "Dumps BIFS media frames as AVI, BMP or raw\n\n" "Options\n" "-fps Framerate: specifies extraction framerate - if not set computed from track length\n" "-size WxH: forces output BIFS to the given resolution\n" "-raw [frame]: uses raw format for output - only dumps one frame if specified\n" "-bmp [frame]: uses BMP format for output - only dumps one frame if specified\n" "-outpath path: specifies where to dump frames/movie\n" "\n" "Note: when dumping a frame, either the frame number can be specified or the frame time\n" "in the format hh:mm:ss:xFz where hh, mm, ss are hours, minutes, seconds, x the number\n" "of the frame in the seconds and z the frame rate used to express the time\n" "\n" "-cfg: specifies path to GPAC config file (GPAC.cfg)\n" "-v: prints version\n" "-h: prints this message\n" "\nWritten by Jean Le Feuvre - (c) 2000-2005\n"); } typedef struct { GF_Renderer *sr; GF_SceneGraph *sg; GF_BifsDecoder *bifs; GF_ISOFile *file; u32 track; u64 duration, cts; } BIFSVID; void node_init(void *cbk, GF_Node *node) { BIFSVID *b2v = cbk; switch (gf_node_get_tag(node)) { case TAG_MPEG4_Conditional: case TAG_MPEG4_QuantizationParameter: break; default: if (b2v->sr) gf_sr_on_node_init(b2v->sr, node); break; } } void node_modif(void *cbk, GF_Node *node) { BIFSVID *b2v = cbk; if (b2v->sr) gf_sr_invalidate(b2v->sr, node); } Double get_scene_time(void *cbk) { Double res; BIFSVID *b2v = cbk; res = (Double) (s64) b2v->cts; res /= (Double) (s64) b2v->duration; return res; } void write_bmp(GF_VideoSurface *fb, char *rad_name, u32 img_num) { char str[GF_MAX_PATH]; BITMAPFILEHEADER fh; BITMAPINFOHEADER fi; FILE *fout; u32 j, i; char *ptr; if (img_num<10) { sprintf(str, "%s_00%d.bmp", rad_name, img_num); } else if (img_num<100) { sprintf(str, "%s_0%d.bmp", rad_name, img_num); } else { sprintf(str, "%s_%d.bmp", rad_name, img_num); } fout = fopen(str, "wb"); if (!fout) return; memset(&fh, 0, sizeof(fh)); fh.bfType = 19778; fh.bfOffBits = 14 + 40; memset(&fi, 0, sizeof(char)*40); fi.biSize = sizeof(char)*40; fi.biWidth = fb->width; fi.biHeight = fb->height; fi.biPlanes = 1; fi.biBitCount = 24; fi.biCompression = BI_RGB; fi.biSizeImage = fb->pitch * fb->height; /*NOT ALIGNED!!*/ fwrite(&fh.bfType, 2, 1, fout); fwrite(&fh.bfSize, 4, 1, fout); fwrite(&fh.bfReserved1, 2, 1, fout); fwrite(&fh.bfReserved2, 2, 1, fout); fwrite(&fh.bfOffBits, 4, 1, fout); fwrite(&fi, 1, 40, fout); for (j=fb->height; j>0; j--) { ptr = fb->video_buffer + (j-1)*fb->pitch; //fwrite(ptr, 1, fb->width * 3, fout); for (i=0;iwidth; i++) { fputc(ptr[2], fout); fputc(ptr[1], fout); fputc(ptr[0], fout); ptr+=3; } } fclose(fout); } void write_raw(GF_VideoSurface *fb, char *rad_name, u32 img_num) { char str[GF_MAX_PATH]; FILE *fout; if (img_num<10) { sprintf(str, "%s_00%d.raw", rad_name, img_num); } else if (img_num<100) { sprintf(str, "%s_0%d.raw", rad_name, img_num); } else { sprintf(str, "%s_%d.raw", rad_name, img_num); } fout = fopen(str, "wb"); if (!fout) return; fwrite(fb->video_buffer , fb->height*fb->pitch, 1, fout); fclose(fout); } void dump_frame(BIFSVID b2v, char *conv_buf, char *out_path, u32 dump_type, avi_t *avi_out, u32 frameNum) { u32 k; GF_VideoSurface fb; /*lock it*/ gf_sr_get_screen_buffer(b2v.sr, &fb); /*export frame*/ switch (dump_type) { case 0: /*reverse frame*/ for (k=0; kdependsOnESID && (esd->decoderConfig->streamType == GF_STREAM_SCENE)) break; gf_odf_desc_del((GF_Descriptor *) esd); esd = NULL; } if (!esd) { printf("no bifs track found\n"); goto err_exit; } es_id = (u16) gf_isom_get_track_id(file, track_number+1); e = gf_bifs_decoder_configure_stream(b2v.bifs, es_id, esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, esd->decoderConfig->objectTypeIndication); if (e) { printf("BIFS init error %s\n", gf_error_to_string(e)); gf_odf_desc_del((GF_Descriptor *) esd); esd = NULL; goto err_exit; } { GF_ISOSample *samp = gf_isom_get_sample(file, track_number+1, 1, &di); b2v.cts = samp->DTS + samp->CTS_Offset; /*apply command*/ gf_bifs_decode_au(b2v.bifs, es_id, samp->data, samp->dataLength, ((Double)(s64)b2v.cts)/1000.0); gf_isom_sample_del(&samp); } b2v.duration = gf_isom_get_media_duration(file, track_number+1); gf_odf_desc_del((GF_Descriptor *) esd); } gf_sr_set_scene(b2v.sr, b2v.sg); if (!width || !height) { gf_sg_get_scene_size_info(b2v.sg, &width, &height); } /*we work in RGB24, and we must make sure the pitch is %4*/ if ((width*3)%4) { printf("Adjusting width (%d) to have a stride multiple of 4\n", width); while ((width*3)%4) width--; } gf_sr_set_size(b2v.sr, width, height); gf_sr_get_screen_buffer(b2v.sr, &fb); width = fb.width; height = fb.height; gf_sr_release_screen_buffer(b2v.sr, &fb); GF_SAFEALLOC(rendered_frames, nb_viewpoints*sizeof(char *)); for (viewpoint_index = 1; viewpoint_index <= nb_viewpoints; viewpoint_index++) { GF_SAFEALLOC(rendered_frames[viewpoint_index-1], fb.width*fb.height*3); gf_sr_set_viewpoint(b2v.sr, viewpoint_index, NULL); gf_sr_render_frame(b2v.sr); /*needed for background2D !!*/ gf_sr_render_frame(b2v.sr); strcpy(out_path, ""); if (out_dir) { strcat(out_path, out_dir); if (out_path[strlen(out_path)-1] != '\\') strcat(out_path, "\\"); } strcat(out_path, rad_name); strcat(out_path, "_view"); gf_sr_get_screen_buffer(b2v.sr, &fb); write_bmp(&fb, out_path, viewpoint_index); memcpy(rendered_frames[viewpoint_index-1], fb.video_buffer, fb.width*fb.height*3); gf_sr_release_screen_buffer(b2v.sr, &fb); } if (width != 800 || height != 480) { printf("Wrong scene dimension, cannot produce output\n"); goto err_exit; } else { u32 x, y; GF_VideoSurface out_fb; u32 bpp = 3; out_fb.width = 800; out_fb.height = 480; out_fb.pitch = 800*bpp; out_fb.pixel_format = GF_PIXEL_RGB_24; out_fb.is_hardware_memory = 0; GF_SAFEALLOC(out_fb.video_buffer, out_fb.pitch*out_fb.height) #if 1 for (y=0; ydependsOnESID && (esd->decoderConfig->streamType == GF_STREAM_SCENE)) break; gf_odf_desc_del((GF_Descriptor *) esd); esd = NULL; } if (!esd) { printf("no bifs track found\n"); goto err_exit; } b2v.duration = gf_isom_get_media_duration(file, i+1); timescale = gf_isom_get_media_timescale(file, i+1); es_id = (u16) gf_isom_get_track_id(file, i+1); e = gf_bifs_decoder_configure_stream(b2v.bifs, es_id, esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, esd->decoderConfig->objectTypeIndication); if (e) { printf("BIFS init error %s\n", gf_error_to_string(e)); gf_odf_desc_del((GF_Descriptor *) esd); esd = NULL; goto err_exit; } if (dump_time>=0) dump_time = dump_time *1000 / timescale; gf_sr_set_scene(b2v.sr, b2v.sg); count = gf_isom_get_sample_count(file, i+1); reset_fps = 0; if (!fps) { fps = (Float) (count * timescale); fps /= (Double) (s64) b2v.duration; printf("Estimated BIFS FrameRate %g\n", fps); reset_fps = 1; } if (!width || !height) { gf_sg_get_scene_size_info(b2v.sg, &width, &height); } /*we work in RGB24, and we must make sure the pitch is %4*/ if ((width*3)%4) { printf("Adjusting width (%d) to have a stride multiple of 4\n", width); while ((width*3)%4) width--; } gf_sr_set_size(b2v.sr, width, height); gf_sr_render_frame(b2v.sr); gf_sr_get_screen_buffer(b2v.sr, &fb); width = fb.width; height = fb.height; if (avi_out) { AVI_set_video(avi_out, width, height, fps, comp); conv_buf = malloc(sizeof(char) * width * height * 3); } printf("Dumping at BIFS resolution %d x %d\n\n", width, height); gf_sr_release_screen_buffer(b2v.sr, &fb); cur_time = 0; duration = (u32)(timescale / fps); if (reset_fps) fps = 0; frameNum = 1; first_dump = 1; for (j=0; jDTS + samp->CTS_Offset; /*apply command*/ gf_bifs_decode_au(b2v.bifs, es_id, samp->data, samp->dataLength, ((Double)(s64)b2v.cts)/1000.0); gf_isom_sample_del(&samp); if ((frameID>=0) && (j<(u32)frameID)) continue; if ((dump_time>=0) && ((u32) dump_time>b2v.cts)) continue; /*render frame*/ gf_sr_render_frame(b2v.sr); /*needed for background2D !!*/ if (first_dump) { gf_sr_render_frame(b2v.sr); first_dump = 0; } if (fps) { if (cur_time > b2v.cts) continue; while (1) { printf("dumped frame time %f (frame %d - sample %d)\r", ((Float)cur_time)/timescale, frameNum, j+1); dump_frame(b2v, conv_buf, config_path, dump_type, avi_out, frameNum); frameNum++; cur_time += duration; if (cur_time > b2v.cts) break; } } else { dump_frame(b2v, conv_buf, config_path, dump_type, avi_out, (frameID>=0) ? frameID : frameNum); if (frameID>=0 || dump_time>=0) break; frameNum++; printf("dumped frame %d / %d\r", j+1, count); } } gf_odf_desc_del((GF_Descriptor *) esd); /*destroy everything*/ gf_bifs_decoder_del(b2v.bifs); gf_sg_del(b2v.sg); gf_sr_set_scene(b2v.sr, NULL); gf_sr_del(b2v.sr); err_exit: if (avi_out) AVI_close(avi_out); if (conv_buf) free(conv_buf); if (user.modules) gf_modules_del(user.modules); if (needs_raw) gf_cfg_set_key(user.config, "Video", "DriverName", old_driv); gf_cfg_del(user.config); } int main (int argc, char **argv) { Double fps_dump; u32 i; char rad[500]; s32 frameID, h, m, s, f; Float fps; u32 dump_type; s32 dump_time; u32 dump_w, dump_h; Bool copy; char szConfigFile[4096]; char *dump_out; char *inName, *arg; GF_ISOFile *file; if (argc < 2) { PrintUsage(); return 0; } dump_type = 0; fps_dump = 0.0f; dump_w = dump_h = 0; dump_out = NULL; inName = NULL; frameID = -1; dump_time = -1; szConfigFile[0] = 0; for (i = 1; i < (u32) argc ; i++) { arg = argv[i]; if (arg[0] != '-') { inName = arg; break; } if (!stricmp(arg, "-h")) { PrintUsage(); return 0; } else if (!stricmp(arg, "-version")) { PrintVersion(); return 0; } else if (!stricmp(arg, "-size")) { sscanf(argv[i+1], "%dx%d", &dump_w, &dump_h); i++; } else if (!stricmp(arg, "-raw")) { dump_type = 2; if ((i+1<(u32)argc) && (argv[i+1][0]!='-')) { if (strstr(argv[i+1], "T")) { if (strstr(argv[i+1], "F")) { sscanf(argv[i+1], "T%d:%d:%d:%dF%f", &h, &m, &s, &f, &fps); dump_time = (s32) ((3600*h + 60*m + s)*1000 + 1000*f/fps); } else { sscanf(argv[i+1], "T%d:%d:%d", &h, &m, &s); dump_time = (s32) ((3600*h + 60*m + s)*1000); } } else { frameID = atoi(argv[i+1]); } i++; } } else if (!stricmp(arg, "-bmp")) { dump_type = 1; if ((i+1<(u32)argc) && (argv[i+1][0]!='-')) { if (strstr(argv[i+1], "T")) { if (strstr(argv[i+1], "F")) { sscanf(argv[i+1], "T%d:%d:%d:%dF%f", &h, &m, &s, &f, &fps); dump_time = (s32) ((3600*h + 60*m + s)*1000 + 1000*f/fps); } else { sscanf(argv[i+1], "T%d:%d:%d", &h, &m, &s); dump_time = (s32) ((3600*h + 60*m + s)*1000); } } else { frameID = atoi(argv[i+1]); } i++; } } else if (!stricmp(arg, "-3d")) { dump_type = 3; } else if (!stricmp(arg, "-outpath")) { dump_out = argv[i+1]; i++; } else if (!stricmp(arg, "-fps")) { fps_dump = atof(argv[i+1]); i++; } else if (!stricmp(arg, "-copy")) { copy = 1; } else if (!stricmp(arg, "-cfg")) { strcpy(szConfigFile, argv[i+1]); i += 1; } else { PrintUsage(); return (0); } } if (!inName) { PrintUsage(); return 0; } gf_sys_init(); file = gf_isom_open(inName, GF_ISOM_OPEN_READ, NULL); if (!file) { printf("Error opening file: %s\n", gf_error_to_string(gf_isom_last_error(NULL))); return 0; } if (dump_out) { arg = strrchr(inName, GF_PATH_SEPARATOR); if (arg) { strcpy(rad, arg + 1); } else { strcpy(rad, inName); } } else { strcpy(rad, inName); } while (rad[strlen(rad)-1] != '.') rad[strlen(rad)-1] = 0; rad[strlen(rad)-1] = 0; if (dump_type == 3) { bifs3d_viewpoints_merger(file, szConfigFile, dump_w, dump_h, rad, dump_type, dump_out, fps_dump, frameID, dump_time); } else bifs_to_vid(file, szConfigFile, dump_w, dump_h, rad, dump_type, dump_out, fps_dump, frameID, dump_time); printf("\ndone\n"); gf_isom_delete(file); return 0; }