/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx 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, or (at your option) any later version. ### File Specific: main-programmer: David Hasenfratz, Stephan Lienhard co-programmer: */ /* this is for debug output. It just says, that all calls to PRINT() belong to the DEBUG_MODULE_MEDIA module For more information refere to https://www.orxonox.net/cgi-bin/trac.cgi/wiki/DebugOutput */ #define DEBUG_MODULE_MEDIA /* include your own header */ #include "media_container.h" /* header for debug output */ #include "debug.h" /** * Default constructor */ MediaContainer::MediaContainer(const char* filename) { this->init(); if (filename != NULL) this->loadMedia(filename); } MediaContainer::MediaContainer() { this->init(); } /** * Default destructor */ MediaContainer::~MediaContainer() { // delete all textures. while(!this->texture_list.empty()) { if (glIsTexture(this->texture_list.back())) glDeleteTextures(1, &this->texture_list.back()); this->texture_list.pop_back(); } glDeleteTextures(1, &texture); SDL_FreeSurface(surface); // Free the RGB image delete [] buffer; av_free(RGB_frame); /* Free the frame */ av_free(frame); /* Close the codec */ avcodec_close(codec_context); /* Close the video file */ av_close_input_file(format_context); } void MediaContainer::init() { /* set the class id for the base object */ this->setClassID(CL_MEDIA_CONTAINER, "MediaContainer"); /* register all formats and codecs */ av_register_all(); fps = 0; frame_num = 0; } GLuint MediaContainer::getFrame(int frame_number) { // seek doesnt work for the first two frames // you will get ugly fragments if(frame_number < 2) { // go to the begin of the video av_seek_frame(format_context, video_stream, 0, AVSEEK_FLAG_BACKWARD); frame_num = 0; return this->getNextFrame(); } else { // seeks to the nearest keyframe // NOTE: there is only about every 5s a keyframe! av_seek_frame(format_context, video_stream, frame_number, AVSEEK_FLAG_BACKWARD); // go from the keyframe to the exact position codec_context->hurry_up = 1; do { av_read_frame(format_context, &packet); if(packet.pts >= frame_number-1) break; int frame_finished; avcodec_decode_video(codec_context, frame, &frame_finished, packet.data, packet.size); av_free_packet(&packet); } while(1); codec_context->hurry_up = 0; frame_num = frame_number; return this->getNextFrame(); } } GLuint MediaContainer::getNextFrame() { /* get next frame */ if(av_read_frame(format_context, &packet) >= 0) { //this->printPacketInformation(); /* Is this a packet from the video stream? */ if(packet.stream_index == video_stream) { int frame_finished; // Decode video frame avcodec_decode_video(codec_context, frame, &frame_finished, packet.data, packet.size); // Free the packet that was allocated by av_read_frame av_free_packet(&packet); // Did we get a video frame? if(frame_finished) { frame_num++; //PRINTF(1)("frame_number: %i\n", this->getFrameNumber()); // Conversion from YUV to RGB // Most codecs return images in YUV 420 format // (one luminance and two chrominance channels, with the chrominance // channels samples at half the spatial resolution of the luminance channel) img_convert((AVPicture*)RGB_frame, PIX_FMT_RGB24, (AVPicture*)frame, codec_context->pix_fmt, codec_context->width, codec_context->height); picture = (AVPicture*)RGB_frame; data = 0; data = new uint8_t[codec_context->width*codec_context->height*3*sizeof(uint8_t)]; for(int i = 0; i < codec_context->height; i++) memcpy(&data[i*codec_context->width*3], picture->data[0]+i * picture->linesize[0],codec_context->width*sizeof(uint8_t)*3); surface = SDL_CreateRGBSurfaceFrom(data, codec_context->width, codec_context->height,24, codec_context->width*sizeof(uint8_t)*3, #if SDL_BYTEORDER == SDL_LIL_ENDIAN /* OpenGL RGBA masks */ 0x000000FF, 0x0000FF00, 0x00FF0000, 0 #else 0xFF000000, 0x00FF0000, 0x0000FF00, 0 #endif ); // NOTE: use glTexSubImage2D, it's faster!! // /* Create an OpenGL texture from the surface */ glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // build the Texture glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, surface->w, surface->h, 0, GL_RGB, GL_UNSIGNED_BYTE, surface->pixels); // build the MipMaps gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, surface->w, surface->h, GL_RGB, GL_UNSIGNED_BYTE, surface->pixels); glBindTexture(GL_TEXTURE_2D, 0); return texture; } } else { av_free_packet(&packet); this->getNextFrame(); } } else return NULL; } void MediaContainer::skipFrame(int num_frames) { } vector MediaContainer::getFrameList() { while((texture = this->getNextFrame()) != NULL) texture_list.push_back(texture); return texture_list; } void MediaContainer::saveCurrentFrame() { FILE *file; char filename[32]; int y; // Open file sprintf(filename, "frame%i.ppm", frame_num); file = fopen(filename, "wb"); if(file == NULL) return; // Write header fprintf(file, "P6\n%d %d\n255\n", codec_context->width, codec_context->height); // Write pixel data for(y = 0; y < codec_context->height; y++) fwrite(picture->data[0]+y * picture->linesize[0], 1, codec_context->width*3, file); // Close file fclose(file); PRINTF(1)("created file: %s\n", filename); } void MediaContainer::loadMedia(const char* filename) { /* Open video file */ if (av_open_input_file(&format_context, filename, NULL, 0, NULL) !=0 ) PRINTF(1)("Could not open %s\n", filename); /* Retrieve stream information */ if (av_find_stream_info(format_context) < 0) PRINTF(1)("Could not find stream information in %s\n", filename); // Dump information about file onto standard error //dump_format(pFormatCtx, 0, argv[1], false); /* Find the first video stream and take it */ video_stream = -1; for(int i = 0; i < format_context->nb_streams; i++) { // NOTE: different code for the 0.4.9-pre1 release of ffmpeg (tardis) // if(format_context->streams[i]->codec.codec_type == CODEC_TYPE_VIDEO) if(format_context->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) { video_stream = i; break; } } if(video_stream == -1) PRINTF(1)("Could not find a video stream in %s\n", filename); /* Get a pointer to the codec context for the video stream */ // NOTE: different code for the 0.4.9-pre1 release of ffmpeg (tardis) // codec_context = &format_context->streams[video_stream]->codec; codec_context = format_context->streams[video_stream]->codec; /* Find the decoder for the video stream */ codec = avcodec_find_decoder(codec_context->codec_id); if (codec == NULL) PRINTF(1)("Could not find codec\n"); /* Open codec */ if (avcodec_open(codec_context, codec) < 0) PRINTF(1)("Could not open codec\n"); // Allocate video frame frame = avcodec_alloc_frame(); RGB_frame = avcodec_alloc_frame(); // Determine required buffer size and allocate buffer num_bytes = avpicture_get_size(PIX_FMT_RGB24, codec_context->width, codec_context->height); buffer=new uint8_t[num_bytes]; // Assign appropriate parts of buffer to image planes in pFrameRGB avpicture_fill((AVPicture *)RGB_frame, buffer, PIX_FMT_RGB24, codec_context->width, codec_context->height); // Calculate fps fps = av_q2d(format_context->streams[video_stream]->r_frame_rate); // duration duration = format_context->duration / 1000000LL; frame_num = 0; } int MediaContainer::getHeight() { return codec_context->height; } int MediaContainer::getWidth() { return codec_context->width; } int MediaContainer::getFrameNumber() { return frame_num; } double MediaContainer::getFPS() { return this->fps; } void MediaContainer::printMediaInformation() { PRINTF(1)("========================\n"); PRINTF(1)("========================\n"); PRINTF(1)("= MEDIACONTAINER =\n"); PRINTF(1)("========================\n"); PRINTF(1)("========================\n"); PRINTF(1)("= AVFormatContext =\n"); PRINTF(1)("========================\n"); PRINTF(1)("filename: %s\n", format_context->filename); PRINTF(1)("nb_streams: %i\n", format_context->nb_streams); PRINTF(1)("duration: (%02d:%02d:%02d)\n", duration/3600, (duration%3600)/60, duration%60); PRINTF(1)("file_size: %ikb\n", format_context->file_size/1024); PRINTF(1)("bit_rate: %ikb/s\n", format_context->bit_rate/1000); PRINTF(1)("nb_frames: %i\n", format_context->streams[video_stream]->nb_frames); PRINTF(1)("r_frame_rate: %i\n", format_context->streams[video_stream]->r_frame_rate.num); PRINTF(1)("fps: %0.2f\n", av_q2d(format_context->streams[video_stream]->r_frame_rate)); PRINTF(1)("========================\n"); PRINTF(1)("= AVCodecContext =\n"); PRINTF(1)("========================\n"); PRINTF(1)("width: %i\n", codec_context->width); PRINTF(1)("height: %i\n", codec_context->height); PRINTF(1)("time_base.den: %i\n", codec_context->time_base.den); PRINTF(1)("time_base.num: %i\n", codec_context->time_base.num); PRINTF(1)("========================\n"); PRINTF(1)("= AVCodec =\n"); PRINTF(1)("========================\n"); PRINTF(1)("codec name: %s\n", codec->name); PRINTF(1)("========================\n"); PRINTF(1)("========================\n"); } void MediaContainer::printPacketInformation() { PRINTF(1)("========================\n"); PRINTF(1)("========================\n"); PRINTF(1)("= AVPacket =\n"); PRINTF(1)("========================\n"); PRINTF(1)("pts: %i\n", packet.pts); PRINTF(1)("dts: %i\n", packet.dts); PRINTF(1)("size: %i\n", packet.size); PRINTF(1)("stream_index: %i\n", packet.stream_index); PRINTF(1)("duration: %i\n", packet.duration); PRINTF(1)("pos: %i\n", packet.pos); PRINTF(1)("========================\n"); PRINTF(1)("========================\n"); }