FFmpeg and SDL integration to achieve video playback

This blog is excerpted from the course “Production of Video Player Based on FFmpeg + SDL” by Master Lei Xiaohua. It is very suitable for beginners in audio and video. I would like to thank Lei Shen for his guidance!

Integration of FFmpeg and SDL to achieve video playback

Integration
? FFmpeg decoder implemented: video file->YUV
? SDL video display is implemented: YUV->Screen
? After the integration of FFmpeg + SDL, it is realized: video file->YUV->screen

Add processing events, such as moving video window, zooming, etc.

/**
 * The simplest FFmpeg-based video player 2 (SDL upgraded version)
 * Simplest FFmpeg Player 2(SDL Update)
 *
 * Lei Xiaohua
 *[email protected]
 * Communication University of China/Digital TV Technology
 * Communication University of China / Digital TV Technology
 * http://blog.csdn.net/leixiaohua1020
 *
 * This program implements the decoding and display of video files (supports HEVC, H.264, MPEG2, etc.).
 * It is the simplest tutorial on FFmpeg video decoding.
 * By studying this example, you can understand the decoding process of FFmpeg.
 * This version uses the SDL message mechanism to refresh the video screen.
 * This software is a simplest video player based on FFmpeg.
 * Suitable for beginners of FFmpeg.
 *
 */

#include <stdio.h>

#define __STDC_CONSTANT_MACROS
#define SDL_MAIN_HANDLED

extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include "SDL.h"
};


// Custom event
// Refresh Event refresh video event
#define SFM_REFRESH_EVENT (SDL_USEREVENT + 1)

#define SFM_BREAK_EVENT (SDL_USEREVENT + 2)

int thread_exit = 0;

int sfp_refresh_thread(void* opaque) {
thread_exit = 0;
while (!thread_exit) {
SDL_Event event;
event.type = SFM_REFRESH_EVENT;
SDL_PushEvent( & amp;event);
SDL_Delay(40);
}
thread_exit = 0;
//Break
SDL_Event event;
event.type = SFM_BREAK_EVENT;
SDL_PushEvent( & amp;event);

return 0;
}


int main(int argc, char* argv[])
{

AVFormatContext* pFormatCtx;
int i, videoindex;
AVCodecContext* pCodecCtx;
AVCodec* pCodec;
AVFrame* pFrame, * pFrameYUV;
uint8_t* out_buffer;
AVPacket* packet;
int ret, got_picture;

//----------------SDL----------------
int screen_w, screen_h;
SDL_Window* screen;
SDL_Renderer* sdlRenderer;
SDL_Texture* sdlTexture;
SDL_Rect sdlRect;
SDL_Thread* video_tid;
SDL_Event event;

struct SwsContext* img_convert_ctx;

\t// file path
char filepath[] = "test.avi";

//av_register_all();
avformat_network_init();
pFormatCtx = avformat_alloc_context();

if (avformat_open_input( & amp;pFormatCtx, filepath, NULL, NULL) != 0) {
printf("Couldn't open input stream.\\
");
return -1;
}
if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
printf("Couldn't find stream information.\\
");
return -1;
}
videoindex = -1;
for (i = 0; i < pFormatCtx->nb_streams; i + + )
if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
videoindex = i;
break;
}
if (videoindex == -1) {
printf("Didn't find a video stream.\\
");
return -1;
}

AVCodecParameters* codecParameters = pFormatCtx->streams[videoindex]->codecpar;
pCodecCtx = avcodec_alloc_context3(nullptr);
avcodec_parameters_to_context(pCodecCtx, codecParameters);

//pCodecCtx = pFormatCtx->streams[videoindex]->codec;
pCodec = (AVCodec*)avcodec_find_decoder(pCodecCtx->codec_id);
if (pCodec == NULL) {
printf("Codec not found.\\
");
return -1;
}
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
printf("Could not open codec.\\
");
return -1;
}
packet = av_packet_alloc();
pFrame = av_frame_alloc();
pFrameYUV = av_frame_alloc();

out_buffer = (uint8_t*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1));
av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1);

img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);


if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) {
printf("Could not initialize SDL - %s\\
", SDL_GetError());
return -1;
}
//SDL 2.0 Support for multiple windows
screen_w = pCodecCtx->width;
screen_h = pCodecCtx->height;
screen = SDL_CreateWindow("Simplest ffmpeg player's Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
screen_w, screen_h, SDL_WINDOW_OPENGL);

if (!screen) {
printf("SDL: could not create window - exiting:%s\\
", SDL_GetError());
return -1;
}
sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
//IYUV: Y + U + V (3 planes)
//YV12: Y + V + U (3 planes)
sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, pCodecCtx->width, pCodecCtx->height);

sdlRect.x = 0;
sdlRect.y = 0;
sdlRect.w = screen_w;
sdlRect.h = screen_h;

packet = (AVPacket*)av_malloc(sizeof(AVPacket));

//Create a thread
video_tid = SDL_CreateThread(sfp_refresh_thread, NULL, NULL);
//------------SDL End------------
//Event Loop

for (;;) {
//Wait
SDL_WaitEvent( & amp;event);

// Loop waiting for the custom event SFM_REFRESH_EVENT
if (event.type == SFM_REFRESH_EVENT) {
//---------------------------------
if (av_read_frame(pFormatCtx, packet) >= 0) {
if (packet->stream_index == videoindex) {

if (avcodec_send_packet(pCodecCtx, packet) < 0) {
printf("avcodec_send_packet failed!.\\
");
continue;
}
while (1) {
ret = avcodec_receive_frame(pCodecCtx, pFrame);
if (ret != 0)break;
sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);

//SDL--------------------------
SDL_UpdateTexture(sdlTexture, NULL, pFrameYUV->data[0], pFrameYUV->linesize[0]);
SDL_RenderClear(sdlRenderer);
//SDL_RenderCopy( sdlRenderer, sdlTexture, & amp;sdlRect, & amp;sdlRect );
SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
SDL_RenderPresent(sdlRenderer);
//SDL End----------------------
}
}
av_packet_unref(packet);
}
else {
//Exit Thread
thread_exit = 1;
}
}

else if (event.type == SDL_WINDOWEVENT) {
// Zoom to change the size of the video playback window
SDL_GetWindowSize(screen, & amp;screen_w, & amp;screen_h);
}
else if (event.type == SDL_QUIT) {
thread_exit = 1;
}
else if (event.type == SFM_BREAK_EVENT) {
break;
}

}

sws_freeContext(img_convert_ctx);

SDL_Quit();
//-------------
av_frame_free( & amp;pFrameYUV);
av_frame_free( & amp;pFrame);
avcodec_close(pCodecCtx);
avformat_close_input( & amp;pFormatCtx);

return 0;
}