Sign in to follow this  

FFMPEG and SDL

This topic is 2046 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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:

[CODE]
printf("Creating test.\n");
[/CODE]

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:

[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[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && mov->videoStream < 0)
mov->videoStream=i;
if (mov->pFormatCtx->streams[i]->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;
}
[/CODE]

[CODE]
//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;
}
[/CODE]

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?

Share this post


Link to post
Share on other sites
[quote name='DemonRad' timestamp='1336372279' post='4937989']
have you checked if that library redefined printf? (adding some obscure side effect) it is really strange. Maybe some broken binary? :?
[/quote]

[left]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[size=3]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.[/size][/left]

Share this post


Link to post
Share on other sites
Something like this hints to me that you're invoking undefined behavior somewhere. Try:
[list]
[*]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
[/list]

As far as I can tell, a return code of 3 indicates [font=courier new,courier,monospace][url="http://www.barricane.com/c-error-codes-include-errno"]ESRCH 3 /* No such process */[/url] [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]

Share this post


Link to post
Share on other sites
[quote name='Cornstalks' timestamp='1336395669' post='4938054']
Something like this hints to me that you're invoking undefined behavior somewhere. Try:[list]
[*]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
[/list]
As far as I can tell, a return code of 3 indicates [font=courier new,courier,monospace][url="http://www.barricane.com/c-error-codes-include-errno"]ESRCH 3 /* No such process */[/url] [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]
[/quote]

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.

Share this post


Link to post
Share on other sites
[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. Edited by Cornstalks

Share this post


Link to post
Share on other sites
[quote name='Cornstalks' timestamp='1336424250' post='4938185']
[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.
[/quote]

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. Edited by Spirrwell

Share this post


Link to post
Share on other sites
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:

[code]
SDL_Movie *mov = (SDL_Movie*)malloc(sizeof(SDL_Movie));
[/code]

[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. Edited by Cornstalks

Share this post


Link to post
Share on other sites
[quote name='Cornstalks' timestamp='1336436983' post='4938237']
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:

[code]
SDL_Movie *mov = (SDL_Movie*)malloc(sizeof(SDL_Movie));
[/code]

[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.
[/quote]

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:

[CODE]
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));
}
[/CODE]

Share this post


Link to post
Share on other sites
To simplify what I need to do is to create a RGB surface from an AVFrame. SDL has a function just for this:

[CODE]
SDL_Surface *SDL_CreateRGBSurfaceFrom(void *pixels,
int width, int height, int depth, int pitch,
Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask);
[/CODE]

The function is straightforward, I just don't know how to get from an AVFrame to raw pixel data readable into this function. I've never been good with this. The Rmask, Gmask, Bmask stuff I believe has to do with the byte order. For another similar function which is SDL_CreateRGBSurface, SDL has an example which includes the following:

[CODE]
/* Create a 32-bit surface with the bytes of each pixel in R,G,B,A order,
as expected by OpenGL for textures */
SDL_Surface *surface;
Uint32 rmask, gmask, bmask, amask;
/* SDL interprets each pixel as a 32-bit number, so our masks must depend
on the endianness (byte order) of the machine */
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
rmask = 0xff000000;
gmask = 0x00ff0000;
bmask = 0x0000ff00;
amask = 0x000000ff;
#else
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = 0xff000000;
#endif
. . .
. . .
. . .
[/CODE]

Share this post


Link to post
Share on other sites
You can use [font=courier new,courier,monospace]sws_scale[/font] to convert the [font=courier new,courier,monospace]AVFrame[/font] to an RGB24/RGBA32 frame ([font=courier new,courier,monospace]PIX_FMT_RGB24[/font] or [font=courier new,courier,monospace]PIX_FMT_RGBA[/font]), similar to how you're already doing it. I don't have time right now to post some sample code, but I'll try and do that in the morning. The RGB pixel data will be stored in the [font=courier new,courier,monospace]AVFrame[/font]'s [font=courier new,courier,monospace]data[0][/font] member, in the format: [r, g, b, r, g, b, r, g, b, ...] or [r, g, b, a, r, g, b, a, r, g, b, a, ...] (regardless of endianness). The [font=courier new,courier,monospace]AVFrame[/font]'s [font=courier new,courier,monospace]linesize[0][/font] member will tell you how many bytes are in each row of pixel data in the frame (it'll be at least 3 * the frame's width (or 4 * the frame's with, if RGBA), but it can be slightly more (for padding... they do this so they can use certain SIMD instructions for optimizations)). You should be able to create a byte buffer the size of width * height * 3 (or * 4 if RGBA), and copy each row of pixels in the [font=courier new,courier,monospace]AVFrame[/font] to this buffer that you can pass to [font=courier new,courier,monospace]SDL_CreateRGBSurfaceFrom()[/font]. I'll post a sample tomorrow. Here's some small psuedo-code until I can write some proper stuff tomorrow (it's C++, sorry, but the concept is there):

[code]
char *buffer = new char[frameWidth * frameHeight];
AVFrame *frame = yourRgb24Frame;
for (int i = 0; i < frameHeight; ++i)
{
// The primary purpose I don't just copy the frame->data[0] straight into the buffer is because frame->linesize[0] may not equal
// the frame's width
std::copy(frame->data[0] + i * frame->linesize[0], frame->data[0] + i * frame->linesize[0] + 3 * frameWidth, buffer + i * frameWidth);
}
SDL_CreateRGBSurfaceFrom(buffer, ...);
[/code]

That code is for RGB24 pixel data, but I don't know if [font=courier new,courier,monospace]SDL_CreateRGBSurfaceFrom()[/font] expects a fourth alpha channel to be in the buffer. If so, the code will need to be modified (the simplest would probably be to use [font=courier new,courier,monospace]sws_scale[/font] to make a RGBA frame and replace the 3 from above to a 4).

Share this post


Link to post
Share on other sites
Alright, here's some code. It's not quite fully functional, but it has the basics of each step. You'll probably need to separate out each chunk of put it where it belongs. I'm not sure about how to use SDL, so you're kind of on your own there (unless someone else can comment on that). There could be some errors in this code sample, but it should help illustrate the idea. You could also safely make some alterations to this, but I'll leave that up to you. I went for simplicity in this small sample.

[code] // I'm assuming you've already opened the video...
SwsContext* imageConvertContext = NULL;
int frameFinished = 0;
int videoStreamIndex = 0; // I'm making this up, of course
char* buffer;
AVPacket packet;
AVFrame* decodingFrame;
AVFrame* rgbFrame;

buffer = (char*)malloc(codecContext->width * codecContext->height);
decodingFrame = avcodec_alloc_frame();
rgbFrame = avcodec_alloc_frame();
av_init_packet(&packet);

// Use PIX_FMT_RGBA instead if you need rgba...
if (avpicture_alloc((AVPicture*)rgbFrame, PIX_FMT_RGB24, codecContext->width, codecContext->height) != 0)
{
// error
}

imageConvertContext = sws_getCachedContext(NULL, // current swscontext
codecContext->width, // source width
codecContext->height, // source height
codecContext->pix_fmt, // source format
codecContext->width, // dest width
codecContext->height, // dest height
PIX_FMT_RGB24, // dest format
SWS_FAST_BILINEAR, // flags
NULL, // source filter
NULL, // dest filter
NULL); // params

if (m_imageConvertContext == NULL)
{
// error...
}

// This is where the decoding and converting part go. I'll assume you've got a decoding loop already.
// This isn't quite a proper loop, but it has the important part of using sws_scale()
av_free_packet(&packet);
while (av_read_frame(formatContext, &packet) == 0)
{
if (packet.stream_index == videoStreamIndex)
{
avcodec_decode_video2(codecContext, decodingFrame, &frameFinished, &packet);
if (frameFinished)
{
sws_scale(imageConvertContext,
decodingFrame->data,
decodingFrame->linesize,
0,
decodingFrame->height,
rgbFrame->data,
rgbFrame->linesize);
break;
}

}

av_free_packet(&packet);
}

// Now copy the rgbFrame into the buffer
for (int i = 0; i < codecContext->height; ++i)
{
memcpy(buffer + i * codecContext->width, rgbFrame->data[0] + i * rgbFrame->linesize[0], codecContext->width * 3); // change the 3 to a 4 if doing rgba
}
SDL_CreateRGBSurfaceFrom(buffer, ...);

// Clean up when you're done... probably at the end of your program
sws_freeContext(imageConvertContext);
avpicture_free((AVPicture*)rgbFrame);
av_free(rgbFrame);
av_free(decodingFrame);[/code]

Share this post


Link to post
Share on other sites
[quote name='Cornstalks' timestamp='1336576776' post='4938686']
Alright, here's some code. It's not quite fully functional, but it has the basics of each step. You'll probably need to separate out each chunk of put it where it belongs. I'm not sure about how to use SDL, so you're kind of on your own there (unless someone else can comment on that). There could be some errors in this code sample, but it should help illustrate the idea. You could also safely make some alterations to this, but I'll leave that up to you. I went for simplicity in this small sample.

[code] // I'm assuming you've already opened the video...
SwsContext* imageConvertContext = NULL;
int frameFinished = 0;
int videoStreamIndex = 0; // I'm making this up, of course
char* buffer;
AVPacket packet;
AVFrame* decodingFrame;
AVFrame* rgbFrame;

buffer = (char*)malloc(codecContext->width * codecContext->height);
decodingFrame = avcodec_alloc_frame();
rgbFrame = avcodec_alloc_frame();
av_init_packet(&packet);

// Use PIX_FMT_RGBA instead if you need rgba...
if (avpicture_alloc((AVPicture*)rgbFrame, PIX_FMT_RGB24, codecContext->width, codecContext->height) != 0)
{
// error
}

imageConvertContext = sws_getCachedContext(NULL, // current swscontext
codecContext->width, // source width
codecContext->height, // source height
codecContext->pix_fmt, // source format
codecContext->width, // dest width
codecContext->height, // dest height
PIX_FMT_RGB24, // dest format
SWS_FAST_BILINEAR, // flags
NULL, // source filter
NULL, // dest filter
NULL); // params

if (m_imageConvertContext == NULL)
{
// error...
}

// This is where the decoding and converting part go. I'll assume you've got a decoding loop already.
// This isn't quite a proper loop, but it has the important part of using sws_scale()
av_free_packet(&packet);
while (av_read_frame(formatContext, &packet) == 0)
{
if (packet.stream_index == videoStreamIndex)
{
avcodec_decode_video2(codecContext, decodingFrame, &frameFinished, &packet);
if (frameFinished)
{
sws_scale(imageConvertContext,
decodingFrame->data,
decodingFrame->linesize,
0,
decodingFrame->height,
rgbFrame->data,
rgbFrame->linesize);
break;
}

}

av_free_packet(&packet);
}

// Now copy the rgbFrame into the buffer
for (int i = 0; i < codecContext->height; ++i)
{
memcpy(buffer + i * codecContext->width, rgbFrame->data[0] + i * rgbFrame->linesize[0], codecContext->width * 3); // change the 3 to a 4 if doing rgba
}
SDL_CreateRGBSurfaceFrom(buffer, ...);

// Clean up when you're done... probably at the end of your program
sws_freeContext(imageConvertContext);
avpicture_free((AVPicture*)rgbFrame);
av_free(rgbFrame);
av_free(decodingFrame);[/code]
[/quote]

Man, it's amazing to work with so much code, Just last year I couldn't possibly conceive of doing something like this... Ah well enough of that, I'm getting another segmentation fault when attempting to blit it to the screen.

I modified my struct with the appropriate variables I'll need to manipulate:

[CODE]
typedef struct SDL_Movie {
int videoStream, audioStream;
AVFormatContext* pFormatCtx;
AVCodecContext* pCodecCtx;
AVCodecContext* aCodecCtx;
AVCodec* pCodec;
AVCodec* aCodec;
AVFrame* pFrame;
AVFrame* pFrameRGB;
char* buffer;
} SDL_Movie;
[/CODE]

And I've also modified my loading function to compensate for the change:

[CODE]
SDL_Movie *SDL_LoadMovie(const char *file) {
SDL_Movie *mov = (SDL_Movie*)malloc(sizeof(SDL_Movie));
int ret;
if ((ret = avformat_open_input(&mov->pFormatCtx, file, NULL, NULL)) < 0)
{
SDL_MOVIE_ERROR = 1;
return NULL;
}
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[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && mov->videoStream < 0)
mov->videoStream=i;
if (mov->pFormatCtx->streams[i]->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;
}
//int numBytes = avpicture_get_size(PIX_FMT_RGB24, mov->pCodecCtx->width, mov->pCodecCtx->height);
mov->buffer = (char*)malloc(mov->pCodecCtx->width * mov->pCodecCtx->height);
//avpicture_fill((AVPicture *)mov->pFrameRGB, mov->buffer, PIX_FMT_RGB24, mov->pCodecCtx->width, mov->pCodecCtx->height);
if (avpicture_alloc((AVPicture*)mov->pFrameRGB, PIX_FMT_RGB24, mov->pCodecCtx->width, mov->pCodecCtx->height) != 0)
return NULL;
return mov;
}
[/CODE]

Going back to my main function I created an SDL_Surface* bmp as well as the Uint32s for rmask, gmask, and etc. that were defined in my previous example. So here's where the conversion and stuff is actually taking place:

[CODE]
struct SwsContext* iCCtx = NULL;
int frameFinished;
AVPacket packet;
av_init_packet(&packet);
int i = 0;
iCCtx = sws_getCachedContext(NULL,
test->pCodecCtx->width,
test->pCodecCtx->height,
test->pCodecCtx->pix_fmt,
test->pCodecCtx->width,
test->pCodecCtx->height,
PIX_FMT_RGB24,
SWS_FAST_BILINEAR,
NULL,
NULL,
NULL);
if (!iCCtx)
return -1;
printf("While loop here:\n");
while (av_read_frame(test->pFormatCtx, &packet) >= 0) {
if (packet.stream_index == test->videoStream) {
avcodec_decode_video2(test->pCodecCtx, test->pFrame, &frameFinished, &packet);
printf("Decoding.\n");
SDL_Rect rect;
if (frameFinished) {
//SDL_LockYUVOverlay(bmp);
SDL_LockSurface(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(iCCtx, test->pFrame->data, test->pFrame->linesize, 0, test->pFrame->height, test->pFrameRGB->data, test->pFrame->linesize);
//sws_scale(iCCtx, test->pFrame->data, test->pFrame->linesize, 0, test->pCodecCtx->height, pict.data, pict.linesize);
printf("SWSSCALE Passed\n");
//SDL_UnlockYUVOverlay(bmp);
rect.x = 0;
rect.y = 0;
rect.w = 1680;
rect.h = 1050;
//SDL_DisplayYUVOverlay(bmp, &rect);
printf("For loop here:\n");
for (i = 0; i < test->pCodecCtx->height; ++i)
{
memcpy(test->buffer + i * test->pCodecCtx->width, test->pFrameRGB->data[0] + i * test->pFrameRGB->linesize[0], test->pCodecCtx->width * 3); // change the 3 to a 4 if doing rgba
}
printf("End for loop.\n");
bmp = SDL_CreateRGBSurfaceFrom(test->buffer, test->pCodecCtx->width, test->pCodecCtx->height, 24, test->pFrameRGB->linesize, rmask, gmask, bmask, amask); //Creating RGB surface from buffer.
SDL_UnlockSurface(bmp);
printf("Surface created\n");
if (!bmp)
printf("%s\n", SDL_GetError());
SDL_BlitSurface(bmp, NULL, screen, NULL); //Blit SDL_Surface* bmp
printf("Blitted\n");
SDL_Flip(screen); //Updates screen
SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, 0, 0, 0)); //Clears screen to prevent nasty effects.
}
}
}
[/CODE]

I assume I'm doing something wrong when I'm creating the RGB surface. If it would be any easier to you since you aren't familiar with SDL, I would be just fine with an OpenGL texture. The actual game I plan on doing will be in SDL_OpenGL. (basically OpenGL that instead of using GLUT, it uses SDL) I just need to play these two intro videos, and one of them with audio and both are at 720p. I just would've really liked to make something for SDL, but I guess I'm just not ready.

Either way, any help is appreciated.

Share this post


Link to post
Share on other sites

This topic is 2046 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this