Skip to content

Instantly share code, notes, and snippets.

@aneury1
Created July 27, 2023 14:25
Show Gist options
  • Save aneury1/b465b97ffc2ddcc89f1a0b3fa153ebe9 to your computer and use it in GitHub Desktop.
Save aneury1/b465b97ffc2ddcc89f1a0b3fa153ebe9 to your computer and use it in GitHub Desktop.
// g++ main.cpp -o video_player -lSDL2 -lavformat -lavcodec -lavutil -lswscale
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
extern "C" {
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
}
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cout << "Usage: " << argv[0] << " <video_file_path>\n";
return 1;
}
const char* videoFilePath = argv[1];
// Initialize SDL
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
std::cerr << "SDL_Init error: " << SDL_GetError() << std::endl;
return 1;
}
// Create SDL window and renderer
SDL_Window* window = SDL_CreateWindow("Video Player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
640, 480, SDL_WINDOW_SHOWN);
if (window == nullptr) {
std::cerr << "SDL_CreateWindow error: " << SDL_GetError() << std::endl;
SDL_Quit();
return 1;
}
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
if (renderer == nullptr) {
std::cerr << "SDL_CreateRenderer error: " << SDL_GetError() << std::endl;
SDL_DestroyWindow(window);
SDL_Quit();
return 1;
}
// Initialize FFmpeg
av_register_all();
avformat_network_init();
AVFormatContext* formatContext = nullptr;
if (avformat_open_input(&formatContext, videoFilePath, nullptr, nullptr) != 0) {
std::cerr << "Could not open video file" << std::endl;
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 1;
}
if (avformat_find_stream_info(formatContext, nullptr) < 0) {
std::cerr << "Could not find stream information" << std::endl;
avformat_close_input(&formatContext);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 1;
}
// Find the first video stream
int videoStreamIndex = -1;
for (unsigned int i = 0; i < formatContext->nb_streams; i++) {
if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStreamIndex = i;
break;
}
}
if (videoStreamIndex == -1) {
std::cerr << "Could not find video stream" << std::endl;
avformat_close_input(&formatContext);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 1;
}
// Get video codec context
AVCodecContext* codecContext = avcodec_alloc_context3(nullptr);
if (codecContext == nullptr) {
std::cerr << "Could not allocate video codec context" << std::endl;
avformat_close_input(&formatContext);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 1;
}
avcodec_parameters_to_context(codecContext, formatContext->streams[videoStreamIndex]->codecpar);
AVCodec* codec = avcodec_find_decoder(codecContext->codec_id);
if (codec == nullptr) {
std::cerr << "Codec not found" << std::endl;
avcodec_free_context(&codecContext);
avformat_close_input(&formatContext);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 1;
}
if (avcodec_open2(codecContext, codec, nullptr) < 0) {
std::cerr << "Could not open codec" << std::endl;
avcodec_free_context(&codecContext);
avformat_close_input(&formatContext);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 1;
}
// Allocate video frame and buffer
AVFrame* frame = av_frame_alloc();
if (frame == nullptr) {
std::cerr << "Could not allocate video frame" << std::endl;
avcodec_free_context(&codecContext);
avformat_close_input(&formatContext);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 1;
}
uint8_t* buffer = static_cast<uint8_t*>(av_malloc(av_image_get_buffer_size(
AV_PIX_FMT_RGB24, codecContext->width, codecContext->height, 1)));
AVFrame* rgbFrame = av_frame_alloc();
if (buffer == nullptr || rgbFrame == nullptr) {
std::cerr << "Could not allocate frame buffer" << std::endl;
av_free(buffer);
av_frame_free(&rgbFrame);
av_frame_free(&frame);
avcodec_free_context(&codecContext);
avformat_close_input(&formatContext);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 1;
}
av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize, buffer, AV_PIX_FMT_RGB24, codecContext->width,
codecContext->height, 1);
// Initialize SwsContext for color conversion
SwsContext* swsContext = sws_getContext(codecContext->width, codecContext->height, codecContext->pix_fmt,
codecContext->width, codecContext->height, AV_PIX_FMT_RGB24,
SWS_BILINEAR, nullptr, nullptr, nullptr);
if (swsContext == nullptr) {
std::cerr << "Could not initialize color conversion context" << std::endl;
av_free(buffer);
av_frame_free(&rgbFrame);
av_frame_free(&frame);
avcodec_free_context(&codecContext);
avformat_close_input(&formatContext);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 1;
}
// Main loop to read and render video frames
AVPacket packet;
SDL_Texture* texture = nullptr;
SDL_Event event;
int quit = 0;
while (av_read_frame(formatContext, &packet) >= 0 && !quit) {
if (packet.stream_index == videoStreamIndex) {
avcodec_send_packet(codecContext, &packet);
avcodec_receive_frame(codecContext, frame);
// Convert the frame to RGB24
sws_scale(swsContext, frame->data, frame->linesize, 0, codecContext->height, rgbFrame->data,
rgbFrame->linesize);
// Create the texture if it's not created yet
if (texture == nullptr) {
texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING,
codecContext->width, codecContext->height);
if (texture == nullptr) {
std::cerr << "Could not create SDL texture: " << SDL_GetError() << std::endl;
quit = 1;
}
}
// Update texture with the new frame data
SDL_UpdateTexture(texture, nullptr, rgbFrame->data[0], rgbFrame->linesize[0]);
// Clear the screen and render the texture
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, nullptr, nullptr);
SDL_RenderPresent(renderer);
}
// Handle quit event
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
quit = 1;
}
}
av_packet_unref(&packet);
}
// Clean up resources
av_free(buffer);
av_frame_free(&rgbFrame);
av_frame_free(&frame);
avcodec_free_context(&codecContext);
avformat_close_input(&formatContext);
sws_freeContext(swsContext);
if (texture != nullptr) {
SDL_DestroyTexture(texture);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment