Jump to content

  • Log In with Google      Sign In   
  • Create Account

FFMPEG and SDL


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
13 replies to this topic

#1 Spirrwell   Members   -  Reputation: 268

Like
0Likes
Like

Posted 06 May 2012 - 04:21 PM

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[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;
}

//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?

Sponsor:

#2 DemonRad   Members   -  Reputation: 290

Like
0Likes
Like

Posted 07 May 2012 - 12:31 AM

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!


#3 Spirrwell   Members   -  Reputation: 268

Like
0Likes
Like

Posted 07 May 2012 - 04:26 AM

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 the 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.



#4 Cornstalks   Crossbones+   -  Reputation: 6995

Like
0Likes
Like

Posted 07 May 2012 - 07:01 AM

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 ESRCH 3 /* No such process */ (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.
[ 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 ]

#5 Spirrwell   Members   -  Reputation: 268

Like
0Likes
Like

Posted 07 May 2012 - 02:12 PM

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 ESRCH 3 /* No such process */ (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.


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.

#6 Cornstalks   Crossbones+   -  Reputation: 6995

Like
1Likes
Like

Posted 07 May 2012 - 02:57 PM

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

[edit]

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

Edited by Cornstalks, 07 May 2012 - 03:01 PM.

[ 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 ]

#7 Spirrwell   Members   -  Reputation: 268

Like
0Likes
Like

Posted 07 May 2012 - 04:07 PM

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

[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.

Edited by Spirrwell, 07 May 2012 - 04:10 PM.


#8 Spirrwell   Members   -  Reputation: 268

Like
0Likes
Like

Posted 07 May 2012 - 04:26 PM

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?

#9 Cornstalks   Crossbones+   -  Reputation: 6995

Like
1Likes
Like

Posted 07 May 2012 - 06:29 PM

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));

sizeof(SDL_Movie) returns the size (in bytes) of an SDL_Movie, and malloc() then uses that to allocate that many bytes, which is the perfect size for storing one SDL_Movie structure. Don't forget to free() the memory (free(mov)) when you're done and want to deallocate the memory (probably at the end of your program, just before main() exits). If you don't free() what you malloc(), you'll leak memory (but if you free() 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 mov an actual structure and not a pointer (SDL_Movie mov;), so that you don't have to malloc() or free() anything. You would have to change the code to do mov. instead of mov-> and make SDL_LoadMovie return a plain SDL_Movie instead of a pointer.

Edited by Cornstalks, 07 May 2012 - 06:37 PM.

[ 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 ]

#10 Spirrwell   Members   -  Reputation: 268

Like
0Likes
Like

Posted 07 May 2012 - 08:01 PM

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));

sizeof(SDL_Movie) returns the size (in bytes) of an SDL_Movie, and malloc() then uses that to allocate that many bytes, which is the perfect size for storing one SDL_Movie structure. Don't forget to free() the memory (free(mov)) when you're done and want to deallocate the memory (probably at the end of your program, just before main() exits). If you don't free() what you malloc(), you'll leak memory (but if you free() 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 mov an actual structure and not a pointer (SDL_Movie mov;), so that you don't have to malloc() or free() anything. You would have to change the code to do mov. instead of mov-> and make SDL_LoadMovie return a plain SDL_Movie 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));
		    }


#11 Spirrwell   Members   -  Reputation: 268

Like
0Likes
Like

Posted 08 May 2012 - 08:11 PM

To simplify what I need to do is to create a RGB surface from an AVFrame. SDL has a function just for this:

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

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:

/* 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
. . .
. . .
. . .


#12 Cornstalks   Crossbones+   -  Reputation: 6995

Like
0Likes
Like

Posted 08 May 2012 - 09:04 PM

You can use sws_scale to convert the AVFrame to an RGB24/RGBA32 frame (PIX_FMT_RGB24 or PIX_FMT_RGBA), 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 AVFrame's data[0] 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 AVFrame's linesize[0] 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 AVFrame to this buffer that you can pass to SDL_CreateRGBSurfaceFrom(). 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):

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, ...);

That code is for RGB24 pixel data, but I don't know if SDL_CreateRGBSurfaceFrom() 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 sws_scale to make a RGBA frame and replace the 3 from above to a 4).
[ 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 ]

#13 Cornstalks   Crossbones+   -  Reputation: 6995

Like
0Likes
Like

Posted 09 May 2012 - 09:19 AM

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.

// 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);

[ 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 ]

#14 Spirrwell   Members   -  Reputation: 268

Like
0Likes
Like

Posted 09 May 2012 - 07:17 PM

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.

// 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);


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:

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;

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

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;
}

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:

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.
		    }
	    }
    }

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.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS