/* 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_SPECIAL_MODULE DEBUG_MODULE_GRAPHICS /* include your own header */ #include "media_container.h" #include "util/loading/resource_manager.h" /* header for debug output */ #include "debug.h" /** * Default constructor */ MediaContainer::MediaContainer(const std::string& filename) { // set the class id for the base object this->setClassID(CL_MEDIA_CONTAINER, "MediaContainer"); fps = 0; mediaLoaded = false; if (!filename.empty()) this->loadMedia(filename); } /** * Default destructor */ MediaContainer::~MediaContainer() { this->unloadMedia(); } void MediaContainer::unloadMedia() { // check whether a movie is already loaded if(!mediaLoaded) return; fps = 0; // clear texturelist this->clearLists(); if (glIsTexture(texture)) glDeleteTextures(1, &texture); // 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); mediaLoaded = false; } bool MediaContainer::loadMedia(const std::string& filename) { this->unloadMedia(); if(filename.empty()) return false; // check whether file exists if(!ResourceManager::isInDataDir(filename)) { PRINTF(1)("Could not find %s\n", filename.c_str()); return false; } // register all formats and codecs av_register_all(); // Open video file if (av_open_input_file(&format_context, ResourceManager::getFullName(filename).c_str(), NULL, 0, NULL) !=0 ) { PRINTF(1)("Could not open %s\n", ResourceManager::getFullName(filename).c_str()); return false; } // Retrieve stream information if (av_find_stream_info(format_context) < 0) { PRINTF(1)("Could not find stream information in %s\n", ResourceManager::getFullName(filename).c_str()); return false; } // Find the first video stream and take it video_stream = av_find_default_stream_index(format_context); if(video_stream == -1) { PRINTF(1)("Could not find a video stream in %s\n", ResourceManager::getFullName(filename).c_str()); return false; } // Get a pointer to the codec context for the video stream 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"); return false; } // Open codec if (avcodec_open(codec_context, codec) < 0) { PRINTF(1)("Could not open codec\n"); return false; } // 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]; // data buffer for the texture data = new uint8_t[codec_context->width*codec_context->height*3*sizeof(uint8_t)]; // Assign appropriate parts of buffer to image planes in RGB_frame 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); // NOTE: fix this fps problem!! if(fps < 0 || fps > 1000) fps = 30; // read the frames and save them in a sequence as textures this->loadFrames(); mediaLoaded = true; return true; } double MediaContainer::getFPS() { return this->fps; } void MediaContainer::loadFrames() { // empty texture list this->clearLists(); // go to the begin of the video av_seek_frame(format_context, video_stream, 0, AVSEEK_FLAG_BACKWARD); // get all the frames and save them in the sequence while(this->addFrame(this->getNextFrame()) != 0); } GLuint MediaContainer::getNextFrame() { // get next frame if(av_read_frame(format_context, &packet) >= 0) { // 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) { // 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); for(int i = 0; i < codec_context->height; i++) memcpy(&data[i*codec_context->width*3], ((AVPicture*)RGB_frame)->data[0]+i * ((AVPicture*)RGB_frame)->linesize[0], codec_context->width*sizeof(uint8_t)*3); // Create an OpenGL texture 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); // create the texture glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, codec_context->width, codec_context->height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); // build the MipMaps gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, codec_context->width, codec_context->height, GL_RGB, GL_UNSIGNED_BYTE, data); glBindTexture(GL_TEXTURE_2D, 0); return texture; } else { av_free_packet(&packet); return this->getNextFrame(); } } else { av_free_packet(&packet); return this->getNextFrame(); } } else return 0; }