Playing Back Video with SDL
Hello!
I was wondering if SDL can play back video files somehow. I'm sure it can, especially since it's used to create media players on non-Windows platforms, but... how?
Would I be able to play back an Indio or DivX encoded movie file? I'd like to be able to play back a video and draw over it. Is this possible? What do I need?
All help is appreciated! :-)
Sure, you can pull it off using ffmpeg, the source comes with an SDL sample which is rather complex, but if you google for some examples, you should be able to find code to just play the video.
I currently use it to play videos on OpenGL textures.
I currently use it to play videos on OpenGL textures.
I'm confused.
I downloaded the only available official release of ffmpeg and got a huge jumble of source files. I don't know how to build a library out of any of this and it seems to be an application for converting audio and video content rather than a library.
So... how do I use this? Haha, sorry for my incompetence. :-)
I downloaded the only available official release of ffmpeg and got a huge jumble of source files. I don't know how to build a library out of any of this and it seems to be an application for converting audio and video content rather than a library.
So... how do I use this? Haha, sorry for my incompetence. :-)
I have two pages, here and here that go over using SMPEG in SDL. Everything you need for using that library, should be there.
I can't remember the link off hand, but you could use the SDL example for loading ROQ(Quake and Doom 3 movie files), the implementation was rather simple.
Quote:Original post by eviltwigflipper
I can't remember the link off hand, but you could use the SDL example for loading ROQ(Quake and Doom 3 movie files), the implementation was rather simple.
Very interesting! I did a little searching and it seems you can get a plugin for ffmpeg to make those files, as well as some other stuff. I will look into this myself later on when time permits, thanks for sharing! One question though, what is the license of that format? If it is propierty, then it'd be better not to use it in your games, which also I should add, SMPEG is GPL, so you have to release all source if you want to use it. Just something to keep in mind as well.
Well, with ffmpeg you have to compile from sources before using it, they recomend always grabbing the latest code from CVS, I use rpms for Linux and compile using MinGW and MSYS on Windows, its not that hard once you figure out which parameters to pass to configure.
What you need are the 3 AV libraries (avcodec.dll,avformat.dll and avutil.dll) build and then link them to your program.
Basically what you do is grab a stream from the video file (which may be Video, Sound or Subtitles) and in the case of the video one, start decoding frames, rendering them to SDL surfaces and then blit them to the screen.
You do HAVE to be very careful about what format you use, MPEG is pattent encumbered, and so is DivX, I really recomend theora, which uses the same philosophy as Vorbis does for audio (in fact it uses Vorbis for audio streams).
ffmpeg can handle theora.
Here is a link I found when writting my video to texture code, and here is my code based on that, sorry for the sloppiness and the mess, I focused on getting something working I could go back and carefully work out better later:
What you need are the 3 AV libraries (avcodec.dll,avformat.dll and avutil.dll) build and then link them to your program.
Basically what you do is grab a stream from the video file (which may be Video, Sound or Subtitles) and in the case of the video one, start decoding frames, rendering them to SDL surfaces and then blit them to the screen.
You do HAVE to be very careful about what format you use, MPEG is pattent encumbered, and so is DivX, I really recomend theora, which uses the same philosophy as Vorbis does for audio (in fact it uses Vorbis for audio streams).
ffmpeg can handle theora.
Here is a link I found when writting my video to texture code, and here is my code based on that, sorry for the sloppiness and the mess, I focused on getting something working I could go back and carefully work out better later:
// (c) 2005 Rodrigo Hernandez#ifndef VIDEO_H#define VIDEO_H#include <ffmpeg/avcodec.h>#include <ffmpeg/avformat.h>#include <string>#include "SDL.h"#include "SDL_thread.h"#include "SDL_opengl.h"class Video{ public: Video(); ~Video(); bool Load(const char*); void Play(); void Stop(); void Pause(); void Update(); // Called every frame or so to advance playing inline GLuint GetTexture(){return videotex;}; inline unsigned int GetTexWidth() {return width;}; inline unsigned int GetTexHeight(){return height;}; inline unsigned int GetVidWidth() {return pCodecCtx->width;}; inline unsigned int GetVidHeight(){return pCodecCtx->height;}; private:/* static int video_thread(void *); */ static int audio_thread(void *); std::string filename; bool ispaused; bool stop; GLuint videotex; // SDL_Thread *vid_thread; AVFormatContext *pFormatCtx; AVCodecContext *pCodecCtx; AVCodec *pCodec; AVFrame *pFrame; AVFrame *pFrameRGB; int videoStream; uint8_t *buffer; int numBytes; AVPacket packet; unsigned int width; unsigned int height; Uint32 time_between_frames; Uint32 ticks;};#endif
// (c) 2005 Rodrigo Hernandez#include <assert.h>#include "video.h"inline void FindBestTextureSize(unsigned int &w,unsigned int &h){ Uint32 temp = 1; while(w>temp) { temp = temp << 1; } if(w==h) { w=h=temp; return; } w=temp; temp = 1; while(h>temp) { temp = temp << 1; } h=temp;}Video::Video(){ ispaused=false; stop=false; videotex=0; pFormatCtx=NULL; //vid_thread=NULL; buffer=NULL;}Video::~Video(){ //SDL_WaitThread(vid_thread,NULL); stop=true; // Free the RGB image if(buffer!=NULL) delete [] buffer; av_free(pFrameRGB); // Free the YUV frame av_free(pFrame); // Close the codec avcodec_close(pCodecCtx); // Close the video file if(pFormatCtx!=NULL) { // Close the video file av_close_input_file(pFormatCtx); } if(videotex!=0) { glDeleteTextures(1,&videotex); }}bool Video::Load(const char* videofile){ GLenum error; //filename=videofile; // this should probably be moved somewhere else av_register_all(); // Open video file if(av_open_input_file(&pFormatCtx, videofile, NULL, 0, NULL)!=0) { fprintf(stderr,"Couldn't open file %s\n",videofile); return false; // Couldn't open file } // Retrieve stream information if(av_find_stream_info(pFormatCtx)<0) { fprintf(stderr,"Couldn't find stream information in %s\n",videofile); return false; // Couldn't find stream information } // Dump information about file onto standard error dump_format(pFormatCtx, 0, videofile, false); // Create Texture for rendering the Video glGenTextures(1, &videotex); fprintf(stderr,"Tex Name %u\n",videotex); if((error=glGetError())!=GL_NO_ERROR) { fprintf(stderr,"OpenGL Error %d in Load\n",error); } glBindTexture(GL_TEXTURE_2D,videotex); if((error=glGetError())!=GL_NO_ERROR) { if(error==GL_INVALID_ENUM) { fprintf(stderr,"glBindTexture OpenGL Error GL_INVALID_ENUM Tex Name %u\n",videotex); } else if(error==GL_INVALID_OPERATION) { fprintf(stderr,"glBindTexture OpenGL Error GL_INVALID_OPERATION Tex Name %u\n",videotex); } } // Find the first video stream videoStream=-1; for(int i=0; i<pFormatCtx->nb_streams; i++) { if(pFormatCtx->streams->codec->codec_type==CODEC_TYPE_VIDEO) { videoStream=i; break; } } if(videoStream==-1) { fprintf(stderr,"No video stream\n"); return false; // Didn't find a video stream } // Get a pointer to the codec context for the video stream pCodecCtx=pFormatCtx->streams[videoStream]->codec; // Find the decoder for the video stream pCodec=avcodec_find_decoder(pCodecCtx->codec_id); if(pCodec==NULL) return false; // Codec not found // Inform the codec that we can handle truncated bitstreams -- i.e., // bitstreams where frame boundaries can fall in the middle of packets if(pCodec->capabilities & CODEC_CAP_TRUNCATED) pCodecCtx->flags|=CODEC_FLAG_TRUNCATED; // Open codec if(avcodec_open(pCodecCtx, pCodec)<0) return false; // Could not open codec // Allocate video frame pFrame=avcodec_alloc_frame(); // Allocate an AVFrame structure pFrameRGB=avcodec_alloc_frame(); if(pFrameRGB==NULL) return false; // Determine required buffer size and allocate buffer numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height); buffer=new uint8_t[numBytes]; // Assign appropriate parts of buffer to image planes in pFrameRGB avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height); time_between_frames=int(av_q2d(pCodecCtx->time_base)*1000); fprintf(stderr,"%d/%d TBF %u\n", pCodecCtx->time_base.num, pCodecCtx->time_base.den,time_between_frames); // Allocate OpenGL Texture width =pCodecCtx->width; height=pCodecCtx->height; FindBestTextureSize(width,height); fprintf(stderr,"Texture size %dx%d\n",width,height); //unsigned char *data=new unsigned char[width*height*3]; glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); if((error=glGetError())!=GL_NO_ERROR) { fprintf(stderr,"glTexImage2D OpenGL Error %d\n",error); } //delete [] data; glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); if((error=glGetError())!=GL_NO_ERROR) { fprintf(stderr,"OpenGL Error\n"); } glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); if((error=glGetError())!=GL_NO_ERROR) { fprintf(stderr,"OpenGL Error\n"); } return true;}void Video::Play(){ ispaused=false; stop=false;}void Video::Stop(){ stop=true;}void Video::Pause(){ if(ispaused)ispaused=false; else ispaused=true;}void Video::Update(){ GLenum error; int frameFinished; if(av_read_frame(pFormatCtx, &packet)>=0) { // Is this a packet from the video stream? if(packet.stream_index==videoStream) { // Decode video frame avcodec_decode_video(pCodecCtx, pFrame, &frameFinished, packet.data, packet.size); // Did we get a video frame? if(frameFinished) { // Convert the image from its native format to RGB img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24, (AVPicture*)pFrame, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height); // --- Add image to texture code here --- // glBindTexture(GL_TEXTURE_2D,videotex); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, pCodecCtx->width, pCodecCtx->height, GL_RGB, GL_UNSIGNED_BYTE, pFrameRGB->data[0]); if((error=glGetError())!=GL_NO_ERROR) { if(error==GL_INVALID_OPERATION) { fprintf(stderr,"glTexSubImage Error GL_INVALID_OPERATION\n"); } else { fprintf(stderr,"glTexSubImage Error %d\n",error); } } SDL_Delay(time_between_frames); } } // Free the packet that was allocated by av_read_frame av_free_packet(&packet); }}int Video::audio_thread(void *arg){ Video *v=(Video *)arg; return 0;}
So it seems that I'd be able to use more formats with ffmpeg than with SMpeg, which apparently only plays back MPEG encoded files.
I'm already in the process of creating a small wrapper for SMpeg to see if I can get it up and working, but I'll see what I can make of the ffmpeg files I downloaded. It's still a bit confusing. :-P
Thanks for all of the help!
I'm already in the process of creating a small wrapper for SMpeg to see if I can get it up and working, but I'll see what I can make of the ffmpeg files I downloaded. It's still a bit confusing. :-P
Thanks for all of the help!
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement