Advertisement

FFMPEG and SDL

Started by May 06, 2012 10:21 PM
12 comments, last by MichaelBarth 12 years, 6 months ago
This problem is just impossible so I'll get right to it. I've gotten a video to play in SDL using FFMPEG.

If I remove this one line of code:


printf("Creating test.\n");


It won't work. I've got everything working except with the slight problem that the program crashes when it gets to the end if I don't remove this line.

I don't get it printf is a simple print to console command, how the hell is it doing anything?

Alright before my head explodes here's my code:


//SDL_Movie.h
#include "SDL.h"
#include "libavcodec/avcodec.h"
#include "libavutil/avutil.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
int SDL_MOVIE_ERROR = 0;
typedef struct SDL_Movie {
int videoStream, audioStream;
AVFormatContext* pFormatCtx;
AVCodecContext* pCodecCtx;
AVCodecContext* aCodecCtx;
AVCodec* pCodec;
AVCodec* aCodec;
AVFrame* pFrame;
AVFrame* pFrameRGB;
} SDL_Movie;
char *SDL_Movie_GetError()
{
char* error;
switch (SDL_MOVIE_ERROR)
{
case 0:
error = "Unknown/No error.";
break;
case 1:
error = "Error opening file.";
break;
case 2:
error = "Couldn't find stream information.";
break;
case 3:
error = "Couldn't find video stream.";
break;
case 4:
error = "Couldn't find audio stream";
break;
case 5:
error = "Unsupported video codec.";
break;
case 6:
error = "Unsupported audio codec.";
break;
case 7:
error = "Video codec could not be opened.";
break;
case 8:
error = "Couldn't allocate frame.";
break;
default:
error = "Unknown/No error.";
break;
}
SDL_MOVIE_ERROR = 0;
return error;
}
SDL_Movie *SDL_LoadMovie(const char *file) {
SDL_Movie *mov;
printf("Entered.\n");
int ret;
mov->pFormatCtx = NULL;
if ((ret = avformat_open_input(&mov->pFormatCtx, file, NULL, NULL)) < 0)
{
printf("Failure.\n");
SDL_MOVIE_ERROR = 1;
return NULL;
}
printf("Opened.\n");
if (avformat_find_stream_info(mov->pFormatCtx, NULL) < 0)
{
SDL_MOVIE_ERROR = 2;
return NULL;
}
int i;
mov->videoStream = -1;
mov->audioStream = -1;
for (i=0; i < mov->pFormatCtx->nb_streams; i++)
{
if (mov->pFormatCtx->streams->codec->codec_type == AVMEDIA_TYPE_VIDEO && mov->videoStream < 0)
mov->videoStream=i;
if (mov->pFormatCtx->streams->codec->codec_type == AVMEDIA_TYPE_AUDIO && mov->audioStream < 0)
mov->audioStream = i;
}
if (mov->videoStream == -1)
{
SDL_MOVIE_ERROR = 3;
return NULL;
}
if (mov->audioStream == -1)
{
SDL_MOVIE_ERROR = 4;
return NULL;
}
mov->pCodecCtx = mov->pFormatCtx->streams[mov->videoStream]->codec;
mov->aCodecCtx = mov->pFormatCtx->streams[mov->audioStream]->codec;
if (!(mov->pCodec = avcodec_find_decoder(mov->pCodecCtx->codec_id)))
{
SDL_MOVIE_ERROR = 5;
return NULL;
}
if (!(mov->aCodec = avcodec_find_decoder(mov->aCodecCtx->codec_id)))
{
SDL_MOVIE_ERROR = 6;
return NULL;
}
if (avcodec_open(mov->pCodecCtx, mov->pCodec) < 0)
{
SDL_MOVIE_ERROR = 7;
return NULL;
}
avcodec_open(mov->aCodecCtx, mov->aCodec);
mov->pFrame = avcodec_alloc_frame();
mov->pFrameRGB = avcodec_alloc_frame();
if (!mov->pFrameRGB)
{
SDL_MOVIE_ERROR = 8;
return NULL;
}
uint8_t* buffer;
int numBytes = avpicture_get_size(PIX_FMT_RGB24, mov->pCodecCtx->width, mov->pCodecCtx->height);
buffer = (uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
avpicture_fill((AVPicture *)mov->pFrameRGB, buffer, PIX_FMT_RGB24, mov->pCodecCtx->width, mov->pCodecCtx->height);
int frameFinished;
AVPacket packet;
av_free(buffer);
return mov;
}



//main.c
#include <SDL.h>
#include "SDL_Movie.h"
#include <SDL_mixer.h>
#include <SDL_thread.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
SDL_Surface* screen = NULL;
int quit = 0;
SDL_Event event;
/*
void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) {
FILE *pFile;
char szFilename[32];
int y;
// Open file
sprintf(szFilename, "frame%d.ppm", iFrame);
pFile=fopen(szFilename, "wb");
if(pFile==NULL)
return;
// Write header
fprintf(pFile, "P6\n%d %d\n255\n", width, height);
// Write pixel data
for(y=0; y<height; y++)
fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);
// Close file
fclose(pFile);
}
*/
int main(int argc, char* argv[]) {
if (SDL_Init(SDL_INIT_EVERYTHING) < 0)
return 1;
atexit(SDL_Quit);
av_register_all();
printf("Creating test.\n");
SDL_Movie* test = SDL_LoadMovie("nuke.mp4");
printf("Created test.\n");
if (!test)
printf("%s", SDL_Movie_GetError());
if (!(screen = SDL_SetVideoMode(/*pCodecCtx->width, pCodecCtx->height,*/ 1680, 1050, 24, SDL_HWSURFACE | SDL_ANYFORMAT)))
return 1;
SDL_Overlay* bmp = SDL_CreateYUVOverlay(test->pCodecCtx->width, test->pCodecCtx->height, SDL_YV12_OVERLAY, screen);
int frameFinished;
AVPacket packet;
int i = 0;
while (av_read_frame(test->pFormatCtx, &packet) >= 0) {
if (packet.stream_index == test->videoStream) {
avcodec_decode_video2(test->pCodecCtx, test->pFrame, &frameFinished, &packet);
SDL_Rect rect;
if (frameFinished) {
SDL_LockYUVOverlay(bmp);
/*struct SwsContext* swsContext = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);
sws_scale(swsContext, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
if (++i <= 70)
SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i);
*/
AVPicture pict;
pict.data[0] = bmp->pixels[0];
pict.data[1] = bmp->pixels[2];
pict.data[2] = bmp->pixels[1];
pict.linesize[0] = bmp->pitches[0];
pict.linesize[1] = bmp->pitches[2];
pict.linesize[2] = bmp->pitches[1];
struct SwsContext* swsContext = sws_getContext(test->pCodecCtx->width, test->pCodecCtx->height, test->pCodecCtx->pix_fmt, test->pCodecCtx->width, test->pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
sws_scale(swsContext, test->pFrame->data, test->pFrame->linesize, 0, test->pCodecCtx->height, pict.data, pict.linesize);
SDL_UnlockYUVOverlay(bmp);
rect.x = 0;
rect.y = 0;
//rect.w = pCodecCtx->width;
//rect.h = pCodecCtx->height;
rect.w = 1680;
rect.h = 1050;
SDL_DisplayYUVOverlay(bmp, &rect);
}
}
}
//av_free(buffer);

av_free(test->pFrameRGB);
av_free(test->pFrame);
avcodec_close(test->pCodecCtx);
av_close_input_file(test->pFormatCtx);

return 0;
}


The main function is where I use the printf function that fixes almost everything.

If I don't have it, the program returns 3 when I call avformat_open_input in the SDL_LoadMovie function.

I just don't get it. Can anyone make sense of this?
have you checked if that library redefined printf? (adding some obscure side effect) it is really strange. Maybe some broken binary? :?

Peace and love, now I understand really what it means! Guardian Angels exist! Thanks!

Advertisement

have you checked if that library redefined printf? (adding some obscure side effect) it is really strange. Maybe some broken binary? :?


It would make no sense for something to redefine printf. I had a hard time with some version conflicts between tutorials when I was compiling FFMPEG so I just went with a precompiled binary. The only possible way I could see this making sense is that I compiled SDL with --disable-stdio-redirect to get it to stop creating the files "stderr.txt" and "stdout.txt." Even then it' a long shot because again, why would it make any sort of sense to redefine printf? It's just so weird. Here's something else that's weird, if I move t

he AVFormat* pFormatCtx at all within the struct I created, it will stop at the same point and return 3. I'll mess with it some more, but this basically killed my brain.

Something like this hints to me that you're invoking undefined behavior somewhere. Try:

  • Doing a clean rebuild of the project (no need to rebuild FFmpeg though)
  • Stepping through with a debugger, starting at the beginning of main and stepping through the program until it crashes


    As far as I can tell, a return code of 3 indicates [font=courier new,courier,monospace]ESRCH 3 /* No such process */ [font=arial,helvetica,sans-serif](assuming that's being returned from FFmpeg), which I have no clue about. Your best bet will be to step through it with a debugger though, line by line (after doing a clean rebuild). Post back here with your results.[/font][/font]
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

Something like this hints to me that you're invoking undefined behavior somewhere. Try:

  • Doing a clean rebuild of the project (no need to rebuild FFmpeg though)
  • Stepping through with a debugger, starting at the beginning of main and stepping through the program until it crashes

    As far as I can tell, a return code of 3 indicates [font=courier new,courier,monospace]ESRCH 3 /* No such process */ [font=arial,helvetica,sans-serif](assuming that's being returned from FFmpeg), which I have no clue about. Your best bet will be to step through it with a debugger though, line by line (after doing a clean rebuild). Post back here with your results.[/font][/font]


I assume by clean rebuild you mean right clicking on the project and clicking rebuild? I did that and ran the debugger and I got a SIGSEGV segmentation fault. I also caught where it was crashing. It crashed setting the pFormatCtx to NULL I removed that, and I got the same exact problem although with less information and I haven't found where it's crashing yet.

I remember reading about segmentation faults in earlier versions of FFMPEG if the AVFormatContext* is not set to NULL, but that was apparently fixed in the newer versions. What I was doing before I created the struct was simply create and initialize it as NULL in the same line. If I didn't it initialize it as NULL then, the program would crash. Unfortunately you can't set it to NULL in the same line in a struct, at least that I am aware of.
[font=courier new,courier,monospace]SDL_Movie *mov;[/font] creates a pointer, but it doesn't allocate memory for that pointer. Hence, when you do[font=courier new,courier,monospace] mov->pFormatCtx = NULL;[/font] you get a SIGSEV (which means you're doing things with memory that you don't own), because [font=courier new,courier,monospace]mov [/font]doesn't point to any valid memory. You have to allocate memory for mov first ([font=courier new,courier,monospace]SDL_Movie *mov = new SDL_Movie;[/font]).

[edit]

And yes, that's what I meant by doing a clean rebuild.
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]
Advertisement

[font=courier new,courier,monospace]SDL_Movie *mov;[/font] creates a pointer, but it doesn't allocate memory for that pointer. Hence, when you do[font=courier new,courier,monospace] mov->pFormatCtx = NULL;[/font] you get a SIGSEV (which means you're doing things with memory that you don't own), because [font=courier new,courier,monospace]mov [/font]doesn't point to any valid memory. You have to allocate memory for mov first ([font=courier new,courier,monospace]SDL_Movie *mov = new SDL_Movie;[/font]).

[edit]

And yes, that's what I meant by doing a clean rebuild.


I'm not too worried about a memory leak, but do I have to "delete mov"?

edit:

Actually I'm just using C right now, not C++ (for compatibility reasons, I'll leave it at that), but I assume I could use malloc? I've never really used it or know how to, but I'll look into it.
Yup I got it to work using malloc, but I have a couple of questions, how much memory should I allocate and how do I free memory allocated to mov?
Sorry, I made the poor assumption that you were using C++ (since that's what I use every day, my brain defaulted to that). It would be:


SDL_Movie *mov = (SDL_Movie*)malloc(sizeof(SDL_Movie));


[font=courier new,courier,monospace]sizeof(SDL_Movie)[/font] returns the size (in bytes) of an [font=courier new,courier,monospace]SDL_Movie[/font], and [font=courier new,courier,monospace]malloc()[/font] then uses that to allocate that many bytes, which is the perfect size for storing one [font=courier new,courier,monospace]SDL_Movie[/font] structure. Don't forget to [font=courier new,courier,monospace]free()[/font] the memory ([font=courier new,courier,monospace]free(mov)[/font]) when you're done and want to deallocate the memory (probably at the end of your program, just before [font=courier new,courier,monospace]main()[/font] exits). If you don't [font=courier new,courier,monospace]free()[/font] what you [font=courier new,courier,monospace]malloc()[/font], you'll leak memory (but if you [font=courier new,courier,monospace]free()[/font] too early and try to use the memory you just deallocated, you'll invoke undefined behavior and probably get a SIGSEV).

Alternatively, you could just get rid of the pointer all together. That is, make [font=courier new,courier,monospace]mov[/font] an actual structure and not a pointer ([font=courier new,courier,monospace]SDL_Movie mov;[/font]), so that you don't have to [font=courier new,courier,monospace]malloc()[/font] or [font=courier new,courier,monospace]free()[/font] anything. You would have to change the code to do [font=courier new,courier,monospace]mov.[/font] instead of [font=courier new,courier,monospace]mov->[/font] and make [font=courier new,courier,monospace]SDL_LoadMovie[/font] return a plain [font=courier new,courier,monospace]SDL_Movie[/font] instead of a pointer.
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

Sorry, I made the poor assumption that you were using C++ (since that's what I use every day, my brain defaulted to that). It would be:


SDL_Movie *mov = (SDL_Movie*)malloc(sizeof(SDL_Movie));


[font=courier new,courier,monospace]sizeof(SDL_Movie)[/font] returns the size (in bytes) of an [font=courier new,courier,monospace]SDL_Movie[/font], and [font=courier new,courier,monospace]malloc()[/font] then uses that to allocate that many bytes, which is the perfect size for storing one [font=courier new,courier,monospace]SDL_Movie[/font] structure. Don't forget to [font=courier new,courier,monospace]free()[/font] the memory ([font=courier new,courier,monospace]free(mov)[/font]) when you're done and want to deallocate the memory (probably at the end of your program, just before [font=courier new,courier,monospace]main()[/font] exits). If you don't [font=courier new,courier,monospace]free()[/font] what you [font=courier new,courier,monospace]malloc()[/font], you'll leak memory (but if you [font=courier new,courier,monospace]free()[/font] too early and try to use the memory you just deallocated, you'll invoke undefined behavior and probably get a SIGSEV).

Alternatively, you could just get rid of the pointer all together. That is, make [font=courier new,courier,monospace]mov[/font] an actual structure and not a pointer ([font=courier new,courier,monospace]SDL_Movie mov;[/font]), so that you don't have to [font=courier new,courier,monospace]malloc()[/font] or [font=courier new,courier,monospace]free()[/font] anything. You would have to change the code to do [font=courier new,courier,monospace]mov.[/font] instead of [font=courier new,courier,monospace]mov->[/font] and make [font=courier new,courier,monospace]SDL_LoadMovie[/font] return a plain [font=courier new,courier,monospace]SDL_Movie[/font] instead of a pointer.


Ah thank you so much! Everything is running fine now. I actually tried making it into structure instead of a pointer to see if that would fix the problem, but it never did, I might've done something wrong, but that doesn't matter now. The original reason I was doing it this way was to make it feel like an SDL struct pointer like SDL_Surface. There was a tutorial posted about using SDL with SMPEG on here, but the links are dead and it used the SDL_movie concept. So I'm making my own little SDL_movie.h for people to eventually use after I test it in a project.

Anyway, onto another problem (there always seems to be one when you program).

I'm not a fan of the SDL_Overlay and if I could use an SDL_Surface I could solve this problem I'm having when switching to fullscreen. If set the video mode with the SDL_ANYFORMAT flag, no video appears in fullscreen. If I use fullscreen, the end frame freezes for a second, and then moves on. Does anyone know how to convert an AVFrame to an SDL_Surface? For the YUV Overlay I would just do this:


SDL_Rect rect;
if (frameFinished) {
SDL_LockYUVOverlay(bmp);
AVPicture pict;
pict.data[0] = bmp->pixels[0];
pict.data[1] = bmp->pixels[2];
pict.data[2] = bmp->pixels[1];
pict.linesize[0] = bmp->pitches[0];
pict.linesize[1] = bmp->pitches[2];
pict.linesize[2] = bmp->pitches[1];
struct SwsContext* swsContext = sws_getContext(test->pCodecCtx->width, test->pCodecCtx->height, test->pCodecCtx->pix_fmt, test->pCodecCtx->width, test->pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
sws_scale(swsContext, test->pFrame->data, test->pFrame->linesize, 0, test->pCodecCtx->height, pict.data, pict.linesize);
SDL_UnlockYUVOverlay(bmp);
rect.x = 0;
rect.y = 0;
rect.w = 1680;
rect.h = 1050;
SDL_DisplayYUVOverlay(bmp, &rect);
SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, 0, 0, 0));
}

This topic is closed to new replies.

Advertisement